fix(subagent): inherit parent policy overrides in continuable children
A continuable background child (the default backgroundMode for both delegation tools) never received the parent session's explicit sandbox/approval overrides: materialization applied only child composition, so a danger-full-access parent produced workspace-write children whose every out-of-workspace operation raised an approval prompt. Move the one-shot driver's capture/append pair into the shared child-agent module (captureDelegatedPolicyOverrides / appendDelegatedPolicyOverrides) and call it from both paths: startContinuable captures before its first await, only fresh materialization appends the source-tagged events (after any fork seed), and a cold resume replays the persisted delegation events instead of re-capturing the parent. Adds the continuable inheritance unit suite, the ACP snapshot scenario subagent-continuable-inheritance (fails without the fix), the continuable policy-inheritance Agent Note, and the seam-level README contract, with bilingual counterparts. Fixes #1692
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/subagent/subagent-inprocess/README.md
|
||||
README.md: 67f0cf5dd1ecb18542af56953a0eaa40988aca0d
|
||||
README.zh.md: 648a160be5f1c3dcbe66a867a273a3df610dbc0a
|
||||
README.md: 4189979806ccd4e7dcfee231ab3e9e2331550b0d
|
||||
README.zh.md: c6a9005cbfdb9d40a54383f921671fa22a31dc32
|
||||
|
||||
@@ -20,7 +20,7 @@ The child gets the parent's working-directory/session lineage and inherits the p
|
||||
|
||||
This result boundary is valid because the provider owns an isolated child lifecycle from publication through quiescence. Steering submitted during that lifecycle belongs to the child run; the provider does not pretend the initial follow-up alone owns its output.
|
||||
|
||||
When the optional sandbox-policy or approval service is composed, the driver snapshots the parent's explicit session override before child creation and appends a source-tagged event during unpublished setup, after any fork history and before session publication. It never copies deployment defaults or one-shot grants; later child switches still win. See the [policy-inheritance decision](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md).
|
||||
The driver applies the seam's [delegated policy inheritance](../subagent/README.md#delegated-policy-inheritance) through the shared child-agent helpers: it captures the parent's explicit sandbox/approval overrides before child creation and appends the source-tagged events during unpublished setup, after any fork history and before session publication. See the [policy-inheritance decision](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md).
|
||||
|
||||
## Cancellation and ownership
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
该结果边界成立,是因为提供方拥有从发布到完全停稳的隔离子 agent 生命周期。在该生命周期内提交的 steering(中途引导)属于子运行;提供方不会声称输出只归初始 follow-up 所有。
|
||||
|
||||
当组合中挂载了可选的沙箱策略或审批服务时,驱动器会在创建子 agent 前对父级的显式会话覆盖项获取快照,并在未发布的设置阶段追加一条带来源标记的事件,使其位于所有 fork 历史之后、会话发布之前。它绝不复制部署默认值或一次性授权;子 agent 后续的切换仍然优先。参见[策略继承决策](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)。
|
||||
驱动器通过共享的子 agent 辅助函数应用该 seam 的[委派策略继承](../subagent/README.md#delegated-policy-inheritance):它会在创建子 agent 前捕获父级的显式沙箱/审批覆盖项,并在未发布的设置阶段追加带来源标记的事件,使其位于所有 fork 历史之后、会话发布之前。参见[策略继承决策](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)。
|
||||
|
||||
## 取消与所有权
|
||||
|
||||
|
||||
@@ -17,8 +17,10 @@ import type { Agent, AgentHandle } from '@deepseek-ai/dsh-agent'
|
||||
import { findLastMessageTurnEnd, SessionId, type SessionEvent, type TurnEndReason } from '@deepseek-ai/dsh-session'
|
||||
import { createUserMessage, type ContentBlock } from '@deepseek-ai/dsh-llm'
|
||||
import {
|
||||
appendDelegatedPolicyOverrides,
|
||||
applyChildComposition,
|
||||
assertSubagentMaxDepth,
|
||||
captureDelegatedPolicyOverrides,
|
||||
childSessionMeta,
|
||||
resolveChildAgentOptions,
|
||||
resolveChildDepth,
|
||||
@@ -30,11 +32,6 @@ import type {
|
||||
SubagentRun,
|
||||
SubagentStopReason,
|
||||
} from '@deepseek-ai/dsh-subagent'
|
||||
// Type-only: make `ctx.get('sandboxPolicy')` / `ctx.get('approval')` resolve
|
||||
// to the policy services when composed — the driver consumes both
|
||||
// opportunistically (the documented `ctx.get` pattern), never as a hard dep.
|
||||
import type {} from '@deepseek-ai/dsh-sandbox-policy'
|
||||
import type {} from '@deepseek-ai/dsh-user-approval'
|
||||
import {
|
||||
attachStructuredRuntime,
|
||||
type StructuredAttachment,
|
||||
@@ -111,20 +108,11 @@ export async function startInProcessRun(
|
||||
|
||||
// Capture before the first await: a later parent switch belongs to the
|
||||
// parent's future.
|
||||
const inheritedMode = parent.ctx.get('sandboxPolicy')?.overrideOf(parent.session)
|
||||
const inheritedPolicy = parent.ctx.get('approval')?.overrideOf(parent.session)
|
||||
const inherited = captureDelegatedPolicyOverrides(parent)
|
||||
|
||||
let structured: StructuredAttachment | undefined
|
||||
const setup = (childCtx: Context): void => {
|
||||
// Inherited overrides land on the child's own log, so its effective policy
|
||||
// is reconstructable from that log alone.
|
||||
const childSession = (childCtx.agent as Agent).session
|
||||
if (inheritedMode !== undefined) {
|
||||
childSession.append('sandbox/mode', { mode: inheritedMode, source: 'delegation' })
|
||||
}
|
||||
if (inheritedPolicy !== undefined) {
|
||||
childSession.append('approval/policy', { policy: inheritedPolicy, source: 'delegation' })
|
||||
}
|
||||
appendDelegatedPolicyOverrides((childCtx.agent as Agent).session, inherited)
|
||||
applyChildComposition(childCtx, {
|
||||
persona: request.persona,
|
||||
toolFilter: request.toolFilter,
|
||||
|
||||
Reference in New Issue
Block a user