feat(auth): allow passive terminal observation - #9791
Conversation
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
1b83112 to
77bea7c
Compare
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR introduces passive terminal observation across server RPCs, authorization scopes, web/mobile clients, and native terminal surfaces. It also grants terminal-read access through default client scopes, creating broad security and product-default impact that warrants human review. Not approved because:
No code changes detected at Review your spending limits in Billing settings, or comment |
77bea7c to
755a0b0
Compare
755a0b0 to
a7693db
Compare
a7693db to
a9d5aa3
Compare
a9d5aa3 to
12ffc87
Compare
12ffc87 to
c391e46
Compare
c391e46 to
8cf3080
Compare
273e55a to
b053bc0
Compare
b053bc0 to
e104363
Compare
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Watching terminal output previously required
terminal:operate, which also grants shell creation, input, resize, clear, restart, and close. Addterminal:readand a passiveterminal.observestream so clients can read existing history and live output without controlling the shell.Observation supports retained exited sessions and reports missing sessions.
terminal.attachkeeps its existing behavior and requiresterminal:operate. Output and metadata subscriptions requireterminal:read. New read-only, standard, and administrative grants include it; existing credentials keep their recorded scopes and need a fresh grant if terminal viewing is missing.Web, desktop, and mobile use observation for read-only grants. Terminal callbacks recheck the target environment's current grant. Native views guard keyboard input, focus, and terminal-generated replies, and dismiss input when operation access is revoked. Host-editor file links independently require
orchestration:operate.Permission changes preserve visible output and exited history. Version-zero buffers stay pending until session state arrives. Operation access arriving before an observer's first snapshot preserves its observation intent; a later closed or exited snapshot stays passive. An explicit operator that starts while permissions are loading still reopens when its grant arrives; routes without an observation target retain normal behavior. Explicit New, Split, Run, Reopen, and Restart actions keep their behavior. New, Split, and Run allocate fresh IDs when metadata is unavailable. Mobile replays its measured grid when a terminal becomes writable or reconnects; read-only, pending, and revoked connections do not send resize requests.
Earlier revisions passed 61 web terminal cases and 46 mobile/shared terminal cases, including 13 lifecycle cases. The lifecycle suite passed again after integration at 38efd263; those 13 are overlapping coverage. The lifecycle fix passed mobile typecheck and targeted lint. Earlier server tests covered passive history and live output, retained exited and missing sessions, and observer subscriptions with mutations denied.
The browser pass at 6a9376f5 opened an empty observer drawer under
terminal:readwithoutterminal:operate. New Terminal stayed disabled; Enter in the task composer preserved its draft and focus. This verifies the empty observer controls, while the earlier recordings below cover live output and terminal startup.All terminal captures below document earlier revisions. The client checks at 82749030 exercised settings, task drafts, and preview attachment; they did not rerun terminal E2E.
The Android emulator check used only
orchestration:readandterminal:read. It showed live output, no keyboard on tap, and no injected input in the host terminal history.The browser check held terminal startup until the composer had focus and confirmed startup preserved it. Reopening a writable terminal focused its input; reopening an observer kept input read-only and unfocused.
Terminal focus recording
The iPhone check kept the same shell running. Passive observation left its grid at 80×24; re-pairing with operation access resized it to the measured 47×41 without rotation or an external resize request.
stty sizeconfirmed 41 rows and 47 columns. These captures do not demonstrate the version-zero initialization regression.Onboarding Install and Sign in now use the selected environment's
terminal:operategrant and recheck it before queued terminal opening, pretyping, and cleanup. Revocation stops subsequent writes and close requests while preserving an accepted terminal and local Close, Continue, and Skip. Regrant permits an explicit retry without automatically reopening or retyping. Web/desktop resize callbacks now recheck the current grant immediately before dispatch, including when permission changes before React renders again.Real browser verification compared 78c9ad7f with b053bc01, using the same test backend at 78c9ad7f and the same four scopes:
orchestration:read,filesystem:read,terminal:read, andrelay:read. Before, clicking Install ended with “Could not open the setup terminal.” After, both Install buttons are disabled with a permission explanation, while Continue and Skip remain available. Provider installation/authentication states came from owned server fixtures; this pass did not install a provider.The seven setup component cases went from six failures and one pass before the fix to seven passes. The resize regression failed before its fresh check and passed afterward; the targeted terminal run passed all 62 cases. Integrated verification at b053bc01 passed 165 web tests and web/mobile typechecks. The component tests cover revocation across queued setup work and resize dispatch; these races were not manually reproduced in the browser.
Model: GPT 6 Astra. Harness: Codex.
Note
Add passive terminal observation with read-only scopes
AuthTerminalReadScopeand theterminalObserveRPC to stream terminal output without process control.subscribeTerminalEventsandsubscribeTerminalMetadatanow requireterminal:readinstead ofterminal:operate.subscribeTerminalEventsandsubscribeTerminalMetadatain RpcAuthorization.ts now requireterminal:readinstead ofterminal:operate; clients relying solely onterminal:operatewithout the read scope will lose metadata and event streams.Macroscope summarized e104363.
Note
High Risk
Changes auth scopes for terminal subscriptions and splits read vs operate across server RPCs and all clients; clients with operate-only tokens lose metadata/event streams until re-paired with read scope.
Overview
Adds
terminal:readand a passiveterminal.observestream so clients can watch history and live output without attach/write/resize/close. ServerobserveStreamreuses the attach streaming path but only loads an existing snapshot—no spawn, restart, or PTY writes. Terminal event/metadata subscriptions now require read instead of operate alone.Mobile and web gate the terminal UI on
AuthTerminalReadScope/AuthTerminalOperateScope: read-only users observe sessions, see empty states when nothing exists, and get areadOnlysurface (no keyboard accessory, paste, resize RPCs, or “open new terminal”). Native Android/iOS expose areadOnlyprop that disables input, focus, and Ghostty-generated replies.Refactors mobile lifecycle into
useTerminalLifecycle(passive observers don’t reopen or exit-navigate),useTerminalGridSync(resize only when operate + running attachment), anduseTerminalSurfaceBuffer(keeps displayed output across permission/null-buffer transitions). Terminal ID allocation can use a UUID suffix when session metadata isn’t readable, avoiding accidentaldefaultcollisions.Web/desktop mirror the same scope checks for drawer/panel splits, script runs, and keyboard shortcuts;
ProjectScriptsControltreats run as optional when operate is missing.Reviewed by Cursor Bugbot for commit 8274903. Bugbot is set up for automated code reviews on this repo. Configure here.