Repository navigation
fix: an Orca started from an Orca terminal no longer leaks that pane's identity into chat agents - #28267
fix: an Orca started from an Orca terminal no longer leaks that pane's identity into chat agents#28267brennanb2025 wants to merge 5 commits into
Conversation
…at pane's identity When Orca itself is launched from a shell inside one of its terminal panes (a dev build, `orca serve`, or the app run from a pane), its environment carries that pane's identity: ORCA_PANE_KEY, ORCA_TAB_ID, ORCA_WORKTREE_ID, the launch token, the pane's hook route, and so on. Every child the new Orca spawns inherited it. Native-chat Claude and Codex sessions passed it straight through, so their hooks reported the chat's conversation as that pane's agent, the pane saved it, and on the next restart the pane resumed the chat's conversation as a terminal agent in the pane's own workspace (#26417). Both host entry points already drop one piece of the launching terminal's identity (its runtime source stamp). They now drop all of it, from one list: pane identity and its scrub-safe aliases, the pane's workspace hints and terminal handle, the pane's hook route, a chat session's caller identity, and a chat's visuals folder. Orca never sets any of these on itself, so nothing it needs is lost; terminals still get their own identity explicitly at spawn. The native-chat launch parity oracle now models the host's environment after startup, and records that no agent's child carries the parent pane's keys.
The seal that names a native-chat child stripped the pane identity and hook route only for agents that asked (ACP and Pi). Claude and Codex asked for nothing, so a pane key reaching their environment from any source other than the host's own (a settings env overlay or a shell profile) still reached the child and its hooks. The seal now always strips them, for every agent and from the composed environment as well as the inherited one, keeping only the session's own identity and a registered worker's handle. The key list is shared with the host startup drop, and ACP and Pi no longer carry their own copies. The pane's workspace hints and terminal handle join the list, so an ordinary chat no longer carries a pane's handle either.
a7a64f3 to
1766ed5
Compare
Windows reads a child's environment case-insensitively, so an orca_pane_key from a settings overlay or shell snapshot still named a pane after the seal and the final provider merge deleted only the exact uppercase names. One case-aware delete now backs the seal and both final merges.
The final provider merges delete a caller's own keys too, so deleting those in any casing on Windows also removed deliberate lowercase overrides (an OpenCode config dir, say). Only the seal needs it, for the pane and hook keys an overlay supplies; the inherited host env is already clean in every casing after startup.
|
Review loop status: converged at e4be9cc, synced with main once, ready for a human look (still draft, not merged). What this PR does: an Orca started from inside an Orca terminal no longer passes that terminal's identity (its pane key, tab, workspace, launch token and hook route) to the native chat agents it starts, so a chat can't be mistaken for that terminal and saved as its conversation (the cause of #26417). The fix that repairs terminals that already saved a chat's conversation is #28288, stacked on this one. Reviewed in three rounds plus an independent design challenge, which kept this design unchanged. A live check on a remote Mac confirmed a chat started from a terminal-launched Orca no longer carries the terminal's identity. Synced with main at the end of review (e4be9cc). While this PR was in review, main renamed the chat's visuals-folder variable to a scratch-folder variable (#28389). The startup cleanup now removes both the new and the old name, and Pi keeps main's own scratch-folder removal while leaving the pane keys to the shared chat seal. Nothing on main made this fix redundant or contradicts it. A fresh independent review of the merge found no defects. Verified at e4be9cc: node typecheck; the targeted tests (startup cleanup, chat seal, launch parity snapshot, Pi, ACP, headless server); lint and code-quality checks. With the startup cleanup disabled, its two tests fail. CI: the only failing check is static analysis, on Not verified on real Windows/Linux machines (Windows casing is covered by tests). |
Main renamed the chat visuals folder variable to ORCA_CHAT_SCRATCH_DIR: the startup drop now removes it and the legacy name. Pi keeps main's scratch-folder strip; the seal already covers the pane keys and handle Pi listed.
ELI5
Every Orca terminal tells the programs inside it "you are pane X". If you start Orca itself from one of those terminals (a dev build,
orca serve, or the app run from a pane), the new Orca wrongly believed it was pane X too, and passed that on to every chat agent it started. Those agents then reported their conversations as pane X's, and after a restart pane X "resumed" a chat's conversation as a terminal agent in the wrong workspace. Now Orca forgets the launching pane's identity when it starts, and no chat agent can carry a pane's identity whatever its environment says.This PR stops new mix-ups. Panes that already saved a chat's conversation before this fix are handled by the stacked follow-up #28288 (a terminal never auto-resumes a conversation a chat owns).
What Changed
What the user saw (#26417). A background Claude chat worker started in worktree A came back after an Orca restart as a
claude --resume <id>CLI tab in a floating terminal, with the home directory as its cwd, and did so again on every restart. Orca's own session record still placed the chat in worktree A.Why it happened. The Orca that started the worker had been launched from a shell inside that floating pane, so its environment held the pane's
ORCA_PANE_KEY,ORCA_TAB_ID,ORCA_WORKTREE_ID, launch token and hook route. Native-chat Claude and Codex children inherit the app's environment, and the shared step that names a chat's child only removed a pane's identity for the agents that asked (ACP and Pi). The launch refactor in #27204 deliberately kept that behaviour unchanged because it was a parity-only refactor. So the chat's hooks reported its conversation under the floating pane, the pane saved it in its restore record, and the pane's normal cold restore resumed it in the pane's own workspace.After.
orcaCLI inside a pane, hooks and WSL panes are unchanged: each pane still gets its own identity explicitly when it is spawned.Mechanism.
sealStructuredSessionChild) now always strips the same pane identity and hook route from the composed environment, instead of only when an agent opts in. ACP's and Pi's own copies of that list are removed. One pane/hook key list is shared by both steps.orca_pane_keyfrom a settings override or shell profile still names a pane. The seal now drops these keys in any casing on Windows. Only the seal does it, and only for the pane and hook keys: the final step that builds a provider child's environment also deletes a caller's own keys, which must keep their exact spelling, and the inherited host environment is already clean in every casing after startup.Why
Supersedes #26426 by @petersindex, which found the same leak and stripped it in the chat child's environment. Thank you. That PR edits files #27204 has since replaced and covers only chat children; this one fixes the host boundary and makes the chat seal strip the identity for every agent.
Known limits. Background checks that list an agent's models or read its CLI version don't pass through the chat seal: they get the host's cleaned environment, but a pane key a shell profile or settings override adds after startup still reaches them. No effect on saved sessions was found; a follow-up can extend the same strip to them. A remote paired host is fixed once it updates (the headless server drops the identity at its own start). An older Orca keeps leaking until it is updated. No wire or stored format changes. Panes that saved a chat's conversation before this fix keep resuming it until #28288 lands.
Linked Issue
Part of #26417 (#28288, stacked on this PR, closes it).
Visual Proof
Pending: restart repro on a remote QA machine (Orca launched from an Orca pane, structured worker started, restart). Will attach.
Testing
inherited-launch-identity-env.test.ts,configure-process.test.ts: the host drops every key and keeps unrelated ones.structured-session-child-env.test.ts: a chat or worker of an agent that asks for no deletions still loses a pane key, workspace hint and hook route supplied by its environment; a worker keeps its handle. On Windows, lower- and mixed-case pane keys, hook routes and handles from an overlay are gone after the final provider merge, while a deliberate lowercase override that is not identity (an OpenCode config dir) stays.structured-agent-launch-parity.test.ts: the oracle of what every native-chat agent's child receives now models the host's environment after startup. The snapshot change is removals from the children's env (no Claude, Codex, ACP or Pi child carries the parent pane's keys, terminal handle, hook route or scratch folder; a worker keeps its own handle) plus the seal's now-uniform delete list.Ablation: with the host drop and the seal strip disabled, the identity tests and the affected parity cases fail; with the seal's delete made case-sensitive, the Windows test fails.
Existing suites
src/main/startup,src/main/acp,src/main/pi,src/main/claude,src/main/codex, parity: pass.pnpm tc:nodeand the repo's static checks pass.I manually tested these changes locally
Automated tests added/updated, or explained why not below
Review
Agent skill upstream boundary
Notes
Cross-platform: plain environment deletes; Windows
process.envis case-insensitive. SSH/remote: the headless server drops the identity at its own start. Folder workspaces: the folder hints are in the list. Performance: one pass over a short key list at startup and per chat launch. Backwards compatibility: no stored or wire format changes.X: @BrennanKB5