fix(subagent): pin delegated child approvals to 'never' within the inherited sandbox scope
A delegated in-process child now acts only within the sandbox scope fixed at delegation: captureDelegatedPolicyOverrides still snapshots the parent's explicit sandbox override but pins the child approval policy to 'never' (instead of inheriting the parent's), so every child ask — sandbox_permissions escalations included — is rejected deterministically by ApprovalService before any answerer, with the audit pair still logged. Every in-process child additionally receives the scoped subagent:delegation runtime-context statement telling it to report a scope limitation instead of retrying. Supersedes the approval half of the policy-inheritance decision (new Agent Note cross-linked from both prior notes and the approval-seam Q&A); refreshed child snapshot fixtures carry the pinned event, and subagent-published-run-failure now persists a one-event child log.
This commit is contained in:
@@ -52,9 +52,9 @@ The seam owns the depth vocabulary shared by Service providers and Consumers: th
|
||||
|
||||
`inheritsParentContext` is descriptive rather than enforceable. It says only whether the child sees completed parent conversation history (`fork` does; `spawn` and the out-of-process one-shot providers do not), not whether it inherits tools, services, or authority.
|
||||
|
||||
## Delegated policy inheritance
|
||||
## Delegated policy
|
||||
|
||||
Both in-process delegation paths seed the parent's explicit policy overrides into the child through the shared child-agent helpers: `captureDelegatedPolicyOverrides(parent)` snapshots `sandboxPolicy.overrideOf()` and `approval.overrideOf()` synchronously at the delegation boundary (both services are optional `ctx.get` consumers), and `appendDelegatedPolicyOverrides()` writes each captured value onto the child's own log as a `source: 'delegation'` `sandbox/mode` or `approval/policy` event during unpublished setup, after any fork seed — so fresh policy wins stale seed state, a later child switch wins the snapshot, and the child's effective policy stays reconstructable from its log alone. Deployment defaults are never copied: an unswitched parent stamps nothing and its child follows the deployment default dynamically. A continuable start captures before its first await and seeds only fresh materialization; a cold resume replays the persisted delegation events instead of re-capturing the parent, so a parent switch after creation never retroactively changes a durable child. See the [one-shot](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md) and [continuable](../../../.agents/notes/implemented/feature/2026-08-10-continuable-subagent-policy-inheritance.md) policy-inheritance Agent Notes.
|
||||
Both in-process delegation paths fix the child's permission scope at the delegation boundary through the shared child-agent helpers. `captureDelegatedPolicyOverrides(parent)` snapshots the parent session's explicit sandbox override (`sandboxPolicy.overrideOf()`) and pins the child's approval policy to `'never'` whenever the approval capability is composed — regardless of the parent's own policy — so a delegated child acts only within its inherited sandbox scope and every ask (for example a `sandbox_permissions` escalation) is rejected deterministically instead of waiting on a prompt no one is watching (both services are optional `ctx.get` consumers). `appendDelegatedPolicyOverrides()` writes each value onto the child's own log as a `source: 'delegation'` `sandbox/mode` or `approval/policy` event during unpublished setup, after any fork seed — so fresh policy wins stale seed state and the child's effective policy stays reconstructable from its log alone. The sandbox deployment default is never copied: an unswitched parent stamps no `sandbox/mode` and its child follows the deployment default dynamically. A continuable start captures before its first await and seeds only fresh materialization; a cold resume replays the persisted delegation events instead of re-capturing the parent, so a parent switch after creation never retroactively changes a durable child. Every in-process child also receives a scoped runtime-context statement (`subagent:delegation`) telling it the scope is fixed and that a task needing wider access ends with a reported limitation, not retries. See the [one-shot](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md) and [continuable](../../../.agents/notes/implemented/feature/2026-08-10-continuable-subagent-policy-inheritance.md) delegation-policy Agent Notes.
|
||||
|
||||
## One-shot ownership and lifecycle
|
||||
|
||||
@@ -96,11 +96,25 @@ Continuable Activations await a best-effort final session flush without treating
|
||||
|
||||
## Model Experience
|
||||
|
||||
Indirectly, through `dsh-tool-subagent`, `dsh-tool-subagent-control`, and `dsh-tool-subagent-report`. The first owns delegation schemas, the second owns parent continuation and discovery, and the third contributes `report` only to continuable child scopes.
|
||||
### Child delegation-scope statement
|
||||
|
||||
#### What the model sees
|
||||
|
||||
Every in-process child's runtime-context snapshot carries the `subagent:delegation` statement below, after the sandbox-policy and approval-policy sentences; parent-side rendering stays with `dsh-tool-subagent` (delegation schemas), `dsh-tool-subagent-control` (continuation and discovery), and `dsh-tool-subagent-report` (the child-scoped `report`).
|
||||
|
||||
##### The delegation-scope statement
|
||||
|
||||
```markdown
|
||||
You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it.
|
||||
```
|
||||
|
||||
#### Token effect
|
||||
|
||||
One fixed statement in each child's runtime-context snapshot; none in the parent's requests.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
No direct invalidation; the named consumers own any request-prefix changes.
|
||||
Prefix-stable within a child: the statement never changes during the child's lifetime, so it is written once into the first runtime-context snapshot. Parent-side, no direct invalidation; the named tool consumers own any request-prefix changes.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
|
||||
Reference in New Issue
Block a user