subagent: inherit parent sandbox/approval overrides in in-process children
Per-session policy overrides (sandbox/mode, approval/policy) never crossed the delegation boundary: a spawn child of a read-only-switched parent ran under the wider deployment default, and a fork child missed any switch made after its seed boundary — delegation was a bypass channel for a user's tightening. The in-process driver now snapshots the delegating parent's override chain and stamps it onto the child through the canonical write paths (SandboxPolicyService.inheritOverride / ApprovalService.inheritOverride), anchored inside the child's first turn via a one-shot agent/prompt-submit listener: turn-enclosed (durable), ahead of the first request (an inherited 'never' reaches the child's first system prompt), and positioned after any stale fork-seed switch so the ordinary last-event-wins fold resolves it. Only overrides are copied — an unswitched parent stamps nothing and the child follows the live deployment default; both services are consumed opportunistically, so compositions without them delegate unchanged. Nesting composes by construction (each stamp folds the already-stamped parent log). Evidence: inheritance.spec.ts drives scripted-model children into the real dsh-fs-sandbox fence through the real write tool (disk-state + denial-marker assertions; spawn, stale-seed fork, grandchild, escalation fail-closed, and no-stamp guards), inheritOverride contract tests in both service suites, and the recorded subagent-sandbox-inheritance ACP snapshot (read-only preset → delegate → child denied, replayed keylessly). See .agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md.
This commit is contained in:
@@ -6,7 +6,7 @@ Each request must belong to an open agent turn. The service appends a paired `ap
|
||||
|
||||
Answerers are `approval/request` waterfall listeners. Return an outcome to answer for an owned agent or call `next()` to delegate. Agent-scoped listeners receive only that agent's requests; compose one terminal answerer per deployment because sibling listener order is not a policy priority mechanism. The ACP bridge is the shipped human answerer.
|
||||
|
||||
`ApprovalPolicy` is `'ask'` or `'never'`. The effective value is the last `approval/policy` event, falling back to config; `setApprovalPolicy()` is the write path. `'never'` rejects before interactive dispatch and is the only policy stated in the prompt. Switches produce at most one coalesced notice, attributed to the user when the override follows the last `request/header` and to operator/config otherwise.
|
||||
`ApprovalPolicy` is `'ask'` or `'never'`. The effective value is the last `approval/policy` event, falling back to config; `setApprovalPolicy()` is the write path. `'never'` rejects before interactive dispatch and is the only policy stated in the prompt. Switches produce at most one coalesced notice, attributed to the user when the override follows the last `request/header` and to operator/config otherwise. `ctx.approval.inheritOverride(parent, child)` stamps a parent session's override (never the configured default) onto a child session through that write path — the in-process subagent driver calls it inside the child's first turn so a `'never'` parent cannot mint prompting children ([rationale](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)).
|
||||
|
||||
The tools pipeline routes `ask` decisions through this seam and fails closed when it is absent; the sandboxed bash tool also uses it for escalated retries. The ACP bridge is the shipped human answerer for calls it owns. Audit events remain log-only, so the model sees only the asking consumer's result. See the [approval-seam Agent Note](../../../.agents/notes/implemented/feature/2026-07-06-approval-seam.md) and [sandbox Agent Note](../../../.agents/notes/implemented/feature/2026-07-06-sandbox.md).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user