Repository navigation
Conversation
…aude account switch After /exit, an idle Claude's done row stays on the pane, and the pane can then run the user's own program. Any bare node or python process counted as Claude, so a switch offered to restart a dev server as `claude --resume`, and Always restart did it without asking. Only a process recognized as Claude counts now; the fresh read still names an npm-installed Claude from its command line.
Before it shipped a native binary, Claude Code's npm package ran a generic cli.js, and a pnpm or yarn shim, npx, or npm's Windows shim launches it by that path. Nothing named it Claude, so with the bare node wrapper no longer counted, an account switch would stop offering those sessions. The exact package path now identifies it, as it already does for Pi and Prime Agent.
Walkthrough
Priority: Severity of issue fixed: Medium Merge Risk:
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
458363c0-1f74-4363-a988-726bf8177591
📒 Files selected for processing: 4
src/renderer/src/lib/claude-session-account-move.test.tssrc/renderer/src/lib/claude-session-account-move.tssrc/shared/agent-node-entrypoint-identities.tssrc/shared/agent-process-recognition.test.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.
| // Why: Claude Code's npm package ran a generic cli.js before it shipped a native binary, and a | ||
| // pnpm or yarn shim or npx still launches it by that path. | ||
| { | ||
| pattern: /(?:^|\/)node_modules\/@anthropic-ai\/claude-code\/cli\.js$/, | ||
| agent: 'claude', | ||
| processName: 'claude' | ||
| }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
rg -n "EXACT_NODE_ENTRYPOINT_IDENTITIES|recognizeAgentProcess|isClaudeProcess|claude-session-account-move" src/shared src/renderer/src/lib
sed -n '1,100p' src/shared/agent-node-entrypoint-identities.ts
sed -n '1,120p' src/renderer/src/lib/claude-session-account-move.tsRepository: stablyai/orca
Length of output: 31163
🏁 Script executed:
printf '%s\n' '--- agent-process-recognition.ts ---'
nl -ba src/shared/agent-process-recognition.ts | sed -n '1,250p'
printf '%s\n' '--- account switch callers ---'
nl -ba src/renderer/src/lib/claude-account-switch-sessions.ts | sed -n '1,240p'
printf '%s\n' '--- relevant tests ---'
nl -ba src/shared/agent-process-recognition.test.ts | sed -n '100,180p'
nl -ba src/shared/agent-process-recognition.test.ts | sed -n '540,595p'
nl -ba src/renderer/src/lib/claude-session-account-move.test.ts | sed -n '1,220p'Repository: stablyai/orca
Length of output: 34256
🏁 Script executed:
printf '%s\n' '--- command-line entrypoint parser ---'
nl -ba src/shared/agent-command-line-entrypoint.ts | sed -n '1,280p'
printf '%s\n' '--- account-move implementation ---'
nl -ba src/renderer/src/lib/claude-session-account-move.ts | sed -n '120,430p'
printf '%s\n' '--- runtime inspection bindings ---'
rg -n -F --glob '*.ts' -e 'type RuntimeTerminalProcessInspection' -e 'interface RuntimeTerminalProcessInspection' -e 'confirmRuntimeTerminalForegroundProcess' -e 'inspectRuntimeTerminalProcess' src/renderer src/sharedRepository: stablyai/orca
Length of output: 26159
🏁 Script executed:
printf '%s\n' '--- terminal process inspection contract ---'
rg -n -F --glob '*.ts' -e 'export type TerminalProcessInspection' -e 'export interface TerminalProcessInspection' -e 'foregroundProcess:' src
nl -ba src/shared/terminal-process-inspection.ts | sed -n '1,220p'
printf '%s\n' '--- runtime inspection implementation ---'
nl -ba src/renderer/src/runtime/runtime-terminal-inspection.ts | sed -n '1,180p'
printf '%s\n' '--- process inspection producers ---'
rg -n -F --glob '*.ts' -e 'foregroundProcess' -e 'commandLine' src/main src/renderer src/shared | head -160Repository: stablyai/orca
Length of output: 42214
🏁 Script executed:
printf '%s\n' '--- foreground process selection ---'
nl -ba src/shared/foreground-process-selection.ts | sed -n '1,180p'
printf '%s\n' '--- foreground wrapper identity ---'
nl -ba src/shared/foreground-wrapper-agent.ts | sed -n '1,150p'
printf '%s\n' '--- runtime foreground presence ---'
nl -ba src/main/runtime/runtime-terminal-agent-presence.ts | sed -n '150,235p'
printf '%s\n' '--- command-line recognizer consumers ---'
rg -n -F --glob '*.ts' -e 'recognizeAgentProcessFromCommandLine(' src/shared src/main src/renderer | grep -v 'test.ts' | head -120Repository: stablyai/orca
Length of output: 14338
🏁 Script executed:
printf '%s\n' '--- local foreground inspection ---'
nl -ba src/main/providers/local-pty-foreground-inspection.ts | sed -n '130,220p'
printf '%s\n' '--- foreground process provider ---'
nl -ba src/main/providers/agent-foreground-process.ts | sed -n '110,180p'
printf '%s\n' '--- runtime inspection host binding ---'
rg -n -F --glob '*.ts' -e 'inspectProcess:' -e 'getForegroundProcess:' -e 'inspectRuntimeTerminalProcess' src/main/runtime src/main/providers src/main/daemon | head -100Repository: stablyai/orca
Length of output: 18877
🏁 Script executed:
nl -ba src/main/providers/local-pty-foreground-inspection.ts | sed -n '180,285p'
rg -n -F --glob '*.ts' -e 'function resolveAgentForegroundProcessFromPs' -e 'resolveAgentForegroundProcessFromPs' src/main/providers src/mainRepository: stablyai/orca
Length of output: 6140
🏁 Script executed:
nl -ba src/main/providers/agent-foreground-process.ts | sed -n '180,255p'
rg -n -F --glob '*.ts' -e 'function resolveStableForegroundProcess' -e 'resolveStableForegroundProcess' src/main/providersRepository: stablyai/orca
Length of output: 4105
Stop scanning after the actual Node script token.
Bare server.js is skipped, so a later Claude package path can identify an unrelated Node process as Claude. The foreground resolver returns that recognized claude identity, which can pass the account-move check and queue the pane for a Claude restart.
Suggested fix
-const PROCESS_EXTENSION_RE = /\.(?:exe|cmd|bat|ps1)$/i
+const PROCESS_EXTENSION_RE = /\.(?:js|mjs|cjs|exe|cmd|bat|ps1)$/iThere was a problem hiding this comment.
Right, and it's not specific to this entry: Pi's and Prime Agent's exact entrypoints behave the same, because the shared scanner skips a bare script name like server.js and takes the next path-looking argument. Hitting it here needs a node program that's handed Claude Code's own cli.js path as an argument. I noted it in the PR description and kept the scanner as is, since changing it touches every agent's detection. Happy to do that as a separate PR if the maintainers want it.
There was a problem hiding this comment.
@adifsgaid Thanks for clarifying. A separate PR is reasonable because the scanner change affects detection across agents, including Pi and Prime Agent. This remains a known limitation, not a completed fix.
Would you like me to open a follow-up issue for the shared scanner?
✏️ Learnings added
Learnt from: adifsgaid
Repo: stablyai/orca PR: 30328
File: src/shared/agent-node-entrypoint-identities.ts:17-23
Timestamp: 2026-10-19T06:52:36.369Z
Learning: In stablyai/orca, the shared Node entrypoint scanner in src/shared/agent-command-line-entrypoint.ts can skip a bare script token such as server.js and recognize a later path argument as an agent entrypoint. This limitation affects exact entrypoint identities for Claude Code, Pi, and Prime Agent in src/shared/agent-node-entrypoint-identities.ts; it is not specific to Claude Code.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
You are interacting with an AI system.
…ves-user-programs
ELI5
After you
/exitClaude in a terminal, Orca still remembers that a Claude session finished there. If you then start something else in that terminal, likenpm run dev, the next Claude account switch treats it as a Claude session. The dialog counts it, and Restart stops your program and runsclaude --resumein its place. With "Always restart" that happens without asking.What Changed
Before: any
nodeorpythonprocess in a pane whose Claude had finished counted as Claude.After: only a process Orca recognizes as Claude counts. Anything else in that pane is left alone.
Real Claude sessions still count:
bin/claude.exenow), reads asclaudeby name.node, so it now goes through the fresh process read thatisClaudeInForegroundalready does for names it doesn't recognize. The native macOS install already takes that path, since it reports its version folder (2.1.x). npm's symlink shim (node /usr/local/bin/claude) was already recognized there. A pnpm or yarn shim, npx, or npm's Windows shim runs the package'scli.jsdirectly, which nothing named Claude, so the second commit adds that exact path to the entrypoint table next to Pi's and Prime Agent's.Why
Counting node and python as Claude is a guess, and a wrong guess here kills the user's program. A miss only means one session isn't offered a restart. The fresh read is already there and already identifies a real Claude, so dropping the guess doesn't lose real sessions.
I considered keeping the wrapper names but only in tabs that launched Claude. That still restarts a dev server started in such a tab, which is exactly this case.
Linked Issue
Fixes #30327
Visual Proof
N/A: no UI change. The dialog just stops counting panes that aren't running Claude.
Testing
I manually tested these changes locally
Automated tests added/updated, or explained why not below
claude-session-account-move.test.ts: anodeand apython3program in panes whose Claude finished are not offered, and an older npm Claude (nodein the cached read,claudein the fresh one) still is, through the fresh read. Both fail on the old code.agent-process-recognition.test.ts: Claude Code'scli.jspath is recognized on macOS, Linux and Windows paths, and lookalikes or a plain argument are not.Ran the shared, providers, daemon and agent-hooks suites and the Claude and Codex renderer suites (19,334 passed), both typechecks, oxlint, oxfmt and the changed-code gate, on macOS. Two tests in
repro-13767-shell-ready-marker-lost-to-exec.test.tstime out on this machine, the same on untouched main.Review
cc @Jinwoo-H, a follow-up to #29898. I offered on #29802 to check a few edge cases from its review against #29898, and this is the one that applied. One smaller thing came up too, left out of this PR:
A second one, Claude sessions in a WSL project never being offered, is fixed on main by #31945.
One I couldn't check: since #31945, WSL panes on a Windows host go by the guest's hook evidence instead of a process read. Reading the code, a WSL pane where Claude exited and the user then started their own program may still be offered, since starting the command clears the shell mark and the pane's Claude session record is kept. This PR doesn't change that path, and I can't run WSL here to confirm it.
Agent skill upstream boundary
Notes
Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance
node server.js <path to Claude Code's cli.js>would read as Claude. That needs a program handed Claude Code's owncli.jsas an argument, and changing the shared scanner would touch every agent's detection, so I left it.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)