Review fixes (ds-review-bot warnings on #623): - One chain, every consumer: the override resolution (own post-seed switches ?? header baseline, closed-vocabulary validated) moves into pure exports (sandboxOverrideOf / approvalOverrideOf); the services delegate, and the permission presets consume them — current(session) and set() now see inherited baselines, so a child inheriting danger-full-access gets REAL knob switches when workspace-write is selected instead of a silent no-op, and a seed-carried preset selection is subsumed by the baseline. current(events) becomes current(session) (pre-release; the only callers were tests). - Unconditional durable validation: a malformed header baseline fails loud on every read, no longer shadowed by an own switch. - The two policy peers are declared optional (peerDependenciesMeta), so a thin spawn/fork deployment without policy plugins can consume the driver; verify-runtime-closure honors the flag. Red-first: inherited-preset derive/switch-away and seeded-selection tests in the permission suite; malformed-baseline-with-own-switch tests in both policy suites.
@deepseek-ai/dsh-user-approval
English | 中文
Channel-neutral one-shot approval seam. ctx.approval.request(req) returns allowed-once, rejected, cancelled, or unavailable; missing or failing answerers fail closed, and a grant applies only to the requested action. Exact event signatures live in the generated Cordis catalog.
Each request must belong to an open agent turn. The service appends a paired approval/asked and approval/decided audit record, while the model sees only the resulting logged tool outcome. An aborted request resolves cancelled; an audit append that fails before commit rejects rather than returning an unlogged decision.
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 automation bridge supplies one-shot machine decisions for sessions it owns.
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.overrideOf(session) (the pure approvalOverrideOf export, also consumed by the permission presets) resolves the session's override chain, never the configured default: the fold of the session's OWN switches (events past SessionHeader.seedLength), else the header's inherited approvalPolicy delegation baseline, validated against the closed vocabulary on read; the in-process subagent driver captures this at delegation and writes it into each child's creation-time header, so a 'never' parent cannot mint prompting children, with no first-turn timing window (rationale).
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 automation bridge answers calls for its own agents through the client's machine policy. Audit events remain log-only, so the model sees only the asking consumer's result. See the approval-seam Agent Note and sandbox Agent Note.
Model Experience
System prompt and policy notice
What the model sees
Under ask, every agent request carries the ask-policy prompt section below. Under never, it carries the never-policy prompt section below. A policy switch injects exactly The approval policy changed from "<old>" to "<new>" (changed by the user). or The approval policy changed from "<old>" to "<new>" (changed by the operator/config). before the next step.
Ask-policy prompt section
<!-- dsh-user-approval-policy:ask -->
Never-policy prompt section
Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).
<!-- dsh-user-approval-policy:never -->
Token effect
Small fixed per-request cost, larger under never; a change notice is conditional and retained in history.
KV Cache effect
Prefix-stable while the approval policy is unchanged. An ask/never switch changes the system-prompt section and invalidates reuse from its first changed token; the accompanying notice is append-only.
Tool outcome
What the model sees
approval/asked and approval/decided are log-only. The model sees only the asking consumer's eventual allowed, rejected, cancelled, or unavailable tool outcome; the human permission UI is not context.
Token effect
Zero duplicate audit tokens. A rejection may replace a normal tool result with a small retained error, while an allowance leaves the consumer's ordinary result.
KV Cache effect
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
Known Limitations and Deferred Work
- Requests are valid only inside an open turn — an idle or between-turn caller throws before auditing; a durable out-of-turn approval workflow is deferred.
- Only one-shot grants exist — the outcome vocabulary has
allowed-oncebut noallow-always, remembered rule, revocation, or grant store; session policy is onlyask/never. - The request carries no tool arguments — an answerer sees the tool name, reason, and optional call id; the ACP machine channel requires a call id and delegates requests without one.
- No built-in answerer — headless or incompletely composed deployments resolve
unavailableand fail closed; the service itself never prompts a human.