feat(approval): the approval seam — one-shot permission decisions over a waterfall of answerers
ctx.approval (dsh-approval): request() dispatches the approval/request waterfall and always resolves a closed outcome — allowed-once / rejected / cancelled / unavailable — never rejects; zero listeners fall through to fail-closed unavailable; abort settles cancelled and discards late answers; throwing or rogue answerers are contained as unavailable; every ask lands the log-only approval/asked / approval/decided audit pair. dsh-tools routes a pre-execute ask through the seam opportunistically (ctx.get) with three distinct deny reasons, keeping the historical ask→deny degrade when the seam is absent. The per-session policy tier, the ACP bridge answerer, and the sandbox escalation asker are staged follow-ups of the approval-seam RFC.
This commit is contained in:
@@ -159,6 +159,15 @@ const SERVICE_ROLES: ServiceRole[] = [
|
||||
consumers: ['tool-bash', 'hooks-claude', 'hooks-codex'],
|
||||
note: 'The model-facing bash tools and hook bridges consume this seam; sandboxed or remote executors can replace bash-local.',
|
||||
},
|
||||
{
|
||||
key: 'approval',
|
||||
pkg: 'approval',
|
||||
title: 'Approval seam',
|
||||
mode: 'seam',
|
||||
implementations: [],
|
||||
consumers: ['tools'],
|
||||
note: 'One-shot permission decisions dispatched over the `approval/request` waterfall; answerers are listeners (the ACP bridge for its own agents), absence fails closed to `unavailable`.',
|
||||
},
|
||||
{
|
||||
key: 'codeRuntime',
|
||||
pkg: 'code-runtime',
|
||||
@@ -673,7 +682,8 @@ function renderToolPipeline(): string {
|
||||
` toolCall["Session event: ${mermaidCode('tool/call')}<br/>logged before execution"]`,
|
||||
' presentCall["UI pending card<br/>presentCall(args)"]',
|
||||
` pre["${mermaidCode('tools/pre-execute')} waterfall<br/>hooks, permission, sandbox"]`,
|
||||
' denied["deny or ask<br/>tool body skipped"]',
|
||||
' denied["denied<br/>tool body skipped"]',
|
||||
` approval["${mermaidCode('ctx.approval')} one-shot prompt<br/>absent or unanswerable: deny"]`,
|
||||
` around["${mermaidCode('tools/execute')} waterfall<br/>timeout, retry, metrics (around dispatch)"]`,
|
||||
' toolBody["Registered tool execute() body"]',
|
||||
` fsGate["${mermaidCode('fs/write-intent')} or ${mermaidCode('fs/edit-intent')}<br/>tool-fs mutations only"]`,
|
||||
@@ -687,7 +697,10 @@ function renderToolPipeline(): string {
|
||||
' toolCall --> pre',
|
||||
' pre -->|allow| around',
|
||||
' around --> toolBody',
|
||||
' pre -->|deny or ask| denied',
|
||||
' pre -->|deny| denied',
|
||||
' pre -->|ask| approval',
|
||||
' approval -->|allowed-once| around',
|
||||
' approval -->|rejected, cancelled, unavailable| denied',
|
||||
' denied --> post',
|
||||
' toolBody --> fsGate',
|
||||
' fsGate --> toolBody',
|
||||
@@ -699,7 +712,7 @@ function renderToolPipeline(): string {
|
||||
' toolResult --> presentResult',
|
||||
'```',
|
||||
'',
|
||||
'Filesystem read-before-edit checks live below `tool-fs` on the `fs/*` event gate; hook bridges and future permission prompts live on the generic pre/post tool waterfalls; and around-dispatch concerns like the tool-call timeout policy (`@deepseek-ai/dsh-timeout-policy`) wrap core dispatch on `tools/execute`. That split lets the same hooks observe bash, fs, web, todo, and subagent calls without coupling those tools to one policy service. Code Mode rides the same pipeline twice over: `run_code` is itself a registered tool body, and each tool call its program makes re-enters `ctx.tools.execute()` through BOTH waterfalls — serialized one at a time, logged as a `tool/code-dispatch` session event, with a deny surfacing to the program as a binding rejection (a sub-call\'s `additionalContext` is deliberately dropped — no safe outlet mid-run preserves call/result adjacency).',
|
||||
'Filesystem read-before-edit checks live below `tool-fs` on the `fs/*` event gate; hook bridges and the approval seam\'s permission prompts live on the generic pre/post tool waterfalls; and around-dispatch concerns like the tool-call timeout policy (`@deepseek-ai/dsh-timeout-policy`) wrap core dispatch on `tools/execute`. That split lets the same hooks observe bash, fs, web, todo, and subagent calls without coupling those tools to one policy service.',
|
||||
'',
|
||||
...maintenanceFooter(maintenance),
|
||||
].join('\n')
|
||||
|
||||
Reference in New Issue
Block a user