You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Console + docs for the new Gmail states: read-only vs send-capable, and the write-consent gate #714
Part of the Gmail epic. Follows the tool work — the plumbing has three new refusal states and none of them is currently expressible in the UI.
Why
Once Gmail can send, an owner has three independent switches to reason about, and today the console surfaces one of them:
Is Gmail connected — shown now, AccountConnections.tsx (Preferences → Connections), backed by GET /v1/email/status.
Does this agent have email permission — AgentState.permissions.email, enforced at lib/storage-tools.ts:553, toggled in Settings → Permissions & Connections.
Was the connection granted send scope — new, and unrepresented. A connection made before the send work exists carries gmail.readonly only.
An agent that fails to send because of (3) or (4) reports a tool refusal into a chat transcript. That is the wrong place for a state the owner has to change in Settings.
Scope
GET /v1/email/status gains canSend; AccountConnections.tsx shows Gmail as "connected — read only" with a Reconnect to allow sending action when the stored grant predates the send scope.
The instance Settings → Permissions & Connections block distinguishes "read my mail" from "send mail as me" rather than presenting one email switch, since they are now genuinely different powers.
The header comment at the top of lib/connectors/connected-accounts.ts currently argues at length that Gmail's tools: [] is "the point, not an omission". Once the connector carries declared tools that reasoning is half-true — it remains correct about find_confirmation_link and its permission-flag grant model, and becomes wrong as a statement about the connector. Rewrite it to say which of the two it means, or it will mislead the next reader exactly where the security argument lives.
platform-docs/connectors.md gets the Gmail entry: what it reads, what it can send, and the two gates.
Done when
every refusal the tools can produce corresponds to something the owner can see and change in the console, without reading a chat transcript to discover it
Shipped in 8431765 (console + docs) and 3a1f the seed.
Solved generically rather than as a Gmail special case: GET /v1/connectors now reports grantedScopes and missingScopes for every OAuth connector, comparing what the connector declares against what migration 0133 recorded. Any connector that gains a scope has this problem, not just Gmail.
The distinction that matters: missingScopes: null means we never recorded this grant, which is not the same as nothing is missing — and is exactly the read-only population. needsReconnect treats unknown as stale, but only for a connector that declares write, since a reconnect adds nothing to a read-only one.
The row reads "connected as you@example.com — read-only, reconnect to allow sending".
The stale connected-accounts.ts header the issue flagged was rewritten back in #711 when Gmail moved out, so it no longer half-describes a connector it stopped applying to.
platform-docs/connectors.md gets a Gmail section covering the two scopes and why gmail.modify is not requested, the five tools, the three gates on a send, why a reply's recipient is not the agent's choice, and what happens to a pre-existing connection.
Re-verified at `main` @ `8bf125c4` (2026-08-21) while filing #720/#721. The shipped half is real — `GET /v1/connectors` does report `grantedScopes`/`missingScopes` generically, and `accountConnections.ts` renders the read-only row.
One scope bullet was not delivered and the closing comment does not claim it was:
The instance Settings → Permissions & Connections block distinguishes "read my mail" from "send mail as me" rather than presenting one email switch, since they are now genuinely different powers.
Measured in WebKit at 420px against production, on an instance whose only write connector is GitHub, that block still renders one generic paragraph over an unlabelled checkbox:
Agent write access — "Lets this agent act as you — click, type, and navigate — through the connector on your machine."
☐ Github write access
☐ Allow this agent to read my inbox for sign-in links & codes
31px between the two checkboxes, same card, no per-connector statement anywhere. Not reopening — the finding is carried forward:
Activity
feat(connectors): a connection can be connected AND short of scope, a…
serge-ivo commentedon Aug 19, 2026
Shipped in 8431765 (console + docs) and 3a1f the seed.
Solved generically rather than as a Gmail special case:
GET /v1/connectorsnow reportsgrantedScopesandmissingScopesfor every OAuth connector, comparing what the connector declares against what migration 0133 recorded. Any connector that gains a scope has this problem, not just Gmail.The distinction that matters:
missingScopes: nullmeans we never recorded this grant, which is not the same as nothing is missing — and is exactly the read-only population.needsReconnecttreats unknown as stale, but only for a connector that declares write, since a reconnect adds nothing to a read-only one.The row reads "connected as you@example.com — read-only, reconnect to allow sending".
The stale
connected-accounts.tsheader the issue flagged was rewritten back in #711 when Gmail moved out, so it no longer half-describes a connector it stopped applying to.platform-docs/connectors.mdgets a Gmail section covering the two scopes and whygmail.modifyis not requested, the five tools, the three gates on a send, why a reply's recipient is not the agent's choice, and what happens to a pre-existing connection.serge-ivo commentedon Aug 21, 2026
Re-verified at `main` @ `8bf125c4` (2026-08-21) while filing #720/#721. The shipped half is real — `GET /v1/connectors` does report `grantedScopes`/`missingScopes` generically, and `accountConnections.ts` renders the read-only row.
One scope bullet was not delivered and the closing comment does not claim it was:
Measured in WebKit at 420px against production, on an instance whose only write connector is GitHub, that block still renders one generic paragraph over an unlabelled checkbox:
31px between the two checkboxes, same card, no per-connector statement anywhere. Not reopening — the finding is carried forward:
Related and separately owned: #717, #718.