Repository navigation
fix(agent-status): a nested claude -p no longer takes over its parent pane's status - #31780
Conversation
…t pane The pane-ownership guard told a nested agent apart from the pane's owner by provider only, so a second Claude process (a script's claude -p) wrote its session, prompt and failed turn into the parent's row. A hook whose process identity differs from the owner's live process is now a guest on every route: the local listener, the host status update, the owner probe and the relay.
|
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
|
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:
24de30cd-ba7d-4542-ad93-d8c9d2dc86e7
📒 Files selected for processing: 11
src/main/agent-hooks/server-agent-presence.test.tssrc/main/agent-hooks/server-claude-owed-notification-expiry.test.tssrc/main/agent-hooks/server-nested-same-provider-owner.test.tssrc/main/agent-hooks/server/server-status-update.tssrc/shared/agent-hook-listener.tssrc/shared/agent-hook-owner-presence.tssrc/shared/agent-process-presence.tssrc/shared/agent-status-identity.tssrc/wsl-guest/agent-hook-event-admission.tssrc/wsl-guest/agent-hook-provider-normalization.tssrc/wsl-guest/relay-nested-same-provider-owner.test.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.
What users saw
An interactive Claude runs in an Orca pane, and a script in that pane starts its own
claude -p. Each time that child finished, or hit its quota and fired StopFailure, the parent pane's status row became the child's: its session id, prompt, and adone/failed state. Desktop showed " - Claude failed" for the parent pane, and the paired phone got one push per child retry. The parent's own session, prompt, pending permission and subagent roster were overwritten too.Now
The parent pane keeps its own session, prompt and state while a nested
claude -pruns and fails. The child's hooks leave the row alone, so no banner or push fires for it. A new Claude started in the pane after the old one is gone (exited or crashed) still takes the pane over, because the owner's process is checked first.Root cause
hasOtherAgentProcessOwner(src/shared/agent-process-presence.ts) decided whether a hook comes from someone other than the pane's owning process by comparing provider type only.resolveAgentStatusIdentityalso skipped that check for the same provider, so a second Claude with a different PID and start time counted as the owner. Every route asks that one function: the local listener's pre-fold gate, the host status update, the owner probe before admission, and the SSH/WSL relay's normalization and admission. Now it takes the sender's process too: while the owner's process is live and not suspended, a different process of the same provider is a guest.On the relay, the owner's per-provider prompt/tool folds are shared with a same-provider guest, so the relay now refuses such a guest before folding (
isOwnerProviderGuestHook). Without that, the parent's next Stop over SSH showed the child's prompt.Hooks with no process identity (Windows hooks and hooks installed by older builds) keep today's behaviour: they can only be told apart by provider. A nested
claude -pthere still updates the parent's row. This change never suppresses an owner's own events on a guess. Relayed rows on the desktop keep adopting the relay's verdict, and the phone only shows what the host decided, so old and new phones behave the same.@Jinwoo-H, for #26408: when its
transitionHookPresenceclassifier rebases, it should keep this rule. A different PID/start time of the same provider is not the owner, so "same agent type → owner" must come after the process check.Verified
src/main/agent-hooks/server-nested-same-provider-owner.test.ts(local hook server, real HTTP hooks): a nested Claude's UserPromptSubmit + StopFailure leaves the parent row working with its own prompt and session, and nothing emitsdone. This fails on main. A new Claude is admitted once the owner probe says the old process exited.src/wsl-guest/relay-nested-same-provider-owner.test.ts(real relay hook server): nothing is forwarded for the child, the parent's next Stop keeps "Parent task", and a replacement is admitted after the owner exits. This fails on main, and it also fails when the relay pre-fold check is removed.server-agent-presencetests: a replacement Claude's hook now waits for the owner probe before it is admitted, as a different provider's hook already did.check:code-quality:changedandcheck:react-doctor:changed.Visual Proof
This is a real run in hidden dev builds of main and this branch. An interactive Claude in a pane runs
claude -p "CHILD TASK: reply with the word hi" ; sleep 45with its Bash tool. Each caption shows the pane's status row as the host published it.Before (main): the child's hooks take the row. The session changes to the child's (
7bd3ee97), the prompt becomes "CHILD TASK…", and the row goes Done (green check) while the parent is still working. After the parent resumes, its row keeps the child's prompt.After (this PR): the row stays the parent's: working, with its own prompt and session (
f6f8e1dc), until the parent finishes. The child ran (its session40c90826exists on disk) but never reached the row.Full runs: before · after
Fixes #30067