/** * The terminal approval answerer: one `approval/request` waterfall listener * that renders the pending question as a y/n bar and resolves the decision * back into the waterfall. Mirrors the web host's composer takeover — the * service (audit pair, policy gate, fail-closed defaults) all live in * dsh-base; this module only answers for agents this TUI owns. * * Vocabulary note: a client answerer may only ever resolve `'allowed-once'` * or `'rejected'`; `'cancelled'` belongs to the request signal and * `'unavailable'` to the fail-closed waterfall default. * * @module @deepseek-ai/dsh-code/approval */ import type { Context } from '@deepseek-ai/cordis'; import type { Agent } from '@deepseek-ai/dsh-agent'; import type { ApprovalRequest } from '@deepseek-ai/dsh-user-approval'; /** The answer values a client answerer may resolve with. */ export type ApprovalAnswer = 'allowed-once' | 'rejected'; /** One pending approval question, derived from the request for rendering. */ export interface PendingApproval { /** The asker's human-readable explanation, or a generic fallback. */ headline: string; /** The tool the question is about. */ toolName: string; /** Command-line preview resolved from the paired streaming tool call. */ command: string; /** Resolve the ask; calling twice is inert (one-shot latch). */ answer(outcome: ApprovalAnswer): void; } /** The pending-question snapshot the renderer subscribes to. */ export interface ApprovalSnapshot { /** The question on screen (queue head), or undefined when none is asked. */ pending: PendingApproval | undefined; /** Presentational: an answer was submitted, the ask has not settled yet. */ answered: boolean; /** Further asks waiting behind the on-screen one (FIFO, Codex-style). */ queued: number; } /** Store the pending question lands in; the renderer reads, the answerer writes. */ export interface ApprovalStore { /** Subscribe to pending-state changes; returns the unsubscribe function. */ subscribe(listener: () => void): () => void; /** Read the current snapshot (identity-stable between changes). */ getSnapshot(): ApprovalSnapshot; } /** * Create the approval store and mount the answerer listener on the context. * The listener claims only requests for `owns`-owned agents and defers every * other request back into the waterfall (`next()`), so sibling answerers stay * usable. An aborted ask never reaches the human. Plugin teardown removes the * listener; the service then fails its own question closed. * @param ctx - plugin context whose event bus carries `approval/request`. * @param owns - agents this terminal answers for. * @param preview - resolves a tool-call preview for a pending request (the * request contract carries no arguments; the UI self-serves from the * transcript projection via `callId`). * @returns the store the renderer subscribes to. */ export declare function mountApprovalAnswerer(ctx: Context, owns: (agent: Agent) => boolean, preview: (request: ApprovalRequest) => string): ApprovalStore;