Repository navigation
Description
Four scopes, one tab
SettingsTab.tsx is 1,154 lines and mixes settings that belong to four different things. The tab is
titled with one agent's name, so everything on it reads as this agent — including the parts that
silently change every agent.
| What | Actual scope | Where it is written |
|---|---|---|
| Instance name, agent settings, behaviour, runner node, tool toggles, write consent, supervision, connections, triggers, loop presets | this instance | agent_instances.config |
| Voice, Translation | account default + explicit per-instance override | /v1/preferences ← PrefOverride |
| Connect / disconnect Gmail, Google Drive, Zoho WorkDrive | the whole account | connector_tokens |
| Link GitHub username | the whole account | users.linked_github_login |
| Drive/WorkDrive folder grants, email permission checkbox | this instance | connector_grants, AgentState.permissions |
Rows 3 and 4 are the leak. Disconnecting Gmail from the Coder's Settings tab disconnects it for the
Job Application Assistant too. Nothing on screen says so except one 12px line under the GitHub row
(:939) — and Gmail, Drive and WorkDrive don't even have that.
The disconnect confirmations make the same mistake from the other side:
"Disconnect Gmail? Agents will no longer be able to read your inbox for sign-in links."
Correct — plural, account-wide — but you are reading it inside one agent's settings, having clicked a
button in a card about that agent.
The pattern already exists in this file
Voice and Translation get this exactly right (#211): an account default, an explicit "Use my
defaults / Customise for this agent" radio, and a "Use my defaults" that DELETEs the override.
Preferences.tsx's own header comment states the principle — "Only [what an agent IS] and [the
danger zone] are properties of an agent."
A connection is not a property of an agent. A grant is.
Proposal
Move the account half to Preferences. Add a Connections section there — connect/disconnect for
GitHub, Gmail, Drive, WorkDrive, and any registry connector needing OAuth. That page is already
defined as "what every agent falls back to", and it already carries timezone, voice and translation
on the same footing.
Leave the instance half on the instance — and only when it can do something:
- the folder grant, shown only when the account connection exists and the agent declares a tool
for that connector ([design] Stage 2: register the generic connector OAuth redirect for Drive/WorkDrive/Gmail and retire the dedicated flows (Stage 1 — the registry declaration — has shipped) #352 makes the second half possible); - the "allow this agent to read my inbox" checkbox, which is genuinely per-instance and correctly
scoped today.
Where an instance needs a connection the account doesn't have, link to Preferences rather than
re-rendering connect UI in two places — the same shape as the runner card's pags up remedy.
Why not just add "account-level" labels
Because it doesn't fix what the reader is doing. Someone on the Coder's Settings tab is configuring
the Coder; a label asking them to notice that one card is different is a worse guarantee than putting
the card where its scope is obvious. It is also what #211 already decided for voice — this is the
same argument for the connection rows it didn't reach.
After this
SettingsTab.tsx is on #305's list. Splitting it before this and #352 just relocates the clutter;
after them it separates cleanly into Identity · Runtime · Permissions · Teamwork · Automation ·
Overrides · Danger.
Related: #211 (the override pattern), #305 (large-file split), #352 (registry), #353 (unconfigured
connectors).
Activity
serge-ivo commentedon Aug 7, 2026
This is the primary fix for the settings clutter, not a follow-on
Filed as a scope-hygiene issue and cross-referenced to #352 as though #352 did the real work. Checking
#352 afterwards showed the opposite.
Drive and WorkDrive expose no agent-callable tools, and Gmail's is deliberately non-declarable
(see the correction on #352). So
capabilities.toolscannot gate any of the three, and #352 cannotremove them from the instance page without first inventing tools that do not exist.
Moving connect/disconnect to the account page removes them today, with no registry work. Once the
instance shows only a grant — and a grant is meaningless without an account connection — the block
renders on the condition
status.connected. On this deployment right now:connected: false→ gone from all 26 instancesconfigured: false→ gone anyway ([bug] An unconfigured connector renders a red error on every agent's Settings tab — Zoho WorkDrive has never been configured #353)That is the whole reported symptom, fixed by putting each control where its scope already is.
The residual, stated honestly
Connect Drive once at account level and the grant box comes back on all 26 instances, including the
four terminal Operators that will never read a document. Gating that needs a capability axis, which
is what #352 is for. So the two are complementary — this one fixes the symptom and the scope lie,
#352 fixes the steady state — but this one does not depend on it and should not wait for it.
Not a pure move
Preferences.tsxhas no connector UI today, so the Connections section is new work there, not atransplant. The existing rows in
SettingsTab.tsx:931-1075carry connect/disconnect handlers,confirm dialogs and status polling that mostly lift as-is; what has to be written is the section
shell and the empty states.
Suggested sequence: #353 → #354 → this → #352.
serge-ivo commentedon Aug 7, 2026
Assessment: do it — this is the right diagnosis of a permission-comprehension bug. Three cautions, one of which is that the destination page has the same problem one level up.
Agreeing with the body and the correction on the main point, so this comment is only the parts I'd change.
First, the scope table is wrong about two storage locations
Worth fixing before anyone implements from it, because both names read as purpose-built tables and neither exists:
connector_tokensuser_api_keys, keyed(user_id, provider)where provider is the connector id — the same table and the same helper (saveConnectorRefreshToken,lib/connector-oauth.ts:120) that every registry OAuth connector usesconnector_grantsinstance_connector_grants(lib/connector-grants.ts:111)The first correction matters beyond pedantry: it is why #352 Stage 1 is nearly free, and why the Connections section should be built as a loop over the registry rather than three more hardcoded blocks. See my comment there — short version: land the
GET /v1/connectorslist route first and this section is a.map(), otherwise it is a third hand-written copy that #352 then deletes.Tested: the destination page already breaks its own promise
Preferences.tsxsubtitle:That is true of Voice and Translation. It is false of the two other things already on the page:
c039562) has no per-instance override at all. Its own save confirmation says so —"Saved — every agent uses this", versus Voice's"applies to every agent". Two different promises, one paragraph apart.localStorage, per-browser, and has nothing to do with agents.So the page is not, as the body has it, "already defined as what every agent falls back to". It is already "the account-scoped page", and its header is the stale part. That matters here because a connection is the least overridable thing yet: there is exactly one Gmail account, and no per-instance variant of it can exist. Dropping connect/disconnect under a header that promises "any one of them can be customised on its own Settings tab" reproduces the exact misread this ticket exists to fix, one level up and with the destructive buttons on it.
So: move the cards and rewrite the subtitle in the same change. Something in the shape of "Your account — connections, timezone and how you speak, hear and read. Voice and translation can be overridden per agent; everything else applies everywhere." One sentence, and it is the whole comprehension fix, because after this the page is the definition of account scope rather than a page that happens to hold some.
The move makes an existing defect worse unless it carries one more thing
Filed separately as #357: disconnecting Drive/WorkDrive deletes only the token row (
routes/drive.ts:91). Every instance's folder grants survive ininstance_connector_grants, nothing prunes them, and reconnecting silently restores every one of them — no user action, nothing displayed. The grant is the entire access-control story for that connector.Today the disconnect button at least sits next to one of the instances it affects. After this change it sits on a page that has no idea any grants exist, and reconnect is one click. So the Connections section should show the blast radius: "Google Drive — connected · 3 agents hold 5 folder grants", and the disconnect confirmation should name the number rather than saying "Agents will no longer be able to…".
instance_connector_grantsis queryable by(user_id, provider)and nothing queries it that way today; that is the one new read this section needs.That also fixes the confirmation-copy complaint in the body properly. The problem with "Agents will no longer be able to read your inbox" is not only that it is plural in a singular context — it is that it does not say which agents, when the system knows.
What I would not build
1. A "Use my defaults / Customise for this agent" control for connections. #211 is the right precedent for where these live and the wrong one for what shape they take. Voice has a real per-instance variant; a connection does not. Copying the override radio across would ship a control that promises per-agent scoping and cannot deliver it — the same confusion this ticket is closing, this time with an affordance backing it up. Connections get no override control, and the subtitle should say which sections have one.
2. A scope-badge / "account-level" labelling component. The body already argues against labels as the fix, and I'd go further: don't build the generalised version afterwards either. Once the two genuinely account-scoped controls move, nothing left on the instance tab needs a badge — and a badge component is an invitation to leave the next account-scoped control on an instance page "because it's labelled". The rule the move establishes ("if it's account-scoped it isn't on an instance page") is worth more than any component, and a badge system quietly repeals it.
3. Folding connections into the
/v1/preferencespayload. They share a page, not a resource.PUT /v1/preferencesis a JSON blob of user preferences; a connection is an OAuth lifecycle with a redirect, a bind cookie, a token row and a disconnect. Keep them on their own routes and let the page compose them — otherwise the preferences endpoint grows a callback surface, and #352 Stage 2 (moving Drive onto the generic connector OAuth flow) has to unpick it.4. #305's SettingsTab split, in this change. Agreeing with the body and restating it because it is the tempting one: the file is 1,154 lines and this touches ~145 of them. Splitting first relocates the clutter into new files and then has to be redone.
5. Moving the folder grant or the email permission checkbox. Both are correctly per-instance today. The grant box should render on
status.connected(which is what does the work, per the correction above), and the email checkbox should probably render on connected too — right nowagent-think.ts:638offersfind_confirmation_linkon the permission flag alone, so after a Gmail disconnect an agent is still handed a tool it cannot use. That is a small honesty fix, not a scope change.Where GitHub goes is a real question the ticket skips
users.linked_github_loginis stored as identity,Preferences.tsx's own header comment reserves identity for Profile ("account, profile, candidate profile, API token, billing, keys"), and it is used as a connection (repo access, build status). It is the only one of the four with a scope note today (:939) and the only one that is not an OAuth token.I'd put it with the connections anyway — nobody hunting for "where do I connect GitHub" looks under identity — but say so explicitly in the ticket rather than letting the implementer discover the ambiguity. Note also that
Profile.tsxis currently owned by another change, so a Connections-on-Preferences answer avoids a collision as well as being the better one.Sequence
Unchanged from the correction except for one insertion: #353 → #354 → #352 Stage 1 (declare +
GET /v1/connectors) → this → #352 Stage 2. Stage 1 is smaller than this ticket and turns its main piece of new work into a loop.serge-ivo commentedon Aug 7, 2026
#357 is the data-side half of this issue and was filed independently while assessing it. Verified it:
DELETE /v1/drive/google(routes/drive.ts:91) really does delete only theuser_api_keysrow, leaving every instance'sinstance_connector_grantsintact.The two are the same confusion seen from two directions:
That makes #357 an argument for this one rather than a separate concern: once connect/disconnect lives on the account page, "disconnecting this affects N instances' folder grants" is a sentence that has somewhere to go. Doing #357's pruning without this leaves the warning stranded in a per-agent card that should not be showing an account control at all.
feat(api): the connector catalog says what a disconnect costs and whe…
feat(console): connections move to the account page, grants stay with…
serge-ivo commentedon Aug 7, 2026
Done —
0735b24(API) +91e762c(console). CI green on91e762c; both deploys succeeded.What moved, what stayed
Account (Preferences → Connections): connect / reconnect / disconnect for Gmail, Google Drive and Zoho WorkDrive; the GitHub identity link; and the link callback's
?github_linked=effect, which had to follow the button or its confirmation lands on a page nobody is on.Instance (Settings → Permissions & Connections): the per-agent folder grant, and the "allow this agent to read my inbox" checkbox. Both are genuinely per-agent and were never the leak.
Two gating changes go with it:
connected, notconfigured— a grant against an account you have not connected is the dead row [bug] An unconfigured connector renders a red error on every agent's Settings tab — Zoho WorkDrive has never been configured #353 removed, wearing a different hat. So on this deployment the block is gone from all 26 instances, which is the reported symptom.agent-think.ts:638offersfind_confirmation_linkon the permission flag alone, so hiding the checkbox would remove the only control that turns off something still set. That honesty fix is still open — gating the tool on the connection is a separate change, and until it lands the checkbox has to stay reachable.The new subtitle
The design was right that the destination already broke its own promise, so it is rewritten in the same change:
Every section's scope is now stated, including the two that were already wrong (Timezone has no override; Appearance is
localStorage). The page is the definition of account scope rather than a page that happens to hold some.Did Stage 1 make it a
.map()Mostly yes, with two additions to
GET /v1/connectorsthat Stage 1 had left out — both of which would otherwise have become a fourth hardcoded per-connector table in the console, which is the arrangement that route exists to end:reach— what a disconnect revokes, per connector, as oneGROUP BYoverinstance_connector_grantsalongside the existinguser_api_keysread. Disconnecting Drive/WorkDrive leaves every instance's folder grants, and reconnecting silently restores them #357 gave the confirmation its count, but from a per-connector status route; a page listing every connector needs it for every row.null(not{grants:0}) for auser-model connector, because Gmail's reach is a per-agent flag and a zero would read as "nothing uses this".flow— which connect/disconnect endpoints are live. Drive, Gmail and WorkDrive keep dedicated flows (their OAuth apps register those redirect URIs); every other oauth connector gets the generic pair. Server-side, so Stage 2 retires a dedicated flow by deleting one line rather than hunting for the buttons pointing at it.Also
credentialEnv: WorkDrive declares nooauthblock (per-DC endpoints), so the catalog reported it unconfigured while/v1/workdrive/statusreported it connectable. Two answers to "can I connect this?" is what the catalog is for.Result: no connector is named anywhere in the console. The one hand-written row is GitHub — its "connection" is an identity link plus an App installation, not a vault row, so folding it into the list would make
connectedmean two things. It is on this page anyway, per the design.Not built, on purpose
No "Use my defaults / Customise for this agent" for connections (it would promise a per-agent scoping that cannot exist); no generalised scope-badge component (it would repeal the rule this move establishes); no folding connections into
/v1/preferences; no #305 split in this change — thoughSettingsTab.tsxdid drop 1169 → 1015 lines and the file-size ratchet pin was lowered to record it, so the ground is held.Corrections confirmed: the tables are
user_api_keysandinstance_connector_grants;connector_tokens/connector_grantsdo not exist.refactor(console): the Settings tab stops saying two things about one…
fix(connectors): a file connector is now offered per AGENT, not per a…
fix(console): the agent's Settings tab is where you choose which of t…
feat(mcp): which account an instance uses reaches MCP — get_instance_…
feat(remote-auth): PAS remote sign-in — push to the exact request, si…