Repository navigation
fix(agent-hooks): restore scrubbed pane identity in the claude, codex, grok and antigravity hooks - #31834
fix(agent-hooks): restore scrubbed pane identity in the claude, codex, grok and antigravity hooks#31834daviddesmet wants to merge 1 commit into
Conversation
…, grok and antigravity hooks A harness that drops env vars whose name contains KEY, TOKEN, SECRET or PASSWORD takes ORCA_PANE_KEY with it, and the managed hook then exits at its own guard without posting. ORCA_AGENT_HOOK_TOKEN recovers by sourcing the endpoint file; ORCA_PANE_KEY has no recovery, and the spool fallback returns early on the same missing key, so the event is lost with no trace. Without the hook Orca never sees the provider session id, so no sleeping record is filed and cold restore has nothing to resume — the pane comes back at a bare shell. Lift the dsh restore into shared builders beside the alias definitions and use them in the four remaining managed scripts, POSIX and Windows. dsh now calls the shared builders and emits byte-identical output.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to
|
#33286) Claude's hook script refills ORCA_PANE_KEY / ORCA_AGENT_LAUNCH_TOKEN from the scrub-safe aliases when CLAUDE_CODE_SUBPROCESS_ENV_SCRUB strips them, so scrubbed Claude panes report status and resume again. Fixes #31832. Supersedes #31834. Co-authored-by: David De Smet <2607383+daviddesmet@users.noreply.github.com>
|
Thanks @daviddesmet — you found this one and the alias-restore approach. It landed via #33286 (credited with Co-authored-by). We kept the restore Claude-only: Codex and Grok hooks don't scrub the env ( |
ELI5
Some agent harnesses refuse to hand a hook any environment variable whose name looks like a password; anything with
KEY,TOKEN,SECRETorPASSWORDin it. One of the things Orca puts in there is the name of the pane the hook is reporting for. When that gets taken away, the hook has no idea which pane it belongs to, so it gives up and says nothing. Orca then never learns the agent's session id, and when you quit and reopen the app your panes come back empty instead of picking the conversation back up.Orca already keeps a spare copy of that pane name under a safe-looking name for exactly this reason, but only the DSH hook knew to look for it. This teaches the Claude, Codex, Grok and Antigravity hooks to look for it too.
What Changed
Before: with a scrubbing harness, agent panes restore to a bare shell after an app restart.
Status badges still look right the whole time, so nothing points at the hooks.
After: the same panes restore with
claude --resume <session-id>again, with the harness's credential scrub left switched on.Mechanism: every managed POSIX hook ends in this guard, and a scrubbed
ORCA_PANE_KEYtrips it:ORCA_AGENT_HOOK_TOKENrecovers on its own; the line above sourcesORCA_AGENT_HOOK_ENDPOINTand gets the live port/token back.ORCA_PANE_KEYhas no recovery path, and the spool fallback cannot catch the event either, becausespool_hook_eventreturns early on the same missing pane key. The event is dropped with no file, no log line and no retry.That silence is what makes the bug hard to place: OSC 9999 keeps the pane's status badges accurate, so only the hook-derived facts go missing;
providerSessionnever lands on the status row, no sleeping record is filed,buildColdRestoreAgentResumeStartup()finds neither a live row nor a sleeping record and returnsnull, and the replacement spawn gets no startup command (daemon.log:"event":"startup-command-delivery" ... "hasCommand":false).This PR lifts the existing
dshrestore into two shared builders next to the alias definitions inagent-hook-scrub-safe-env.ts;buildPosixScrubSafeEnvRestoreLinesandbuildWindowsScrubSafeEnvRestoreLines, and uses them in theclaude,codex,grokandantigravitymanaged scripts on both POSIX and Windows.dshnow calls the shared builders and emits byte-identical output.In the Claude script the restore sits immediately after the neutral
{}that permission hooks need on stdout (#14818), and before anything that reads pane identity.Why
Orca already solved this for DSH:
applyScrubSafeAgentEnvAliases()mirrorsORCA_PANE_KEYandORCA_AGENT_LAUNCH_TOKENontoORCA_AGENT_PANE/ORCA_AGENT_LAUNCHat spawn, and the DSH managed script restores the canonical names from them. The aliases are already being set for every pane; only the consuming half was missing from the other four hooks. Reusing that machinery beats the alternatives:The restore is default-assignment (
: "${ORCA_PANE_KEY:=${ORCA_AGENT_PANE:-}}"), never an overwrite, so an unscrubbed host keeps the value it already had and every line below is unchanged.This is not DSH-specific any more. Claude Code performs the same scrub when
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1is set in its settings, which is how the linked issue was found.Linked Issue
Fixes #31832
Visual Proof
N/A— no UI or interaction surface changes. The change is confined to the text of generated hook scripts; the user-visible difference is that a restored pane runs its resume command again, which the Testing section covers.Testing
Platforms actually tested: macOS 26.0 (arm64), local worktrees. Windows and Linux changes are covered by the generated-script tests below, not by hand.
Reproduction and verification, with Claude Code 2.1.307 and
"env": { "CLAUDE_CODE_SUBPROCESS_ENV_SCRUB": "1" }:envfrom a hook subprocess. Stripped:ORCA_PANE_KEY,ORCA_AGENT_HOOK_TOKEN,ORCA_AGENT_LAUNCH_TOKEN. Surviving:ORCA_AGENT_PANE,ORCA_TAB_ID,ORCA_WORKTREE_ID,ORCA_AGENT_HOOK_ENDPOINT/_PORT/_ENV/_VERSION/_TRANSPORT.claude-hook.shby hand with the full env produced a complete status row withproviderSession, so ingest was never the problem. A realclaude -prun under the same env delivered nothing and created no spool file. Atouchhook in the sameStopslot did fire, so hooks were running; Orca's specifically was not.claude -prun now lands a row with bothproviderSessionandlaunchTokenHash.0, the pane key arrives and stock Orca works; consistent with this only reproducing on machines that opt into the scrub.Checks run locally on this branch:
pnpm tc: no type errors across all four projects (17,438 / 3,097 / 34 / 15,521 files)pnpm test src/main/claude src/main/codex src/main/grok src/main/antigravity src/main/dsh src/main/agent-hooks src/shared/agent-hook: 697 files, 7,003 tests passingoxlintclean on every changed filepnpm buildnot run locally; leaving that to CII manually tested these changes locally
Automated tests added/updated, or explained why not below
Added
src/main/agent-hooks/managed-hook-scrub-safe-restore.test.ts: for all four POSIX managed scripts it asserts the restore lines are present and ordered before both the missing-pane-key guard and the spool fallback that reads the same variable. Ordering is the part that actually regresses; a restore placed after either one is still dead code; so the test pins the order rather than just the presence of the lines.AI Disclosure
Review
.cmdwrapper and the Windows Grok script. The Windows form is the sameif not definedidiom already shipping in the DSH script. No path, shell or platform assumptions added.posixtarget used for remote installs is the same code path the tests cover.:=only fills an empty value, so existing env wins and output is unchanged.dshoutput is byte-identical to before this PR. Agents without a managed script are untouched.ORCA_AGENT_HOOK_TOKENis deliberately left to the existing endpoint-file path rather than aliased.Agent skill upstream boundary
Notes
The bug reproduces only on machines that opt into a scrubbing harness, which is why it looks machine-specific: the same Orca version resumes fine on a second Mac without
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB. Worth knowing when triaging similar "resume stopped working on one machine" reports.Author: X — @daviddsmet
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)