Repository navigation
fix(terminal): the chat view returns to the terminal when the agent quits to the shell - #30804
brennanb2025 wants to merge 48 commits into
Conversation
… the agent Codex and grok leave their terminal title set when they quit, so the title tracker never reports the exit and the chat view stayed over the shell prompt. The confirmed-shell proof (process read or command-finished mark) already retires the agent row and title; it now also ends the chat view, except for Orca's own hibernation or restart stops. Tracked in #30613
Co-authored-by: zhangxaochen <zhangxaochen@163.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. ℹ️ Recent review info
Walkthrough
Priority: Merge Risk:
|
A shell back in front now releases the chat view through one accepted departure per pane; only a confirmed process exit retires the row, launch config and sleeping duplicates. Ctrl+Z keeps the agent's identity and chat is available again after fg. Launch hints no longer count as an agent that ran, so a slow start or reveal keeps the chat view. The process monitor delivers hook-only exits and reads a stopped job as child work. The tab bar, shortcut and pane apply the same shell-in-front rule. Tracked in #30613
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
09f88bae-78e5-410a-967a-b266d3a6a5fe
📒 Files selected for processing: 44
src/main/daemon/daemon-pty-process-inspection.tssrc/main/daemon/session.tssrc/main/daemon/terminal-host-cheap-tier-ps-scan-volume.test.tssrc/main/daemon/terminal-host-non-agent-foreground.test.tssrc/main/daemon/terminal-host-process-inspection-cheap-tier.test.tssrc/main/daemon/terminal-host-process-inspection.tssrc/main/providers/local-pty-child-process-verdict.test.tssrc/main/providers/local-pty-foreground-inspection.tssrc/renderer/src/components/native-chat/native-chat-availability.tssrc/renderer/src/components/native-chat/native-chat-leaf-routing.tssrc/renderer/src/components/native-chat/use-native-chat-toggle-shortcut.tssrc/renderer/src/components/tab-bar/TabBarItemRow.test.tsxsrc/renderer/src/components/tab-bar/tab-agent-types-by-tab-id.tssrc/renderer/src/components/tab-bar/tab-bar-item-surface.tsxsrc/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.tssrc/renderer/src/components/tab-bar/use-tab-bar-runtime-model.tssrc/renderer/src/components/terminal-pane/agent-completion-coordinator-queued-inspection-disposal.test.tssrc/renderer/src/components/terminal-pane/agent-completion-coordinator-test-harness.tssrc/renderer/src/components/terminal-pane/agent-completion-coordinator-types.tssrc/renderer/src/components/terminal-pane/agent-completion-coordinator.tssrc/renderer/src/components/terminal-pane/agent-completion-inspection-result.tssrc/renderer/src/components/terminal-pane/agent-completion-process-monitor.tssrc/renderer/src/components/terminal-pane/agent-completion-process-types.tssrc/renderer/src/components/terminal-pane/agent-completion-stale-evidence-backoff.test.tssrc/renderer/src/components/terminal-pane/pane-foreground-agent-tracker-contract.tssrc/renderer/src/components/terminal-pane/pane-foreground-agent-tracker.test.tssrc/renderer/src/components/terminal-pane/pane-foreground-agent-tracker.tssrc/renderer/src/components/terminal-pane/pane-foreground-monitor-observations.tssrc/renderer/src/components/terminal-pane/pane-foreground-process-exit-retire.test.tssrc/renderer/src/components/terminal-pane/pty-connection-command-finished-cleanup.spec.tssrc/renderer/src/components/terminal-pane/pty-connection-foreground-agent-routing.spec.tssrc/renderer/src/components/terminal-pane/pty-connection-foreground-agent-sampling.spec.tssrc/renderer/src/components/terminal-pane/pty-connection-foreground-process-exit.spec.tssrc/renderer/src/components/terminal-pane/pty-connection-hibernation-kill-shell-foreground.test.tssrc/renderer/src/components/terminal-pane/pty-connection-process-exit-row-retire.test.tssrc/renderer/src/components/terminal-pane/pty-connection-test-environment.tssrc/renderer/src/components/terminal-pane/pty-connection-unanswered-command-finish.spec.tssrc/renderer/src/components/terminal-pane/pty-connection/agent-departure.tssrc/renderer/src/components/terminal-pane/pty-connection/agent-idle-working-handlers.tssrc/renderer/src/components/terminal-pane/pty-connection/confirmed-process-exit-row-retire.tssrc/renderer/src/components/terminal-pane/pty-connection/pane-agent-identity.tssrc/renderer/src/components/terminal-pane/pty-connection/terminal-keydown-fit.tssrc/renderer/src/components/terminal-pane/terminal-chat-ownership-lifecycle.test.tsxsrc/renderer/src/components/terminal-pane/use-terminal-pane-chat-state.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.
|
Quitting an agent without resetting its terminal title could leave Chat selected over the returned shell, allowing composer text to execute there. This PR makes the execution host observe the current terminal interface and lets Chat follow that snapshot, independently of the latest turn row or a view remount. Slow launch, recovery and remote uncertainty must still preserve Chat. Not ready on current head Enter regression: a matched 120-second/eight-PTY/30-chunk control preserved all 480 ordered bytes, completed a real normal launch and took no screenshots inside the measurement windows. Same-renderer input to the later xterm onData listener measured p50/p95/max 37.5/45.1/51.5 ms on the branch versus 0.5/0.8/1.5 ms on main. Independent raw/profile review identified unnecessary synchronous React work before sampled input forwarding: Enter discovery notifies startup state, a presentation refresh replaces the shared observation-map identity, and terminal panes subscribe to that identity even when their own derived values are unchanged. A direct sample shows refresh, then React work, then forwarding. These development-build sampled timings include instrumentation and unequal surrounding UI/listener counts; they are not exact function spans or packaged-build timing. They establish the unnecessary rendering mechanism and the workload failure, without attributing downstream host cost to it. The bounded renderer repair now separates real host-observation publication from coalesced presentation notifications. Pane readers select stable values for both availability and confirmed Chat release; tab projections and mounted cleanup observe the same presentation notifications. Two regression controls fail against the old code, and a separate mutation verifies the confirmed-release dependency; all 65 targeted tests pass on the candidate. A fresh independent reviewer completed the whole source and mechanism comparison, readiness and performance scans, plus the pushed merge check, without a new proven defect. This is source acceptance; it does not establish runtime timing or complete QA. The execution-host lifecycle design, startup/recovery checks, old-peer support and input ordering stay in place. The same Windows timing/quit workload, a bounded idle control and affected macOS agent flows still need to pass on the fix head. Several single-attempt sidebar rows also cleared later on the branch (about3.2–4.5s versus0.6–2.3s on main); their cause and acceptance remain undecided, and the repair does not change lifetime policy. The preceding identity correction preserves the existing terminal incarnation through IPC connection-result projections into real session installation. A production-seam regression covers eight response shapes and rejects mismatched or absent identities. The prior CI: required PR workflow 39698417263 failed on current head Other evidence and remaining gates: the copied historical macOS Current mobile comparison on TEMPORARY: the exact ambiguous cold-restored case retains Chat when pending launch or resume cannot be distinguished safely; tracked in STA-11546. This does not cover hot launch defects or restored-row retirement. The physical exit/write race of external PTY conversations remains a main limitation; settled post-exit Chat exposure is a failure. For Brennan’s review: preceding-head Windows comparisons are first below, followed by historical evidence with its original labels. Review and final-head QA remain incomplete; no completion marker is posted.
Historical captures:
|
Keep interface observations independent of turn rows and derive shell release on the host, with negotiated snapshots for desktop and mobile readers. Preserve ordinary lifetime cleanup, launch intent and legacy peer behavior.
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
178cf971-b9b2-4ca4-a270-7c9f43e45b70
📒 Files selected for processing: 106
mobile/src/session/mobile-native-chat-eligibility.tsmobile/src/session/mobile-native-chat-toggle-action.tsmobile/src/session/mobile-session-route-types.tsmobile/src/session/mobile-terminal-interface-flow.test.tsmobile/src/session/mobile-terminal-records.tsmobile/src/session/use-mobile-native-chat-active-resolution.tssrc/main/agent-hooks/agent-hook-runtime-deps.tssrc/main/agent-hooks/server/agent-exit-evidence.tssrc/main/agent-hooks/server/server-lifecycle.tssrc/main/agent-hooks/server/server-listeners.tssrc/main/agent-hooks/server/server-state.tssrc/main/agent-hooks/server/server-status-admission.tssrc/main/agent-hooks/server/server-status-update.tssrc/main/agent-hooks/server/server-terminal-interface-status.tssrc/main/agent-hooks/server/terminal-interface-authority.tssrc/main/agent-hooks/server/terminal-interface-contracts.tssrc/main/agent-hooks/server/terminal-interface-hook-ingress.tssrc/main/agent-hooks/server/terminal-interface-hook-owner.tssrc/main/agent-hooks/server/terminal-interface-inventory.tssrc/main/agent-hooks/server/terminal-interface-probe.tssrc/main/agent-hooks/terminal-interface-host-flow.test.tssrc/main/daemon/daemon-foreground-process-protocol.tssrc/main/daemon/daemon-pty-process-inspection.tssrc/main/daemon/daemon-request-router.tssrc/main/daemon/pty-subprocess/subprocess-handle.tssrc/main/daemon/session-subprocess-handle.tssrc/main/daemon/session.tssrc/main/daemon/terminal-host-process-inspection.tssrc/main/ipc/desktop-renderer-runtime-capabilities.tssrc/main/ipc/pty/runtime/operations.tssrc/main/providers/local-pty-child-process-verdict.test.tssrc/main/providers/local-pty-provider-foreground-process.test.tssrc/main/providers/local-pty-provider.tssrc/main/providers/pty-process-inspection.tssrc/main/providers/terminal-interface-process-evidence.test.tssrc/main/providers/terminal-interface-process-evidence.tssrc/main/runtime/construction/runtime-construction-dependencies.tssrc/main/runtime/construction/runtime-construction-services.tssrc/main/runtime/construction/runtime-construction.tssrc/main/runtime/orca-runtime-get-status.tssrc/main/runtime/orca-runtime-terminal-interfaces.tssrc/main/runtime/orca-runtime.tssrc/main/runtime/rpc/methods/client-events.test.tssrc/main/runtime/rpc/methods/client-events.tssrc/main/runtime/rpc/methods/session-tab-agent-status-projection.tssrc/main/runtime/runtime-mobile-session-incarnation-projection.test.tssrc/main/runtime/runtime-mobile-session-projection-contract.tssrc/main/runtime/runtime-mobile-session-projection.tssrc/main/runtime/runtime-pty-controller-contract.tssrc/main/runtime/session-lifecycle/projection/prune-mobile-session-tab-group-layout.tssrc/main/runtime/terminal-intentional-stops.tssrc/main/runtime/terminal-run-facts.tssrc/main/runtime/terminal/output/pty-output-ingestion.tssrc/renderer/src/components/native-chat/terminal-interface-presentation.tssrc/renderer/src/components/native-chat/terminal-interface-replica-flow.test.tssrc/renderer/src/components/native-chat/use-native-chat-toggle-shortcut.tssrc/renderer/src/components/tab-bar/tab-agent-types-by-tab-id.tssrc/renderer/src/components/terminal-pane/agent-completion-coordinator-process-cadence.test.tssrc/renderer/src/components/terminal-pane/agent-completion-coordinator-queued-inspection-disposal.test.tssrc/renderer/src/components/terminal-pane/agent-completion-coordinator-test-harness.tssrc/renderer/src/components/terminal-pane/agent-completion-coordinator-types.tssrc/renderer/src/components/terminal-pane/agent-completion-coordinator.tssrc/renderer/src/components/terminal-pane/agent-completion-inspection-result.tssrc/renderer/src/components/terminal-pane/agent-completion-process-monitor.tssrc/renderer/src/components/terminal-pane/agent-completion-stale-evidence-backoff.test.tssrc/renderer/src/components/terminal-pane/pane-foreground-agent-tracker.tssrc/renderer/src/components/terminal-pane/pane-foreground-observation-transitions.tssrc/renderer/src/components/terminal-pane/pty-connection-hook-completion-side-effects.spec.tssrc/renderer/src/components/terminal-pane/pty-connection-notification-settings-gating.spec.tssrc/renderer/src/components/terminal-pane/pty-connection-process-exit-row-retire.test.tssrc/renderer/src/components/terminal-pane/pty-connection/agent-departure.tssrc/renderer/src/components/terminal-pane/pty-connection/connect-pane-pty-session.tssrc/renderer/src/components/terminal-pane/pty-connection/connect-pane-pty.tssrc/renderer/src/components/terminal-pane/pty-connection/deferred-cold-restore-and-snapshot.tssrc/renderer/src/components/terminal-pane/pty-connection/deferred-session-reattach-connect.tssrc/renderer/src/components/terminal-pane/pty-connection/fresh-spawn-start.tssrc/renderer/src/components/terminal-pane/pty-connection/hidden-agent-resume-deferral.tssrc/renderer/src/components/terminal-pane/pty-connection/pane-agent-identity.tssrc/renderer/src/components/terminal-pane/pty-connection/pane-pty-visibility-bind.tssrc/renderer/src/components/terminal-pane/pty-connection/pane-serializer-settle.tssrc/renderer/src/components/terminal-pane/pty-connection/pane-terminal-interface-presentation.tssrc/renderer/src/components/terminal-pane/pty-connection/pty-exit-hibernate.tssrc/renderer/src/components/terminal-pane/pty-connection/pty-input-recovery.tssrc/renderer/src/components/terminal-pane/pty-connection/reattach-result-handler.tssrc/renderer/src/components/terminal-pane/pty-connection/restart-replacement-on-show.tssrc/renderer/src/components/terminal-pane/pty-connection/session-reconcile-dispose.tssrc/renderer/src/components/terminal-pane/pty-connection/spawn-deferred-sleep.tssrc/renderer/src/components/terminal-pane/pty-connection/terminal-keydown-fit.tssrc/renderer/src/components/terminal-pane/pty-connection/transport-output-callbacks.tssrc/renderer/src/components/terminal-pane/terminal-chat-ownership-lifecycle.test.tsxsrc/renderer/src/components/terminal-pane/use-terminal-pane-chat-state.tssrc/renderer/src/components/terminal-pane/use-terminal-pane-projection.tssrc/renderer/src/hooks/ipc-events/runtime-client-ipc-bridge.tssrc/renderer/src/lib/pane-spawn-deferral-registry.tssrc/renderer/src/runtime/runtime-client-events.tssrc/renderer/src/store/slices/pane-foreground-agent.tssrc/shared/electron-remote-runtime-client-capabilities.tssrc/shared/process-evidence-budget.tssrc/shared/process-table-snapshot-reader.tssrc/shared/protocol-version.tssrc/shared/remote-runtime-client-capabilities.tssrc/shared/runtime-client-events.tssrc/shared/runtime-mobile-session-tab-contracts.tssrc/shared/terminal-interface-observation.tssrc/shared/terminal-process-inspection.tstests/e2e/terminal-foreground-confirmation.unit.test.ts
💤 Files with no reviewable changes: 1
- src/renderer/src/components/terminal-pane/agent-completion-coordinator.ts
🚧 Files skipped from review as they are similar to previous changes: 5
- src/renderer/src/components/native-chat/use-native-chat-toggle-shortcut.ts
- src/renderer/src/components/terminal-pane/use-terminal-pane-chat-state.ts
- src/renderer/src/components/terminal-pane/terminal-chat-ownership-lifecycle.test.tsx
- src/renderer/src/components/terminal-pane/pty-connection-process-exit-row-retire.test.ts
- src/renderer/src/components/terminal-pane/pane-foreground-agent-tracker.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
| entry.due = | ||
| performance.now() + | ||
| (entry.owner?.current() || | ||
| (entry.actual && entry.observation?.state !== 'shell') || | ||
| performance.now() < entry.discoverUntil | ||
| ? 750 | ||
| : 15000) |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift
🔎 Supported by static analysis
🏁 Script executed:
set -u
printf '%s\n' '--- revision ---'
git rev-parse HEAD
printf '%s\n' '--- diff stat ---'
git diff --stat d4813638d20e7269764dc941d4778ed581628e68 842f870c9a51e793be5b51d5fcd8ac649291937f -- src/main/agent-hooks/server/terminal-interface-authority.ts
printf '%s\n' '--- authority symbols ---'
rg -n -F -- 'getFreshSnapshot' 'inspectPosixTerminalInterface' 'inspectDaemonTerminalInterface' 'input(' 'sample(' 'due =' 'discoverUntil' 'shellSince' 'setTimeout' 'setInterval' 'capture' src/main/agent-hooks/server/terminal-interface-authority.ts src/main/agent-hooks || true
printf '%s\n' '--- authority source 1-360 ---'
nl -ba src/main/agent-hooks/server/terminal-interface-authority.ts | sed -n '1,360p'
printf '%s\n' '--- authority source 361-760 ---'
nl -ba src/main/agent-hooks/server/terminal-interface-authority.ts | sed -n '361,760p'
printf '%s\n' '--- changed diff ---'
git diff --unified=35 d4813638d20e7269764dc941d4778ed581628e68 842f870c9a51e793be5b51d5fcd8ac649291937f -- src/main/agent-hooks/server/terminal-interface-authority.tsRepository: stablyai/orca
Length of output: 22392
🏁 Script executed:
set -u
printf '%s\n' '--- probe and contracts files ---'
rg --files src/main/agent-hooks/server | sort
printf '%s\n' '--- exact symbols ---'
rg -n -F -e 'class TerminalInterfaceProbe' -e 'getFreshSnapshot' -e 'inspectPosixTerminalInterface' -e 'inspectDaemonTerminalInterface' -e 'inspect(' -e 'ps ' -e 'ps)' -e '/proc/' -e 'TTL' src/main/agent-hooks src/main shared || true
printf '%s\n' '--- probe source ---'
probe=$(rg -l -F 'class TerminalInterfaceProbe' src/main/agent-hooks/server | head -n 1)
if [ -n "$probe" ]; then nl -ba "$probe"; fi
printf '%s\n' '--- related inspector source files ---'
for f in $(rg -l -F -e 'getFreshSnapshot' -e 'inspectPosixTerminalInterface' -e 'inspectDaemonTerminalInterface' src/main/agent-hooks src/main shared 2>/dev/null | sort -u); do
echo "### $f"
nl -ba "$f"
done
printf '%s\n' '--- contracts source ---'
for f in src/main/agent-hooks/server/terminal-interface-contracts.ts src/main/agent-hooks/server/terminal-interface-inventory.ts; do
if [ -f "$f" ]; then echo "### $f"; nl -ba "$f"; fi
doneRepository: stablyai/orca
Length of output: 45658
🏁 Script executed:
set -u
printf '%s\n' '--- terminal interface probe ---'
nl -ba src/main/agent-hooks/server/terminal-interface-probe.ts
printf '%s\n' '--- exact inspector definitions and calls ---'
rg -n -F -- 'inspectPosixTerminalInterface' src || true
rg -n -F -- 'inspectDaemonTerminalInterface' src || true
rg -n -F -- 'getFreshSnapshot' src/main || true
rg -n -F -- 'getSnapshot' src/main/agent-hooks src/shared || true
printf '%s\n' '--- terminal interface driver construction ---'
rg -n -F -e 'TerminalInterfaceDriver' -e 'inspect: ' -e 'inspect =' src/main/agent-hooks/server src/main | head -200 || trueRepository: stablyai/orca
Length of output: 16638
🏁 Script executed:
set -u
printf '%s\n' '--- process evidence ---'
nl -ba src/main/providers/terminal-interface-process-evidence.ts
printf '%s\n' '--- local PTY provider inspection ---'
nl -ba src/main/providers/local-pty-provider.ts | sed -n '140,225p'
printf '%s\n' '--- runtime terminal interfaces ---'
nl -ba src/main/runtime/orca-runtime-terminal-interfaces.ts
printf '%s\n' '--- snapshot reader implementation ---'
nl -ba src/shared/process-table-snapshot-reader.ts | sed -n '1,155p'
printf '%s\n' '--- all runtime authority install/use sites ---'
rg -n -F -e '.install(' -e 'TerminalInterfaceAuthority' -e 'terminalInterfaceAuthority' src/main/runtime src/main/daemon src/main/providers src/main/agent-hooks | head -250 || trueRepository: stablyai/orca
Length of output: 31376
🏁 Script executed:
set -u
printf '%s\n' '--- strict fresh reader binding ---'
rg -n -F -e 'getFreshStrictProcessTableSnapshotWithAge' -e 'getStrictProcessTableSnapshotWithAge' src/shared/process-table-snapshot-reader.ts src/main/providers/terminal-interface-process-evidence.ts
nl -ba src/shared/process-table-snapshot-reader.ts | sed -n '280,345p'
printf '%s\n' '--- process inspection implementations ---'
rg -n -F -e 'async inspectProcess' -e 'inspectProcess(' -e 'terminalInterface:' src/main | head -250 || true
printf '%s\n' '--- daemon and local provider definitions ---'
rg -n -F -e 'class Daemon' -e 'class LocalPtyProvider' -e 'inspectChildProcesses' -e 'inspectProcess' src/main/daemon src/main/providers src/main/runtime | head -300 || trueRepository: stablyai/orca
Length of output: 34013
Reduce fresh process-table sampling for active terminal entries.
OrcaRuntimeWithTerminalInterfaces requests terminal-interface inspection for each local sample. On POSIX, that path always calls getFreshStrictProcessTableSnapshotWithAge(), which bypasses the TTL snapshot. Active entries are rescheduled after 750 ms, and each input sets due = 0. This can repeatedly trigger expensive whole-machine process-table captures and increase CPU and I/O under load.
The reader already coalesces concurrent fresh requests, so sharing concurrent captures does not fix the steady-state rate. Use the TTL-backed snapshot for routine samples. Reserve fresh reads for confirming a candidate shell transition or another explicit freshness boundary. Debounce input-triggered sampling so a burst of keystrokes schedules one follow-up sample.
The capture is not continuously in progress: pending prevents duplicate samples for one entry, and the next sample is scheduled 750 ms after the current sample completes.
| children: 'unverifiable', | ||
| capturedAgeMs | ||
| } | ||
| if (!root || !root.tty || root.tty === '?' || capturedAgeMs > 1000) { |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
The 1,000 ms age limit rejects every capture that takes longer than about 1 s, so shell is never published on ordinary hosts.
getFreshStrictProcessTableSnapshotWithAgesetscapturedAgeMsto the full wall time from request to completion. That time includes the wait for any capture already in flight, becausegetFreshSnapshotwaits for it before it starts a new one.- This line rejects ages above 1,000 ms.
terminal-interface-authority.tsLine 205 repeats the same literal. process-evidence-budget.tsdocuments these capture times:- 1.15 s for the
command=column on an idle host with 1,948 processes. - 2.5–9.0 s on an idle laptop with 2,002 processes.
- 1.15 s for the
- On those hosts every sample returns
children: 'unverifiable'. The authority then never publishesshell, and Chat stays selected after the agent quits. That is the defect this PR fixes. - Both callers make this worse, because each runs a second capture after the first one:
- The daemon path (
inspectTerminalHostProcess) first callsgetStrictProcessTableSnapshotWithAge. - The local path (
LocalPtyProvider.inspectProcess) first callsinspectLocalPtyChildProcesses(id, true). inspectSessionTerminalInterface/inspectPosixTerminalInterfacethen wait for that capture and start another fresh one, which roughly doubles the elapsed time.
- The daemon path (
Recommended changes:
- Define the limit once, in
process-evidence-budget.ts, and set it from the documented capture cost. Use the same constant in both places. - Pass the rows already captured in the same inspection to
terminalInterfaceEvidenceFromRows, so each inspection runs only one capture.
🐛 Proposed change for the shared age limit
--- "a/src/main/providers/terminal-interface-process-evidence.ts"
+++ "b/src/main/providers/terminal-interface-process-evidence.ts"
@@ -24,7 +24,12 @@
children: 'unverifiable',
capturedAgeMs
}
- if (!root || !root.tty || root.tty === '?' || capturedAgeMs > 1000) {
+ if (
+ !root ||
+ !root.tty ||
+ root.tty === '?' ||
+ capturedAgeMs > TERMINAL_INTERFACE_EVIDENCE_MAX_AGE_MS
+ ) {
return unknown
}
const tree = [{ ...root, depth: 0 }, ...collectDescendantsFromIndex(index, rootPid)]Use TERMINAL_INTERFACE_EVIDENCE_MAX_AGE_MS in terminal-interface-authority.ts Line 205 as well.
Keep main's confirmation guard, option, wiring and regression coverage. Update the cohort contract from zero confirmations to one per admitted monitor, with one shared ordinary capture and one shared fresh capture. Unverifiable child evidence still adds no confirmation; a later verifiable shell result does. Retain the actual dispatch/result ownership proof.
Keep cross-layer tests in the joint unit program and use a no-children shell fixture for the existing confirmation branch. Tighten the full-tree bundle measurement from 91 to the observed 90 chunks without changing its margin.
Retain incoming Claude view routing and preserve hook ingress timing. Move transport rollback unchanged into the existing lifecycle base to keep the merged file within its line limit.
Preserve fake-time completion ordering and align the mobile budget assertions with the measured chunk count.



















Problem
Quitting an agent can leave Chat selected over a shell because its terminal title never changes. Text entered in the composer can then execute in the shell. Tracked in #30613; includes the reporter's contribution from #30614, credited as co-author.
User-facing change
When the execution host confirms that the agent interface has released the terminal, desktop and phone return to Terminal. Chat stays available through slow launch and recovery. The user's saved Chat choice and phone draft remain intact so a later launch can select Chat again. Commands, aliases, functions, folder workspaces and existing close confirmation keep their behavior.
How it works
The execution host maintains a runtime terminal observation independently of turn completion and sidebar rows. Consumers derive Chat availability from that observation and the current terminal identity. Discovery continues after a completed turn, a silent quit or an unmounted pane. The common pattern keeps observations with their execution owner and delivers them independently of session structure.
The existing command-finished path owns its delayed fresh confirmation and removes the same turn's sidebar row before reconciliation. Foreground presentation observations must not cancel that obligation. Process lifetime remains separate: a current local hook owner can retire only after sustained explicit no-children evidence and the existing binding, input, owner and recovery checks.
Process inspection shares host-wide table captures and keeps the existing cheap steady-state checks. Periodic renderer inspection preserves main's live-agent exit confirmation and adds no confirmation demand beyond main. The phone consumes the existing observation event and joins it with its session tabs; observation changes must cause no browser reads, persistence requests, tab projections or structural version changes. Browser membership, titles and URLs remain owned by ordinary session publication.
Existing capability negotiation preserves older clients and hosts without a minimum version. Missing names, unavailable reads and contact loss do not prove departure. Input does not wait for observation or cleanup. Launch commands and the existing raw-input path remain unchanged; no observation or cleanup result becomes a send guard.
Verification status
This PR is not ready to merge. The pushed candidate is 29892a7, following 95a288a and the accepted merge with exact main bb47718. Independent review approved the exact scheduler and fixture correction. The normal commit hook passed, and the committed tree equals the reviewed candidate. The complete formatted contribution is 3046 canonical production lines against that main revision, within the 3053-line ceiling. The one standard CI watch failed on unit shard 3/5. All other workflows and selected PR checks passed, including static analysis/typecheck, mobile, shard 2/5, WSL, Headless and packaged Windows D7. Both failures are in the startup-mobile fixture after it changes mocked inventory without a notification. The broader source audit also found direct connectivity changes during retained-PTY output and floating liveness that can bypass observation discovery. A production edge correction is being prepared before correcting the fixture; no unchanged retry is planned.
The observation owner now keeps one timer for the first needed 250 ms admission slot and skips empty slots. Existing PTY creation, identity, input, activity, launch completion, recovery, stop and sleep events wake that same owner. A fast entry can rearm while a slower wave peer is still pending. Native-work limits, currentness and age proof, publication grouping and the command-finished confirmation are preserved. The earlier fixture also now stops its real hook-server owner before restoring the clock. Focused gated checks provide passing evidence for 91 distinct cases across recorded runs, including the unchanged seven-frame bound, native-operation contracts and whole-flow Chat recovery. The idle fixture identified a separate machine-name startup timeout and awaits existing readiness before asserting zero wakeups and inventory work over 60 seconds, then discovering a real newly registered PTY. These checks do not establish CPU savings or runtime latency.
Incoming main's live-agent exit confirmation, its wiring and regression suite are preserved. The operation contract requires the same confirmation count as main. At N=10 and N=100 it checks one shared ordinary full capture, then one shared fresh successor after releasing the held confirmations, with no cheap or narrow capture in that controlled wave. The generic daemon result remains unchanged. This is a deterministic work bound, not a latency measurement. Host observation carries the actual dispatch time with each coalesced inspection result, excluding time queued before dispatch consistently from both conservative capture bounds. The age limit, input and identity fences, unavailable behavior and ownership of native work are unchanged. The whole-flow regression covers unavailable and stale evidence, input during a read, coalesced successors, conservative cached age, manual re-entry and preserved draft/settings.
The targeted M4Air diagnostic run reproduced successful Chat shortcut entry after fg on 57a8490, 95a288a and main bb47718. The earlier shortcut failure did not reproduce. The header Continue action is refused while the live CLI holds the conversation, through the same writer gate on 95a288a and main; this is inherited behavior, not a PR regression. The older 57a8490 header trace has a coverage gap. The complete 95a288a refusal trace had a qualified foreground observation and no queue-age rejection. No foreground correction is selected.
On the diagnostic 95a288a comparison, all three zsh row cases, sh Claude Ctrl+C and bash Codex Ctrl+C passed. Three sh row cases still exceeded main by more than 150 ms: Codex /exit, Codex Ctrl+C and Claude /exit. Every slower case was removed by the same-turn command-finished path. Main also showed substantial timing variance; per-trial causal analysis is pending. The candidate released Chat after quit in all 18 primary trials. These are evidence for 95a288a, not runtime verification of the new scheduler. The owner will repeat the remaining sh cases five times each on the final candidate. Temporary metadata-only diagnostic patches remain outside the PR. The new-head patch passed independent source and clean application checks; logging overhead is not assumed timing-neutral. No completion marker has been added.
Deliberate limits
Ctrl+Z returns to Terminal after confirmed shell foreground. Suspension identity keeps the existing limits, including no automatic Chat selection after fg. An agent never recognized in the foreground, with no command-finished mark, keeps the existing Done row until the next turn or tab close.
Ambiguous cold-restored Chat remains TEMPORARY, tracked in STA-11546. A restored row alone cannot authorize process retirement. The presentation fallback requires fresh local shell/no-children evidence and no launch or resume expectation.
Terminal-backed Chat writes to the agent's terminal. A write already issued at the instant the agent quits retains the existing process/write race. A composer remaining available after confirmed, settled release is a failure.
Review scope
Full review: terminal Chat lifecycle, asynchronous process observations and independently updated desktop, server and phone versions. No upstream agent skill source is copied or translated.