Skip to content

feat(auth): allow passive terminal observation - #9791

Open
juliusmarminge wants to merge 24 commits into
t3code/auth-diagnostics-scopefrom
t3code/auth-terminal-read-scope
Open

feat(auth): allow passive terminal observation#9791
juliusmarminge wants to merge 24 commits into
t3code/auth-diagnostics-scopefrom
t3code/auth-terminal-read-scope

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Sep 4, 2026

Copy link
Copy Markdown
Member

Watching terminal output previously required terminal:operate, which also grants shell creation, input, resize, clear, restart, and close. Add terminal:read and a passive terminal.observe stream so clients can read existing history and live output without controlling the shell.

Observation supports retained exited sessions and reports missing sessions. terminal.attach keeps its existing behavior and requires terminal:operate. Output and metadata subscriptions require terminal: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:read without terminal: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.

Current browser observer drawer leaves New Terminal disabled and preserves the task draft

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.

Before After
Before: terminal operation only After: separate terminal viewing permission

The Android emulator check used only orchestration:read and terminal:read. It showed live output, no keyboard on tap, and no injected input in the host terminal history.

Android: passive terminal output with input disabled

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

Composer remains focused after terminal startup

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 size confirmed 41 rows and 47 columns. These captures do not demonstrate the version-zero initialization regression.

iPhone terminal reports its measured grid after gaining operation access

Onboarding Install and Sign in now use the selected environment's terminal:operate grant 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, and relay: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.

Before: Install appears available After: Install explains the missing permission
Before: Claude Code and Codex offer Install under a read-only grant After: both Install buttons are disabled while Continue and Skip remain available

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

  • Introduces AuthTerminalReadScope and the terminalObserve RPC to stream terminal output without process control. subscribeTerminalEvents and subscribeTerminalMetadata now require terminal:read instead of terminal:operate.
  • Web and mobile clients attach as observers when only read access is present. They disable terminal input, paste, resizing, and lifecycle controls for read-only users.
  • Terminal ID allocation falls back to UUID-suffixed IDs when terminal metadata is unavailable. This prevents reuse of unreadable session state.
  • Risk: subscribeTerminalEvents and subscribeTerminalMetadata in RpcAuthorization.ts now require terminal:read instead of terminal:operate; clients relying solely on terminal:operate without 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:read and a passive terminal.observe stream so clients can watch history and live output without attach/write/resize/close. Server observeStream reuses 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 a readOnly surface (no keyboard accessory, paste, resize RPCs, or “open new terminal”). Native Android/iOS expose a readOnly prop 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), and useTerminalSurfaceBuffer (keeps displayed output across permission/null-buffer transitions). Terminal ID allocation can use a UUID suffix when session metadata isn’t readable, avoiding accidental default collisions.

Web/desktop mirror the same scope checks for drawer/panel splits, script runs, and keyboard shortcuts; ProjectScriptsControl treats 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.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.6 KiB 15.1 KiB
Codex Thread snapshot wire 7.0 KiB 7.3 KiB
Codex Live turn WebSocket wire 6.6 KiB 7.8 KiB
Codex Live turn WebSocket decoded 57.1 KiB 66.4 KiB
Codex Live turn messages 10 21
Claude Total thread wire 13.6 KiB 15.1 KiB
Claude Thread snapshot wire 7.0 KiB 7.3 KiB
Claude Live turn WebSocket wire 6.5 KiB 7.8 KiB
Claude Live turn WebSocket decoded 57.8 KiB 66.4 KiB
Claude Live turn messages 9 21

Baseline: unavailable · PR result: e104363 · Source CI: success

Scenario and decoded snapshot size

10 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.

  • Codex decoded thread snapshot: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@juliusmarminge juliusmarminge changed the title t3code/auth terminal read scope feat(auth): allow passive terminal observation Sep 4, 2026
Comment thread packages/contracts/src/auth.ts
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/ThreadTerminalDrawer.tsx
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 1b83112 to 77bea7c Compare September 4, 2026 21:19
@juliusmarminge
juliusmarminge marked this pull request as ready for review September 4, 2026 21:25
@macroscopeapp

macroscopeapp Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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:

  • Per-PR cost limit exceeded (workspace setting). Approvability relies on correctness review in order to determine eligibility

No code changes detected at e104363. Prior analysis still applies.

Review your spending limits in Billing settings, or comment @macroscope-app review this PR to bypass the limit and review now. You can add or adjust custom eligibility rules. Learn more.

@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 77bea7c to 755a0b0 Compare September 4, 2026 21:36
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 755a0b0 to a7693db Compare September 4, 2026 21:38
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from a7693db to a9d5aa3 Compare September 4, 2026 21:40
Comment thread apps/web/src/components/ThreadTerminalDrawer.tsx
Comment thread apps/web/src/components/ProjectScriptsControl.tsx
Comment thread apps/web/src/components/ThreadTerminalDrawer.tsx Outdated
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from a9d5aa3 to 12ffc87 Compare September 4, 2026 21:48
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Sep 4, 2026
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 12ffc87 to c391e46 Compare September 4, 2026 21:51
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from c391e46 to 8cf3080 Compare September 4, 2026 22:00

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch 2 times, most recently from 273e55a to b053bc0 Compare September 5, 2026 09:55
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from b053bc0 to e104363 Compare September 5, 2026 10:06
@cursor

cursor Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Bugbot is paused — on-demand spend limit reached

Bugbot 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant