import type { ToolCall, ToolResult } from "../../providers/types.js"; import type { ToolHandler } from "./handlers.js"; import type { HookDecision } from "../loop-events.js"; /** * Delegating tool-call executor (ADR-2, agent-loop-capability-port sprint 4). * * Ports the agentic loop's per-tool serial block into a standalone function so * contiguous runs of read-only-annotated tool calls can run concurrently while * everything else (writes, unmarked tools, or `parallel: false`) stays strictly * sequential. Classification travels with `ToolDef.readOnly` — this module * never hard-codes a tool-name allow-list; the caller derives `readOnlyTools` * from the tool schemas it was configured with. */ export interface ToolBatch { /** Tool calls requested by the model this turn, in original order. */ toolCalls: ToolCall[]; /** Handler functions for each tool, keyed by name. */ toolHandlers: Map; /** Names of tools annotated `readOnly: true` (derived once by the caller from `params.tools`). */ readOnlyTools: Set; /** * When `false` (or omitted), every call runs strictly serially — byte-identical * to the pre-change for-await loop, regardless of any `readOnly` annotation. */ parallel: boolean; /** Called when a tool is dispatched (for logging/progress). Fires for every call, including unknown tools, before the handler lookup. */ onToolUse?: (name: string, input: unknown) => void; /** * Dispatch-time observability event (agent-loop-capability-port sprint 5). * Fires synchronously, BEFORE any await — same timing guarantee as * `onToolUse` — so dispatch order is unaffected whether this is set or not. */ onToolStart?: (call: ToolCall) => void; /** * Settle-time observability event, fired after a result (allowed, denied, * or errored) is built, for every call. */ onToolEnd?: (call: ToolCall, result: ToolResult) => void; /** * Host-side veto gate. Evaluated BEFORE the handler runs; a `{allow:false}` * decision skips the handler and produces an `isError` rejection result * instead. Always resolves to a decision — the caller (the loop) already * wraps a throwing hook into a fail-closed deny before passing it in here, * so the executor never needs its own try/catch around this call. */ preToolUse?: (call: ToolCall) => Promise; /** * Observes the result after execution (allowed, denied, or errored). The * caller (the loop) already wraps a throwing hook in try/catch before * passing it in here, so the executor never needs its own try/catch * around this call either. */ postToolUse?: (call: ToolCall, result: ToolResult) => void | Promise; } /** * Execute a turn's tool calls, delegating maximal contiguous runs of * read-only-annotated calls to `Promise.all` while everything else runs * strictly one-at-a-time (identical to the old for-await loop). * * - Results are assembled by original array position, so the returned * `ToolResult[]` always matches `toolCalls` order — even when a run * executed concurrently and resolved out of order. * - Never rejects: `executeOne` catches every failure into an in-slot * `isError` ToolResult, so a thrown handler or unknown tool never * propagates past its own slot. */ export declare function executeToolBatch(batch: ToolBatch): Promise; //# sourceMappingURL=executor.d.ts.map