The gap
lib/connectors/registry.ts is the platform's answer to "what can an agent reach, and who says so". A
connector is declared once and everything derives from that entry: the tool catalog,
connectorClient auth dispatch, capabilities.tools gating, the write-consent gate (#90), and all
three surfaces (agent runtime / /v1/instances/:id/tools / MCP).
Eleven connectors live there: github, meta, terminal, tmux, browser, repo-local,
supervision, http, mcp, google_sheets, web-search.
Google Drive, Zoho WorkDrive and Gmail are not among them. They are bespoke routes
(routes/drive.ts, routes/workdrive.ts, routes/email.ts), with hand-written console blocks in
SettingsTab.tsx:967-1075.
Because they are not declared, there is nothing for a capability to gate against. So they render on
every instance, for every agent, regardless of what that agent can do. On this account that is 26
instances — including Repo Chat, whose entire job is answering questions about code, and the four
terminal Operators, none of which has any file-connector tool.
Why this is a half-finished migration, not a refactor
Three pieces of the destination already exist and are already tested:
1. The grant model is built and unused. Connector.grantModel (lib/connectors/types.ts:129)
takes "user" | "instance-resource", the second documented as "each tool call must target a
resource granted to the instance" — which is precisely Drive/WorkDrive's folder grant.
connectors/client.ts:90-102 mints a token for such a connector only after
requireConnectorGrant passes, fail-closed with a 403, and client.test.ts:143,152 covers both
directions.
No connector in CONNECTORS declares grantModel: "instance-resource". The feature was built for
these three and they never arrived.
2. They already share the storage. routes/drive.ts:27 and routes/workdrive.ts:16 both import
lib/connector-grants.js — the same connector_grants table the registry model reads. Its own
header comment names the intended consumers: "Known values: google_drive | zoho_workdrive | (any
connector id with grantModel:"instance-resource")".
3. The OAuth path is proven on the same credential. google_sheets (#89/#143/#147) is a
declarative manifest driven by the generic OAuth2 handler, connecting at
/v1/connectors/google_sheets/oauth/start — using the same GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET
as Drive, with its refresh token in its own vault slot. Its source comment says so explicitly:
"separate from Drive". Sheets took the new road; Drive stayed on the old one.
So the destination is not hypothetical. It is a manifest per connector plus handlers for the parts
that are genuinely custom (Drive's file listing, WorkDrive's DC-specific bases, Gmail's
find_confirmation_link permission gate).
What it buys
Sequencing
This supersedes the symptom fixes rather than competing with them — but those are small and land
today, so do them first and let this delete the code they touch.
Gmail is the delicate one: find_confirmation_link is permission-gated on
AgentState.permissions.email and reads the owner's inbox. Its gate must survive the move as a
declared scope, not be softened into a plain write-consent checkbox.
Related: #146 (connector manifests), #147 (generic connector OAuth), #86 (registry), #90
(write-consent).
The gap
lib/connectors/registry.tsis the platform's answer to "what can an agent reach, and who says so". Aconnector is declared once and everything derives from that entry: the tool catalog,
connectorClientauth dispatch,capabilities.toolsgating, the write-consent gate (#90), and allthree surfaces (agent runtime /
/v1/instances/:id/tools/ MCP).Eleven connectors live there:
github,meta,terminal,tmux,browser,repo-local,supervision,http,mcp,google_sheets,web-search.Google Drive, Zoho WorkDrive and Gmail are not among them. They are bespoke routes
(
routes/drive.ts,routes/workdrive.ts,routes/email.ts), with hand-written console blocks inSettingsTab.tsx:967-1075.Because they are not declared, there is nothing for a capability to gate against. So they render on
every instance, for every agent, regardless of what that agent can do. On this account that is 26
instances — including Repo Chat, whose entire job is answering questions about code, and the four
terminal Operators, none of which has any file-connector tool.
Why this is a half-finished migration, not a refactor
Three pieces of the destination already exist and are already tested:
1. The grant model is built and unused.
Connector.grantModel(lib/connectors/types.ts:129)takes
"user" | "instance-resource", the second documented as "each tool call must target aresource granted to the instance" — which is precisely Drive/WorkDrive's folder grant.
connectors/client.ts:90-102mints a token for such a connector only afterrequireConnectorGrantpasses, fail-closed with a 403, andclient.test.ts:143,152covers bothdirections.
No connector in
CONNECTORSdeclaresgrantModel: "instance-resource". The feature was built forthese three and they never arrived.
2. They already share the storage.
routes/drive.ts:27androutes/workdrive.ts:16both importlib/connector-grants.js— the sameconnector_grantstable the registry model reads. Its ownheader comment names the intended consumers: "Known values:
google_drive|zoho_workdrive| (anyconnector id with grantModel:"instance-resource")".
3. The OAuth path is proven on the same credential.
google_sheets(#89/#143/#147) is adeclarative manifest driven by the generic OAuth2 handler, connecting at
/v1/connectors/google_sheets/oauth/start— using the sameGOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRETas Drive, with its refresh token in its own vault slot. Its source comment says so explicitly:
"separate from Drive". Sheets took the new road; Drive stayed on the old one.
So the destination is not hypothetical. It is a manifest per connector plus handlers for the parts
that are genuinely custom (Drive's file listing, WorkDrive's DC-specific bases, Gmail's
find_confirmation_linkpermission gate).What it buys
in [bug] An unconfigured connector renders a red error on every agent's Settings tab — Zoho WorkDrive has never been configured #353 and [design] Account-level connections are rendered inside one agent's Settings — disconnecting Gmail there disconnects it everywhere #355 stops being UI to prune and becomes a derived property.
connectors/client.ts:34currently carries the tell:"the hand-written google_drive connector keeps its hardcoded creds."
Sequencing
This supersedes the symptom fixes rather than competing with them — but those are small and land
today, so do them first and let this delete the code they touch.
Gmail is the delicate one:
find_confirmation_linkis permission-gated onAgentState.permissions.emailand reads the owner's inbox. Its gate must survive the move as adeclared scope, not be softened into a plain write-consent checkbox.
Related: #146 (connector manifests), #147 (generic connector OAuth), #86 (registry), #90
(write-consent).