/** * Tool fence protocol — shared by the subprocess-backed AgentRunners * (KimiCodeAgentRunner, OpencodeAgentRunner). * * Why this exists: the Claude Agent SDK lets the daemon register org tools * (org_send, ask_human, …) as REAL tools via createSdkMcpServer. Subprocess * backends (the kimi CLI, an opencode server) own their tool surface — an * external caller cannot register tools per-turn. Instead the tools are * rendered INTO the role's system prompt; the model emits ```tool_call * fenced JSON blocks; the runner parses them out of the assistant text, * executes the real OrgToolDef handlers in-process (the same handlers * ClaudeAgentRunner registers with the SDK), and feeds the results back as * ```tool_result fences in the next prompt of the same session. */ import { z } from 'zod'; import type { AgentRunArgs, OrgToolDef } from './agent-runner.js'; /** The zod object that validates `tool`'s arguments: its shape, keeping * unlisted keys when the tool declares a catchall and rejecting them when it * is strict. */ export declare function toolInputSchema(tool: OrgToolDef): z.ZodObject; /** A z.strictObject whose unknown-key error names each key, with `hints[key]` * for a key callers are known to confuse with a declared one. Its JSON Schema * carries `additionalProperties: false`, so the model sees the rule too. */ export declare function strictArgs(shape: T, hints?: Record): z.ZodObject; /** Fenced block the model uses to call a tool (see buildToolProtocol). */ export declare const TOOL_CALL_RE: RegExp; /** Default max tool_call → tool_result round-trips within a single mailbox * prompt (`AgentRunArgs.maxToolRounds` overrides it). Guards against a model * that keeps calling tools forever. */ export declare const MAX_TOOL_ROUNDS = 10; /** The tool result a role gets for each call it made once the round cap was * reached: the call did not run, and the next round is its last. */ export declare function roundCapResult(cap: number): string; /** Handle one round of parsed tool calls, `round` being how many rounds this * message already ran. Below the cap (`args.maxToolRounds`, default * MAX_TOOL_ROUNDS) the calls run. At the cap each gets roundCapResult * instead, so the role learns why and can wrap up; the calls of that wrap-up * round run. Anything later is dropped: `results` is then undefined and the * runner stops. `note` is for the bus. */ export declare function runToolRound(args: Pick, calls: ToolCall[], round: number): Promise<{ results?: string[]; note?: string; }>; export interface ToolCall { name: string; arguments: Record; } /** * Render the org tools as a text protocol appended to the role's system * prompt. The model calls a tool by emitting a fenced block: * * ```tool_call * {"name": "org_send", "arguments": {"to": "...", "subject": "...", "message": "..."}} * ``` * * Results come back as a user-role prompt containing ```tool_result fences. * * IMPORTANT wording note: the tools listed here are ORG tools (org_send, * org_recall, …) that exist ONLY through this protocol — the model cannot * reach them natively. But the agent also has NATIVE file/shell tools * (Write, Edit, Bash, …) for doing its actual work. Saying "you have no * native tools" makes the model believe it cannot even write files — the * protocol must name the distinction explicitly. */ export declare function buildToolProtocol(tools: OrgToolDef[]): string; /** The name of the stand-in call parseToolCalls returns for a fence it could * not parse. It is not an org tool: executeToolCall answers it with an error * telling the role its call did not run and how to send it again, and that * answer goes back to the role as a tool_result like any other. */ export declare const MALFORMED_FENCE_CALL = "__malformed_tool_call"; /** What the role is told for a fence that would not parse. */ export declare function malformedFenceResult(error: unknown): string; /** Extract tool_call fences from raw assistant texts. A fence whose JSON * cannot be parsed is not dropped: `onMalformed` (when given) is invoked with * the raw fence body and the parse error so callers can surface it (runners * emit it as an assistant note, which session.ts routes to the org bus and * scrollback), and a MALFORMED_FENCE_CALL stands in for it in the returned * calls, in its place among the others. Running it returns an error to the * role, so a role whose send did not parse learns that and can retry instead * of ending its turn believing the send went. A parsed object without a * string `name` is skipped quietly — there is nothing actionable to report. */ export declare function parseToolCalls(rawTexts: string[], onMalformed?: (raw: string, error: string) => void): ToolCall[]; /** Execute one tool call against the OrgToolDef handlers, validating args * against the tool's zod shape. Handler errors come back as text so the * model sees the failure instead of the turn dying. * * When `canUseTool` is provided (passed by fence-protocol runners like * CodexAgentRunner that can't use the SDK's native permission gate), it is * invoked AFTER zod validation but BEFORE the handler — a deny decision * short-circuits with a policy-error message instead of executing. */ export declare function executeToolCall(tools: OrgToolDef[], call: ToolCall, canUseTool?: (toolName: string, input: Record) => Promise): Promise; /** Format executed results as the next prompt's tool_result fences. */ export declare function formatToolResults(calls: ToolCall[], results: string[]): string; //# sourceMappingURL=tool-fence.d.ts.map