subagentStart / subagentComplete SidecarEvents with parentToolUseId set — but the codepath that gets there is engine-specific.
The nesting model
- Claude SDK
- Pi
Native Task / Agent tool
Claude SDK ships withAgent/Task as a built-in tool. When the parent LLM calls it:- SDK spawns a sub-query with isolated context.
parentToolUseIdis set automatically to thetool_use_idof the spawningTaskcall.- Events stream through the same normalization layer with the parent id attached.
D9 body filter
When an agent is dispatched as a subagent, the body sent to the model isAGENTS.md only. SOUL.md (persona) and MEMORY.md (seed memory) are dropped because they pollute when an agent runs as another’s helper. The filter is enforced via getAgentDefForSubagent (src-tauri/sidecar/agents/loader.mjs).D9 sub-rule (added 2026-05-05): Any tool whose return value flows back into another model’s context (e.g.
agent_info) MUST also use getAgentDefForSubagent, not getAgentDef. Otherwise the discovery result leaks SOUL+MEMORY into the downstream subagent. Verified leak path was fixed at commit d2a26640. ✓ VERIFIED. See agents-as-first-class-entities.md D9 invariant.Three nesting modes (Pi only)
spawn_subagent accepts three dispatch shapes:
| Mode | Behavior |
|---|---|
single | One subagent, one result. Most common. |
chain | Sequential — output of subagent N feeds into prompt of subagent N+1. |
parallel | All subagents dispatched concurrently; results collected after all complete. |
chain / parallel — those compositions must be expressed by the parent LLM itself (multiple Task calls).
Subagent vs spawn_subagent — the same contract
Both produce the same SidecarEvent shape:ActivityStreamV3 and SubAgentPanel components consume these the same way regardless of engine.
Safety gaps in subagent dispatch
GAP-A3:
parentToolUseId asymmetry. Claude SDK sets it automatically; Pi sets it manually inside pi-subagent-runner.mjs. Equivalent in practice, but if the Pi code regresses, subagent events orphan in the frontend (rendered at top level, not nested). Defensible against regression tests.Key files
src-tauri/sidecar/engine/pi-subagent-runner.mjs
src-tauri/sidecar/engine/pi-subagent-runner.mjs
Pi’s
spawn_subagent implementation. Depth guard _depthMap, model fallback loop, parentToolUseId propagation, JSONL writer wire-up.src-tauri/sidecar/agents/loader.mjs
src-tauri/sidecar/agents/loader.mjs
resolveAgentLayered({builtinDir, vaultDir}) resolves agents from bundle + vault overrides. getAgentDefForSubagent is the D9-filtered variant — drops SOUL+MEMORY.src-tauri/sidecar/agents/initLoader.mjs
src-tauri/sidecar/agents/initLoader.mjs
Boot reconcile + auth-change reconcile. DB index of agents (derived index, D7 invariant — not state-of-record).
src-tauri/sidecar/agents/claude-agents-map.mjs
src-tauri/sidecar/agents/claude-agents-map.mjs
Programmatic agents map for Claude SDK
query({agents}).Next
MCP, Permissions & Sandbox
Subagents inherit tool sets from their parent runtime — which means inheriting the parent’s permission posture. Read this next.