mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Add a three-value BUZZ_ACP_PERMISSION_POLICY (allow | ask | reject) that gates how session/request_permission calls are handled: - reject (headless default): synchronous denial, byte-for-byte unchanged from today's dontAsk behavior; ResolvedPermissionConfig derives dontAsk mode so the adapter self-denies before Buzz sees the request. - allow: synchronous auto-selection of the unique allow_once option from the exact options in the request; zero/multiple allow_once candidates or malformed options fail closed with a denial. Never allow_always, never hardcoded IDs. - ask: interactive — emits an acp_read telemetry frame with an authorization envelope (requestNonce, actionable, reason) and registers a pending entry in a bounded map (cap=8) on AcpClient. The desktop delivers a permission_decision control frame carrying the nonce and chosen optionId; the read loop matches by nonce, validates the optionId against the captured option snapshot, and writes the ACP response. Per-request timeout min(300s, remaining hard deadline) fails closed. Key implementation details: - ResolvedPermissionConfig computed once at startup; transmits effective_mode via set_config_option for every agent that advertises the mode field (goose skipped). - Admission preflight (synchronous, before map insertion): options nonempty, count ≤ 16, every optionId unique+nonempty, required kind/name fields, duplicate live requestId → immediate denial with original untouched, map at cap → deny, serialized payload ≤ OBSERVER_MAX_PLAINTEXT_LEN. - Cancel during writing → PermissionPoisoned error: surfaces through cancel_with_cleanup_grace so classify_control_cancel_failure triggers respawn (not pool return). PermissionPoisoned added to is_transport_error. Pending entries drained with cancelled responses before session/cancel. - ask without observer or unresolved owner downgrades to reject with a loud warning. - acp_read generic emit suppressed for ask permission requests; replaced with a single post-preflight enveloped emit (one frame per request). - Decision receiver arm placed ahead of reader arm in the biased select! for inbound fairness. - ObserverEvent gains optional authorization: Option<AuthorizationEnvelope> with skip_serializing_if. Payload bytes remain raw ACP, never mutated. - NIP-AO.md reconciled: adds authorization envelope, permission_decision control type, control_result telemetry kind, switch_model control type, single-use nonce semantics, best-effort delivery with mandatory timeout, cancel-during-write poison behavior, and 5-minute desktop live lookback. Tests: 720 passing (31 new pinned tests covering mode matrix, admission preflight, allow selector, ask map lifecycle, cancel-during-writing poison, policy × mode combinations, and decision arm behavior). Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>