Skip to content

fix(terminal-pane): quitting Claude or Codex in cmd.exe or Git Bash removes it from the running list - #27742

Merged
brennanb2025 merged 1 commit into
mainfrom
brennanb2025/agent-exit-drops-live-row
Oct 12, 2026
Merged

brennanb2025 merged 1 commit into
mainfrom
brennanb2025/agent-exit-drops-live-row

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​339 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​339
Prod 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​46 $\color{#cf222e}{\Huge{\mathbf{−}}}$​5 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​41

ELI5

If you quit Claude or Codex inside a Windows cmd.exe terminal (or Git Bash), Orca kept treating the agent as running in that terminal. The sidebar still listed it, and the tab's right-click menu still offered "Resume in New Native Chat". Now, once Orca's own process check confirms the agent's process is gone, the agent drops out of the running list. Its saved session is kept, so you can still resume it later.

What Changed

The problem. Orca keeps one "live agent row" per terminal pane. The sidebar, the tab menu and everything else that asks "is an agent running here?" read that row. There were only two ways a pane's row went away while its shell kept running:

  • The shell sends a "command finished" marker (OSC 133;D). Orca's bash, zsh and PowerShell setup does this. cmd.exe and Git Bash never send it.
  • Claude's own SessionEnd hook ends the pane. This only works when the hook carries Claude's process identity, which macOS and Linux capture. The Windows hook (a .cmd file) carries none, so on native Windows SessionEnd is ignored.

The pane already has a process monitor that polls its foreground process. It confirms an agent exited only when no agent is in the foreground, the shell has no child processes, and that has held for more than 1.5 s across polls (a read that can't answer never counts). On that confirmation it reset the tab title and the launch record, but it never touched the live row. So in cmd.exe, after /exit, the row stayed until the tab was closed. We saw this on Windows with Claude v2.1.296.

Before: quit Claude/Codex in a cmd.exe or Git Bash pane, and the sidebar keeps listing it. The tab menu keeps offering "Resume in New Native Chat", the option PR #27520 limits to panes whose agent is still running.

After: within a few seconds after Orca confirms the agent's process is gone, the row is removed and the sidebar stops listing it. The tab menu follows #27520's rule. The resume identity (the session id Orca uses to resume) is kept, exactly as after a normal exit in a zsh pane.

Mechanism. When the pane's process monitor confirms the agent exited, the pane now arms the same pair a confirmed 133;D arms:

  1. the same-turn drop, which removes the row only if no new hook has changed it since, and
  2. the main-side reconcile, which also clears the hook server's per-pane latches. It still keeps the resume identity (preserveResumeIdentity).

It then settles that pair behind the existing hasVerifiableAgentProcess gate. If the execution host can verify the owner process itself (an identified macOS/Linux Claude), the host keeps the decision and nothing changes. Otherwise (native Windows Claude, Codex, other hook agents) the row is dropped and reconciled.

Files:

  • pty-connection/confirmed-process-exit-row-retire.ts (new): the retire step and its skip rules.
  • pty-connection/terminal-keydown-fit.ts: the monitor's confirmed-exit callback calls it next to the existing tracker call. It doesn't depend on the tracker having published its own process read. A pane known only from hooks never gets that read, and the tracker skips it.
  • pty-connection/pane-agent-identity.ts: the existing settle step takes a processExitConfirmed option, so the non-verifiable branch also runs the reconcile. Today that branch only drops, which is right when there is no proof of exit. The reconcile helper is exposed on the session so the new file reuses it.

Skipped (unchanged behavior):

  • A 133;D drop is already armed: its own confirming read decides.
  • A remote pane (SSH or a paired runtime host): the execution host owns its rows.
  • No row.
  • A row of a different agent.

Why

The common pattern is that the agent's own lifecycle decides "running", and a confirmed process exit takes the agent out of the running list while its resume data is kept separately. Nothing relies on a shell's command-finished marker for this. Orca already had both pieces: a process monitor that proves the exit, and a drop/reconcile pair that retires a row. They just weren't connected.

Alternatives considered:

  • Make the Windows hook carry Claude's process identity, so SessionEnd ends the pane on the host. This is the real fix for Claude, but it means capturing the parent process from a .cmd hook. That needs the EDR review in AGENTS.md (no per-hook PowerShell). It also doesn't help Codex or other hook agents, which carry no identity on any platform. It is the follow-up below.
  • Add a shell integration to cmd.exe so it emits 133;D. cmd.exe has no reliable prompt hook, Git Bash would need its own, and it still would not catch an agent that crashes.

The guards from the earlier drop/reconcile changes

The drop/reconcile code came from #23947 and #23948. This change keeps what they guard against:

  • A false exit from a Windows process scan. Nothing new here trusts a single read. Only the monitor's existing confirmation triggers it: no agent in the foreground, no child processes under the shell, more than 1.5 s across at least two polls, and an unanswerable child read resets it. A live Claude is a child of the shell, so a scan that misses it in the foreground still sees the child. Remote "can't reach the host" is never an exit.
  • Clearing Claude's background-task/subagent latches while it is still alive. The reconcile runs only after that confirmation, and only for panes whose owner the host can't verify. An identified macOS/Linux Claude keeps fix(agent-status): preserve hook presence when process checks cannot answer (step 1 of 3) #23947's rule ("only the owner's own proven process can end it") because the gate defers to the host. The reconcile also keeps its acceptedStatusSeq check, so if the pane reported anything after the exit was armed, the latches are left alone.
  • A late hook recreating the row. The drop runs only if the row is unchanged since the exit was confirmed (the same-turn check). A relaunched agent's hook, or a new command start, cancels it before the drop runs. A straggling hook from the exited process that arrives after the drop could still bring the row back. That is the same narrow window the 133;D path already has; Claude runs its hooks before it exits, and on Windows its SessionEnd maps to no status at all. The identity follow-up closes it fully.

Other edge cases:

  • Split panes: rows and monitors are per pane, so one pane's exit can't touch a sibling.
  • Agent relaunched before the exit settles: the monitor resets a pending exit when it sees a new agent, and a hook that lands during the host query changes the row, so neither the drop nor the reconcile runs.
  • WSL: an identified Claude in WSL is verifiable, so it defers to the host as today.
  • Parked (unmounted) panes: no monitor runs there, so they behave as today until revealed.

Deviation

  • Temporary: the renderer drops the row for panes whose owner process the execution host can't verify. That puts a renderer-side process read in charge of a row in the host's store. It is the same split the 133;D path already uses, but it isn't the end state. Follow-up: capture Claude's process identity in the Windows hook, so the host ends the pane from Claude's own SessionEnd. The renderer path is then only the backstop for crashes and killed agents. This needs the EDR review first.

Linked Issue

N/A (internal). This unblocks PR #27520's "only while the agent is running" rule for "Resume in New Native Chat" on cmd.exe and Git Bash panes.

Visual Proof

Fresh live QA on Windows high spec, with real Claude and the existing login in place. Both PRs were applied together: #27520 06d746eb1219c2c54e4ef3d4f0b6399f21868d3d, #27742 50c5600c91d777dc445ac6720bd21de914b3ea65, combined QA commit 34ee6f3321cf18109b131a570d5d250f6f16d1e1. The baseline used the same current-main base, d4a55f583e880b6bbbbca11faa0a45d88646d3a6.

Before — same baseline, after /exit: native Claude had exited, but the sidebar still listed it and the tab menu still offered Resume in New Native Chat.

Baseline after Claude exits: stale row and menu item remain

After — both PRs, after /exit: the sidebar row and menu item disappear. The displayed tab title stays QA27742 exact reply. Claude PID 53412 was first observed absent 1.047 s after the command; the sidebar row was first observed absent at 4.199 s (present at 3.143 s). These are sampling times, not an exact process-exit timestamp.

Combined build after Claude exits: row and menu item gone, title retained

Conversation retained: Agent Session History still lists the same conversation after exit.

Session History retains the conversation after exit

PowerShell control: the pane shell was verified as native powershell.exe. After real Claude exits, the sidebar row and the menu item both disappear.

PowerShell after Claude exits: row and menu item gone

Rows 1, 2, 3 and 5 passed. Optional real Codex row 4 was skipped: the reviewed launcher wraps only Claude, so Codex required a separate wrapper/profile setup. Other platforms and shells were not exercised in this run. Windows were hidden and driven through Chrome DevTools Protocol (CDP); no desktop input or focus changes. SHA-256 checks of the real Claude settings and .orca tree matched before activation and after teardown; no hooks were installed into the real home. Screenshots are unaltered and include existing startup notices.

Testing

New pty-connection-process-exit-row-retire.test.ts runs the real pane wiring (connectPanePty):

  • A cmd-style pane (no 133) with a Claude row: the monitor confirms the exit, the row is dropped without paneRemoved (resume identity kept), and reconcileEndedProcess runs.
  • A hook-identified Codex row the tracker never read: retired.
  • An armed 133;D drop: unchanged.
  • A remote (SSH) pane: unchanged.
  • The host can verify the owner: the row is kept.
  • A hook from a relaunched agent lands before the host answers: no drop, no reconcile.
  • A different agent's row: unchanged.
  • Split panes: only the exited pane is touched.

Each skip rule was checked by removing it and seeing its test fail. The existing terminal-pane suites (pty-connection*, pane-foreground*, agent-completion-coordinator*, pty-connection/*; 102 files, 920 tests) pass. orca-ci-checks: 25 of 26 checks pass, including lint, the changed-code quality gate and pnpm run typecheck. The one failure is typecheck:ci, which fails on this machine because the local Bun is 1.3.14 and CI uses 1.4.3 ("Script not found "check"" for every project). That is local tooling, not this change.

Live Windows QA: cmd.exe running/exit/history and the native PowerShell control passed on the combined build; the same-base baseline retained the stale row and menu item. Optional real Codex was skipped. No automated suites or CI checks were rerun by this QA lane.

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance

  • Renderer-only. No new IPC, no wire change, no new process reads: it reuses the monitor's existing polls.
  • SSH and paired runtime panes are excluded on purpose.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots attached for the Windows UI change
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

…from the running list

In shells that send no command-finished mark (cmd.exe, Git Bash), quitting
Claude or Codex left the pane's live agent row behind: the pane's process
monitor confirmed the exit, but only the OSC 133;D path ever dropped the row.

The monitor's confirmed exit now arms the same drop and main-side reconcile
pair a confirmed 133;D uses, settled behind the existing host-verifiability
gate: a pane whose owner process the host can verify keeps the host's
decision; every other pane drops the row and keeps its resume identity.
Skipped when a 133;D drop is already armed, for remote panes, and when the
row belongs to another agent.
@coderabbitai

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a448d15c-40bc-4180-9b5b-fc14c21941bd


📥 Commits

Reviewing files that changed from the base of the PR and between be4cea1 and 50c5600.



📒 Files selected for processing (4)
  • src/renderer/src/components/terminal-pane/pty-connection-process-exit-row-retire.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection/confirmed-process-exit-row-retire.ts
  • src/renderer/src/components/terminal-pane/pty-connection/pane-agent-identity.ts
  • src/renderer/src/components/terminal-pane/pty-connection/terminal-keydown-fit.ts


Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.




📝 Walkthrough
📝 Walkthrough

Walkthrough

The foreground-process exit callback now retires the matching live agent row after notifying the pane tracker. The retirement helper skips ineligible panes and rows, then defers status dropping and quiet-pane reconciliation. Session settlement reconciles ended-process state when the process exit is confirmed and the deferred drop is not verifiable. Tests cover local and remote panes, pending drops, owner-process verification, newer sessions, agent mismatches, and split panes.



Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to 50c56

A newly started agent could briefly lose its live row until its next status update. This is a bounded, recoverable race; the change remains mergeable with owner awareness.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly describes the primary user-facing fix: removing stale Claude or Codex entries after they quit in cmd.exe or Git Bash.
Description check Passed The description is detailed and covers the required change, rationale, testing, visual proof, platform considerations, and checklist. It also explains the failed local typecheck: Bun 1.3.14 was used i…

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR



  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Quitting real Claude in cmd.exe previously left a stale sidebar row and Resume in New Native Chat action. Windows high-spec QA with #27520 passes all mandatory rows: running row/action present; /exit removes both (first row-absent sample 4.199 s), preserves the displayed title, and keeps the conversation in Session History; the same-base baseline reproduces both stale items; the native PowerShell control clears both. Fresh screenshots are embedded in the PR body.

Fresh live QA on Windows high spec, with real Claude and the existing login in place. Both PRs were applied together: #27520 06d746eb1219c2c54e4ef3d4f0b6399f21868d3d, #27742 50c5600c91d777dc445ac6720bd21de914b3ea65, combined QA commit 34ee6f3321cf18109b131a570d5d250f6f16d1e1. The baseline used the same current-main base, d4a55f583e880b6bbbbca11faa0a45d88646d3a6.

Optional real Codex was skipped because it needs a separate wrapper/profile. Real-home settings and .orca hashes stayed unchanged. Owned apps, controls and QA worktree were cleaned up and verified gone. No source edits, pushes or PR merges; other platforms and automated/CI checks were not rerun.

@brennanb2025
brennanb2025 merged commit 14ec470 into main Oct 12, 2026
37 checks passed
@brennanb2025
brennanb2025 deleted the brennanb2025/agent-exit-drops-live-row branch October 12, 2026 08:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant