import type { ApprovalId, Guard, Harness, Json, RunContext, ToolOutcome, ToolRegistry, ToolResult, TurnId, TurnTools } from "@vendoai/core"; import type { CapabilityMissReporter } from "./capability-miss.js"; import { type ToolBridgeOptions } from "./tool-bridge.js"; /** * Build contract §1.4 — the frozen bound on an interactive approval wait. A * closed tab must not hold a turn open forever, and no sandbox lease is held * while waiting. */ export declare const APPROVAL_WAIT_MS = 90000; /** * What the runtime writes to the transcript and the screen on the harness's * behalf (build contract §1.5: "Tool calls are mirrored by the runtime, never * yielded"). This is the ai-SDK tool-part mirror ONLY — the `data-vendo-*` parts * (view, approval, connect, build-failed, citations) are written by the SHIPPED * bridge inside `guardedCall`/`previewApproval`, so a harness produces the * identical wire the legacy agent path produced. */ export type MirrorEvent = ({ /** The turn that made this call, stamped once by {@link createTurnTools} from * the ctx. Absent only for a call made outside a turn. */ turnId?: TurnId; }) & ({ kind: "call"; toolCallId: string; name: string; args: Json; } /** An interactive parked call. The shipped thread renders its consent card off * the NATIVE approval state, so without this the card never appears and the * wait below can only ever time out. */ | { kind: "approval"; toolCallId: string; approvalId: ApprovalId; } /** `result` is what the MODEL reads (§1.1's three statuses). `outcome` is what * the SCREEN reads: the ai-SDK path puts the whole typed outcome on the native * tool part, and the connect card is rendered from it. */ | { kind: "result"; toolCallId: string; name: string; result: ToolResult; outcome?: ToolOutcome; }); export interface TurnToolsOptions { /** The GUARD-BOUND registry (`VendoGuard.bind(tools)`) — the one choke point. * Wrapping it again here would double-charge the guard's breakers. */ registry: ToolRegistry; guard: Guard; ctx: RunContext; /** §1.4: did the caller prove presence? Decides wait-or-fail, nothing else. */ interactive: boolean; mirror: (event: MirrorEvent) => void; /** The rest of the shipped bridge's rails: the writer the `data-vendo-*` parts * go to, `toolOutputCap`, `preflight`, the per-turn `connectCards` dedupe set, * and the capability-miss `onCall` hook. */ bridge?: Omit; /** The capability-miss reporter, listed on the surface and dispatched here. * Unset means the honest-refusal rail is simply not wired for this turn. */ capabilityMiss?: CapabilityMissReporter; /** Contract §1, amendment 2026-08-03: the harness's say over which names it is * offered — `withhold` takes names OFF this surface (claudeCode withholds * `vendo_make`, whose job its own builder does). Never a safety mechanism — * the ctx projection above is. Loadout curation is the brain's own strategy * now (`vendo()`'s tool-search hand), not a runtime rail. */ toolSurface?: Harness["toolSurface"]; /** This turn's bound on an interactive approval wait. Unset uses * {@link APPROVAL_WAIT_MS} — the web's closed-tab bound, unchanged. A turn * whose person answers on a human clock (a text message) passes its own. */ approvalWaitMs?: number; } /** * §1.4's race: the approvalId only exists once the guard has been consulted, but * the user's tap can land in that same tick. Subscribing to every decision for * the whole turn and buffering the ones nobody is waiting for yet is what makes * the wait reliable; a late subscribe would hang until the timeout. */ export interface ApprovalWaiter { /** Resolves true/false with the decision, or undefined if the bound expired. */ wait(approvalId: ApprovalId, timeoutMs: number): Promise; /** * Note an approval this turn raised, WHICHEVER path minted it — the preview, or * the real dispatching check after the preview said run (a breaker or presence * boundary). Recording only the ones we wait on would leak the rest forever. * * `standing: true` marks the `interactive: false` card, which is MEANT to * survive the turn so "Grant & re-run" can collect it. */ raise(approvalId: ApprovalId, options?: { standing?: boolean; }): void; /** Raised, undecided, and not standing — the runtime abandons these at turn * end, so a live-but-dead card cannot accrete in the pending queue. */ unanswered(): ApprovalId[]; dispose(): void; } export declare function createApprovalWaiter(guard: Guard): ApprovalWaiter; export interface RuntimeTurnTools extends TurnTools { /** §1.4 + the orphaned-approval fix: ids this turn raised and nobody answered. */ unansweredApprovals(): ApprovalId[]; dispose(): void; } export declare function createTurnTools(options: TurnToolsOptions): RuntimeTurnTools; //# sourceMappingURL=turn-tools.d.ts.map