Skip to main content

Module subagents

Module subagents 

Source
Expand description

P5-3 (COMPOSABLE-HARNESS-DESIGN.md §2 module 9 subagents: “D1 spawn tool; D3 sub-agents/named-defs/background+resume/teams; D5 subagent transcripts”; §2.1 D-1 “subagents → core.session(lineage), core.tools; background-mode → permissions.approvals”; §2.2 C6): the data shapes and pure-function resource-bound checks the spawn/join/background machinery in crate::agent::Agent builds on. Kept separate from agent.rs so the depth/concurrency-cap arithmetic and the lineage record shape are unit-testable without a full Agent/mock-Provider harness — the same “pure config → set, testable without the loop” precedent P3’s crate::modules module documents for itself.

Activation. Everything here is inert until Agent actually consults it, which only happens when Config::subagents_enabled is true (capabilities.subagents.enabled, default false) — so importing this module changes nothing for an agent that never turns the module on.

Structs§

ConcurrencyGuard
A held slot against a try_acquire concurrency gauge. Decrements the gauge on drop (including an early return, a panic-unwind, or the normal end of a background task’s future) so a completed spawn always releases its slot — no separate “remember to release” call site to forget.
NamedAgentDefinition
A named subagent type ([capabilities.subagents.agents.<name>], §3.1) — the CC “subagent definition” shape: its own system prompt, an optionally NARROWED tool set (a spawned child’s tool surface is always the intersection of the parent’s already-enabled tools and this list — see Agent::run_spawn_subagent’s doc comment for why it can only narrow, never widen), and an optional model override.
ParentQueueApprovalHandler
§2.2 C6 background_prompts = "parent"’s crate::permissions::PermissionsApprovalHandler: pushes every Ask-tier request onto the parent’s queue (crate::agent::Agent::pending_child_approvals) and returns crate::permissions::ApprovalOutcome::Deny immediately — NEVER blocks, since a detached background child has no way to wait for an answer that can’t arrive synchronously (the hard C6 requirement this whole policy exists to satisfy). “Surfaced to the parent” means exactly that: recorded for the parent to inspect/audit, not a live prompt the parent’s later answer retroactively changes.
QueuedApproval
A queued approval request from a background_prompts = "parent" child, surfaced via Agent::pending_child_approvals (§2.2 C6 “parent-surfaced queue”). This struct is ALWAYS a record for the parent to inspect/audit (never a pending decision the parent’s answer changes retroactively) — but what actually answers the underlying call depends on which PermissionsApprovalHandler Agent::run_spawn_subagent installed for the child:
SubagentLineage
Typed, lossless NATIVE-WRITE lineage record for a spawned child (§1.13; §5.2 P5 row 3: “native write side — store already parses CC sidechains + CX lineage on import”). Field names deliberately mirror the keys the IMPORT-side loaders already populate on crate::session::SessionMeta::parent_tool_use_id/ crate::session::SessionMeta::lineage (see Self::to_lineage_map), so a natively-spawned session and an imported CC/CX one land in the same shape rather than two parallel formats a translator would need to know about separately.

Enums§

BackgroundPromptsPolicy
capabilities.subagents.background_prompts (§2.2 C6’s schema value, §3.1): how a BACKGROUND child’s tool-approval Ask decisions are resolved, since a detached background task cannot block on an interactive prompt it has no way to answer.

Functions§

check_depth
Fail-closed depth check (resource bound, build-brief “a parent spawning children spawning children… must not fork-bomb”): Err NAMES the exceeded cap rather than silently clamping the depth or panicking. current_depth is the SPAWNING agent’s own depth (0 for a top-level agent); the new child would be spawned at current_depth + 1.
try_acquire
Fail-closed concurrency check-and-acquire (resource bound: “a max concurrent subagents… cap, fail-closed”). Atomic compare-exchange loop (not a check-then-increment race, which would let two racing spawns both pass a check against the same stale count) — None when max_concurrent subagents are already in flight anywhere in this spawn tree (the gauge is a single Arc shared root-to-leaf, per crate::agent::Agent’s doc comment on its own concurrency-gauge field), Some(guard) otherwise, with the slot already counted.