33 Commits
Author SHA1 Message Date
arc53-machine 2359e4546b Say which attached resources stopped running and why
The agent and workflow reads now return resource_states to people who may
edit them: every attached tool, source and prompt (and workflow node tool
and source) with active or stopped and the reason: deleted,
owner_lost_access, the sponsor reasons, connection_needs_reconnect,
connection_removed or connector_disabled. Each entry names the sponsor,
someone other than the reader who can fix it, the service for a connection
reason, and whether the reader may take it over or reconnect it. When
something can be taken over, sponsor_audience says who it would reach.

The state comes from the checks the run itself uses (ref_access,
resolve_holder_tool and the tool's connection as the run resolves it), so
the page and the run can't disagree. A run that leaves a resource out logs
resource_stopped with the holder, type, id and reason.

The workflow read gives sponsor details, run state and node resource names
only to people who may edit it, and names only resources the workflow runs,
someone sponsored, or the reader can see. Owner saves and new workflows
now refuse node tools and sources the owner can't use, like editor saves.
2026-09-29 17:11:58 +01:00
arc53-machine f2760a9138 Refuse gated tool calls in research steps instead of running them
Research steps ran each tool call straight through the executor, never
asking check_pause, so approval-gated actions (including a public-link
visitor's wiki edits) and an outside caller's writes on the owner's
accounts ran unchecked. A step can't pause, so it now checks each call
first and answers any pause with a refusal the model reads, journaled
like a headless denial. Headless and research runs share the journaling.
2026-09-29 16:45:55 +01:00
arc53-machine 51f88144c5 Run webhooks with the owner's rules again
The owner sets a webhook up and its URL is a secret, and a webhook run
already refuses every action that needs approval, so holding it to the
API write allowlist only broke the owner's own automations. Webhook runs
no longer count as external callers; schedules keep their caller rules.
The allowlist copy no longer names webhooks.
2026-09-29 15:55:36 +01:00
arc53-machine 080b75c81e Gate an API tool action only when it sends the owner's saved values
Every API tool counted as holding the owner's credentials, so outside
callers were refused any write on one even when it sends nothing the
owner stored. Now an action counts only when its headers or query
parameters carry a saved value (sealed or legacy plaintext) or the tool
stores credentials; the allowlist lists just those writes.
2026-09-29 15:54:30 +01:00
arc53-machine daf6af57ae Keep outside callers' write limits in scheduled and webhook runs
A scheduled or webhook run acts as the agent's owner with no one to
approve, so a public-link user or API-key caller could have the agent
schedule a write and have it run on the owner's accounts. Runs now keep
the caller's rules: a schedule set by someone who reaches the agent only
by its public link runs as a public-link caller, one set through the API
(recorded as created_via 'api', migration 0042) and every webhook run as
an external caller, each with the agent's API write allowlist.
2026-09-29 15:44:24 +01:00
arc53-machine 5c8b97b609 Gate outside writes on the owner's stored credentials, not only connections
API-key, widget and public-link callers were held to the write allowlist
only on connected accounts, so they could still write through an API
tool or a signed-in MCP server that carries the owner's credentials. Any
write on credentials the caller doesn't hold is now refused unless the
owner allowlisted it, and a scheduled run for such a caller counts as not
holding any. The tool list names these writes per tool, so the allowlist
can offer them, and its copy now names every route it covers.
2026-09-29 15:41:34 +01:00
arc53-machine 3dc5080058 Refuse public-link writes on the owner's account unless allowlisted
Someone who reaches an agent only through its public link was offered the
approval card for writes on the owner's connected accounts, so a stranger
could approve for the owner. Those writes are now refused with a tool
result, like an API-key caller's, unless the owner allowed the action in
the agent's Access details. Team members keep the card, and a tool on the
caller's own account (member mode) is unaffected. The flag survives a
resume, and workflow nodes now follow the run's caller rules (scheduled,
API-key and public-link) instead of starting from none.
2026-09-29 15:12:46 +01:00
arc53-machine 5a503e9b3c Give workflow node tools the owner's toolset whoever runs the workflow
A node's tools resolved as the person running the workflow, so a teammate
or public-link user lost every owner tool they could not use themselves.
They now resolve as the workflow owner, then as the editor who attached
them, like an agent's own tools. The runner stays the invoker, so a
member-mode connection still uses their own account.
2026-09-29 15:09:48 +01:00
arc53-machine a45e369d0f Run a tool only on a connection of the connector that provides it
At run time a tool now refuses a connection whose connector does not list
the tool's type (a Telegram bot token never reaches an ntfy or MCP tool),
and an MCP tool refuses a connection when no server is known for it,
instead of sending the key without checking. Legacy MCP rows still find
their server from the stored provider value.
2026-09-29 14:13:03 +01:00
arc53-machine a1789d2ab4 Merge main (roles and access revamp) into connectors
Main's access model (team editors and viewers, edit_credentials, owner-only
OAuth servers, per-user tool preferences, resource sponsors) now applies to
connection-backed tools and sources. Our migrations are renumbered to
0040_connections and 0041_connection_account_name, after main's
0038_resource_access_settings and 0039_resource_sponsors.

Where the two sides met: MCP tools save and load their connections as the
tool owner, editors may add a key (a new connection on the owner's account)
but never rewrite an existing connection's secret, fixed values stay
owner-only, and a member's own connection is used in member mode.
2026-09-29 14:02:26 +01:00
arc53-machine 39063d5e41 Mark fixed values in the chat instead of showing them, and keep them on reload
A value the owner fixed may be a secret, and tool-call events reach whoever
runs the agent, so query and body values the owner fixed now show as
(fixed); a value the connection sets, like Telegram's default chat, still
shows. sent_arguments is saved with the conversation, so a reopened chat
shows the same arguments as the live one.
2026-09-29 12:09:13 +01:00
arc53-machine 12ad4f5e85 Let a GitHub connection opt into write tools
A GitHub connection's MCP tool can now point at GitHub's full endpoint
(/mcp/) instead of the read-only one when its owner opts in, at setup
(allow_writes) or later (PUT /api/connections/<id>/writes), which re-reads
the actions and keeps the choices for those on both endpoints. Actions from
the write endpoint are writes unless GitHub marks them read-only, so they
default to asking first.

Admins can forbid it per connector (allow_writes in Admin > Connectors,
kept in app_metadata). Then the option is refused, a refresh goes back to
read-only, and at run time the tool only ever calls the read-only endpoint
and write calls are denied with a reason.
2026-09-29 11:22:01 +01:00
arc53-machine dba2de590b Show the arguments a tool call sends, fixed values included
The approval card and a finished call showed what the model asked for,
even where a fixed value replaced it. Calls now carry sent_arguments for
the chat, leaving headers out since they may hold a fixed secret. The
model's own arguments are kept as they were, because they are what later
turns replay to it and it never sees fixed values.
2026-09-29 11:08:50 +01:00
arc53-machine bdc453eaab Show the model an account only where it tells tools apart
Actions that different services share are described with the service
alone; the account is named only when one service is connected twice.
2026-09-29 10:42:03 +01:00
arc53-machine bdcb88866a Let people name accounts and use the names to tell accounts apart
Connections get an account_name (migration 0039) that the owner sets with
PATCH /api/connections/<id>; the account label stays the account's
identity, so signing in again still finds it. When someone has more than
one account of a service, its tools are listed as "Telegram · <account>"
and the model sees each account's actions under the account's name
(telegram_send_message_alerts_bot) with the account in the description,
instead of _1 and _2. Actions shared by different services are named
after the service. Tools a user renamed keep their name.
2026-09-29 10:41:54 +01:00
arc53-machine c3e83ff9dd Add a default chat to the Telegram connector
A Telegram connection can now hold an optional default chat ID. When it
is set, both Telegram actions send there, the model is no longer asked
for a chat, and a chat it names anyway is ignored. In member mode each
member's own connection supplies their own chat. The same bot with a
different chat is a separate connection.
2026-09-29 10:41:48 +01:00
arc53-machine 34038ae1c2 Keep fixed tool parameters fixed when the model sends them
A parameter hidden from the model with a stored value is now always sent
with that value; a model that names the key anyway (by mistake, or because
a prompt told it to) no longer overrides it. 0 and false are real fixed
values; an empty value still means the parameter is left out.
2026-09-29 10:41:13 +01:00
arc53-machine 7b516ade82 Add a built-in GitHub connector for repository sync and read-only tools
One GitHub connection feeds both a Knowledge source and an agent tool.
Users connect with a personal access token, checked against GitHub and
named after the account, or, when an admin registers a GitHub App
(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_APP_SLUG), with Sign in
with GitHub. App tokens expire after eight hours and are refreshed before
each sync or tool call; tokens with no expiry are never treated as expired.

The catalog gains an optional second sign-in method (oauth_settings,
exposed as sign_in_methods) without changing any other connector.

Sync lists the repositories the connection can read (the token's own, or
the App installations') and ingests one with the connection's token. The
tool is GitHub's read-only MCP server (api.githubcopilot.com/mcp/readonly):
setup discovers its actions and creates it bound to the connection, and
the executor sends the connection's token only to that server.
2026-09-29 10:41:03 +01:00
Pavel 7530e54aec Shared resource use 2026-09-29 11:43:43 +04:00
Pavel 98afa5d93c Roles audit and revamp 2026-09-29 10:42:39 +04:00
arc53-machine f6c89503d8 Reconnect in place from the chat bar and the health toast
- The Connect card's payload carries connection_id for the caller's own
  connection (never an owner's), so "Reconnect" in chat signs in again
  right there instead of sending the user to Settings. The bar offers
  Continue as soon as the connection works again, also after leaving and
  coming back to the chat.
- The reconnect event carries source and tool counts, and the toast says
  what stopped: "3 sources are paused", "so agents can use it again",
  or both. Reconnect signs in again in place and the toast closes itself
  once the connection works; MCP servers still reconnect from their
  drawer.
2026-09-28 21:37:19 +01:00
arc53-machine a23ace3491 Refuse writes on the owner's accounts from API-key callers unless allowed
An agent called with its API key (widget, API) runs as its owner, and
nobody can approve an action there, so a write set to Always allow ran
on the owner's account for anyone holding the key. A caller is external
when the request carries the key and is not signed in as the owner (an
owner previewing their agent keeps full access).

For external callers, write actions on connection-backed tools are
refused unless listed in the agent config's new api_write_allowlist
(tool_id:action), and a missing connection is refused instead of pausing
on a Connect card the widget cannot show. The flag and list survive a
paused-and-resumed stream. Owners pick the allowed actions under Access
details; a team editor's update keeps the stored list.
2026-09-28 21:33:41 +01:00
arc53-machine fff3e7c904 Never send an MCP connection's secret to a different server
Editing an MCP server's URL kept the previous connection when no new
one could be stored (default encryption key), so its key would reach the
new server. The save route now keeps a previous connection only for the
same server, and the executor refuses an MCP tool whose connection was
stored for another server.
2026-09-28 19:11:54 +01:00
arc53-machine 6b4220d401 Address review: MCP token scope, API-key identity, reconnect lock, setup retries
- An MCP connection's tokens only go to its own server, and a
  connection_id can no longer come from a client: the MCP test/save
  routes drop it and the tool executor uses only the resolved one.
- API-key connections are reused only when they hold the same
  credentials. Two keys that share a hint or a label get separate
  connections ("…abcd (2)"), at runtime and in the 0038 backfill.
- Reconnecting a connection whose secrets cannot be decrypted no longer
  flags it from a second transaction while holding its row lock.
- Connection setup validates the sync request before claiming its
  Idempotency-Key, so a corrected retry is queued.
- A request that names no connection resolves to none without opening
  a database connection; fixes two tests that reached a real database.
2026-09-28 18:45:27 +01:00
arc53-machine b692eaf7ad Add MCP presets, admin connector policies and connector attribution
Six vendor-run MCP servers (Notion, Linear, Atlassian, Sentry, Asana,
Stripe) join the catalog as presets from docsgpt/connectors/presets/
mcp.yaml; existing connections to those servers show under them.

connector_policies lets an admin turn a connector off or force whose
account every share uses, and app_metadata's
connectors.allow_custom_mcp turns custom MCP servers off. Both are
enforced on the server: new connections, OAuth sign-ins, MCP test and
save, and the tools of a disabled connector stop resolving.
GET/PUT /api/admin/connectors reads and changes them.

A shared tool's owner picks owner or member credentials with
PUT /api/connections/tools/<id>/credential-mode, and a member running
the owner's account always confirms write actions.

Tool-call events and retrieved chunks name the connector they came
from (key and display name, never the account), and tool calls keep
those fields when the conversation is reloaded.
2026-09-28 17:30:53 +01:00
arc53-machine 6e2ee43944 Connect services from one wizard, with no tokens in the browser
Every entry point (the Connectors page and drawer, Add Source, Add
Tool, the chat) opens one connect wizard: sign in or paste the
credentials, choose what to sync (content services only, skippable),
then a summary with Try it in chat. Tool services create their tools on
sign-in with write actions needing approval, and each action's
permission (Always allow, Needs approval, Off) can be changed in the
wizard or the drawer. The drawer also reconnects, syncs more content,
refreshes an MCP server's tools and removes a connection, keeping or
deleting what it feeds.

The file pickers work from a connection id and fetch a short-lived
picker token when they need one; no session token is kept in
localStorage any more, and any left by an older build is claimed once
and removed. S3 and Reddit reuse the keys stored on a connection.

A tool whose account needs signing in pauses on a Connect card in the
chat; after connecting, Continue resumes the call. A connection whose
sign-in stops working raises a Reconnect toast. The Connectors page is
hidden when the backend does not report connectors_enabled.
2026-09-28 17:21:44 +01:00
arc53-machine 3511004f50 Connections own their credentials
Migration 0038 moves every stored secret (OAuth tokens, MCP OAuth
tokens and client registrations, API keys) into the connection's
encrypted envelope, links API-key tools to one connection per distinct
credential, allows several accounts per provider, and adds
credential_mode to sources and tools. OAuth MCP tools keep resolving
each member's own token, as they did before.

docsgpt.connectors.service is now the only reader of OAuth tokens:
get_valid_token_info refreshes under a row lock and persists rotated
refresh tokens, and a revoked grant flags the connection, pauses its
sources and notifies the owner. Loaders build from a connection
(BaseConnectorLoader.from_connection), so scheduled sync covers Drive,
SharePoint and Confluence sources with no browser. S3 and Reddit keys
stay on the connection instead of in remote_data.

New endpoints: POST /api/connections, /setup, /reconnect,
/picker-token, /claim, DELETE /api/connections/<id>, per-action
permissions and MCP refresh-tools. Upload, file listing, sync and
validate-session take a connection_id; session tokens keep working for
this release. The tool executor reads credentials from the resolved
connection (owner or member mode) and pauses on a Connect card when a
connection needs signing in. docsgpt connectors reencrypt rewrites
stored credentials after a key rotation.
2026-09-28 17:03:09 +01:00
arc53-machine a74420ed2c Trace unexecuted tool calls in one place; tidy span lifecycles
Paused, denied, skipped and client-run tool calls all go through
trace_unexecuted_tool_call. The research synthesis, continuation and
workflow step spans use with-blocks instead of hand-rolled GeneratorExit
handling. Fixes blank lines and adds a missing hint and docstrings.
2026-09-23 22:42:05 +01:00
arc53-machine 0bbbac0eb2 Skip trace work when disabled; share the agent span builder
With TRACES_ENABLED off, LLM calls record no GenAI metrics, and a streamed
answer is joined into a preview only when a span keeps it. Agent runs and
continuations build their invoke_agent span in one place, which now reads
the model from model_id. The trace panel shows a stream's model time next
to how long it was open. Adds missing type hints.
2026-09-23 22:09:58 +01:00
arc53-machine efec1a2f83 Keep tool output out of span errors and fix trace status and listing
Tool results, denial comments and tool exception text now reach a span only
as a capture-gated preview; span.error is a fixed message, since it is
stored and exported regardless of content settings. A turn whose stream
yields an error event is recorded as failed. Search traces are listed by a
query copied into the small summary column, so the Logs timeline never
reads the spans JSONB. Adds GraphRAG span tests.
2026-09-23 18:11:19 +01:00
arc53-machine a15940c281 Trace agent runs and tool calls
@log_activity opens the invoke_agent span for every agent run and names
the trace's activity; gen_continuation opens its own. ToolExecutor.execute
records each executed call with redacted argument and result previews, and
paused, denied, skipped and client-executed calls are recorded where they
are decided.
2026-09-23 17:25:41 +01:00
arc53-machine f882ef49a7 refactor: read settings directly instead of getattr with a second default
About 85 call sites read a setting as getattr(settings, "NAME", fallback),
each carrying its own copy of the default. Every one of those names is a
field with a default on the model, so the fallback could never apply to
the real settings object; it only masked drift. Two had drifted:

- OPENAI_PROMPT_CACHE_KEY defaults to True on the model but the reader
  fell back to False, and two test stubs relied on that.
- SharePoint's MICROSOFT_AUTHORITY fallback to
  https://login.microsoftonline.com/<tenant> never fired, because the
  attribute always exists (as None), so MSAL got authority=None. The
  connector now derives the tenant authority when the setting is unset,
  as its test always assumed.

Four places read EMBEDDINGS_KEY straight from os.environ, skipping the
"None"/"" normalisation the model applies; they read the setting now.
Test stubs that replaced a module's settings with a SimpleNamespace list
every setting the code under test reads.
2026-09-17 11:14:34 +01:00
Alex 574f96341e refactor: rename the application package to docsgpt
The backend import package is now docsgpt, the name it will carry on PyPI;
application was far too generic to install into anyone's site-packages.
git mv plus a mechanical rewrite of every import, dotted string and path
reference: 734 Python files, the compose files, Dockerfile, workflows, docs,
setup scripts, devcontainer, k8s manifests, vscode config, pytest and coverage
config, .gitignore. Behaviour is unchanged.

Kept for one release:
- A top-level application package whose meta-path finder resolves
  application.x.y to the already-imported docsgpt.x.y object, so old imports
  and entry points (celery -A application.app.celery,
  uvicorn application.asgi:asgi_app) keep working with a FutureWarning.
- Celery registers every application.* task name as an alias of its
  docsgpt.* task on start-up, so messages queued by the previous release still
  run. The redbeat key prefix moves to redbeat:docsgpt:v2: so schedule entries
  the previous release wrote are left unread instead of firing twice.

The backend image builds from the repository root (docker build -f
docsgpt/Dockerfile .) so it can ship the alias package; a root .dockerignore
allow-lists docsgpt/ and application/ and keeps caches, local data, .env
files, the sample index files and the Dockerfile out. Compose and the image
workflows point at the new context.
2026-09-07 10:20:43 +01:00