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:
kingwl
2026-07-09 15:25:18 +08:00
parent 7afdc6e3b9
commit ef35007d75
27 changed files with 830 additions and 42 deletions

View File

@@ -11,6 +11,30 @@ The on-disk envelope around every payload is `SessionEvent` — `type`, monotoni
## Events
### `approval/*`
#### `approval/asked` — log-only
An approval question was put to the answerer chain — log-only audit (like `hook/*`; NOT a surface event, carries no `surfaceOp`). `id` pairs it with the `approval/decided` that always follows; `toolName` is the tool the question is about, `callId` the exact tool call when the asker had one, `reason` the asker's human-readable explanation (e.g. a hook's permission-decision reason).
```ts persistence-catalog
'approval/asked': { id: ApprovalRequestId; toolName: string; callId?: CallId; reason?: string }
```
Types: [CallId](core-data-structures/core.md)
Source: [`packages/approval/approval/src/index.ts:66`](../packages/approval/approval/src/index.ts)
#### `approval/decided` — log-only
The outcome of a prior `approval/asked` (same `id`) — log-only audit. Exactly one per ask, appended when the outcome is known: a decision, a cancellation, or the fail-closed `'unavailable'`.
```ts persistence-catalog
'approval/decided': { id: ApprovalRequestId; outcome: ApprovalOutcome }
```
Source: [`packages/approval/approval/src/index.ts:77`](../packages/approval/approval/src/index.ts)
### `assistant/*`
#### `assistant/chunk` — log-only