import type { z } from "zod"; import type { AuthContext } from "../support/index.js"; import type { SecretRefManifest } from "../wire/manifest.js"; /** * Per-invocation context the worker threads through to a tool. Equivalent to * a tool framework's `ToolContext` but typed in Boardwalk's idiom (AuthContext + run id + * secret resolver scoped to this run's permissions). */ export interface ToolContext { readonly auth: AuthContext; /** Run id (`runs.id`). Used for tagging artifacts + correlation. */ readonly runId: string; /** Org-scoped resolver. Returns the secret VALUE, not the ARN. */ readonly secrets: SecretResolver; } export interface SecretResolver { /** * Resolve a manifest secret reference to its plaintext value. Throws * `AppError(FORBIDDEN)` when the reference isn't in the agent's * `permissions.secrets` allowlist, or `AppError(NOT_FOUND)` when the named * secret doesn't exist in the org. */ resolve(ref: SecretRefManifest): Promise; } /** * Boardwalk-side tool interface. Concrete tools either: * * Return a `ToolReturn` body — synchronous "normal" tools (echo, http, * web_search). The body lands in the conversation as the assistant's * tool-result block. * * Return a `ToolControlSignal` — the legacy tool-level sleep/workflows.call path. The * current JS-body worker exposes these as program SDK hooks instead; agent() leaves strip * control-flow tools before registering model-callable tools. */ export interface BoardwalkTool { /** Stable tool name — what an `agent()` call names in `AgentOptions.tools`. */ readonly name: string; /** Description surfaced to the model. */ readonly description: string; /** Zod schema for the tool's input. */ readonly inputSchema: z.ZodType; /** * Zod schema for the tool's SUCCESS output (the `TOutput` shape — never the * control-signal branch). The adapter validates the tool's return value * against this before it lands in the LLM conversation * (LLM-facing output is treated like untrusted external input). */ readonly outputSchema: z.ZodType; /** * Secret names this tool requires (matching entries in the org's secret * store / the manifest's `permissions.secrets` allowlist). Used for * declarative per-tool secret scoping in the sandbox. Most built-ins are * `[]` because they either need no secrets or receive them via `ctx` * (resolved env / `ctx.secrets`); `web_search` declares the Tavily key. */ readonly secretsRequired: readonly string[]; /** * Optional: normalize the raw LLM-supplied args BEFORE `inputSchema.parse`. Lets a tool accept a * common-but-wrong shape the model tends to emit (e.g. `{command:"clone"}` aliased to the schema's * `{op:"clone"}` discriminator) so the first attempt succeeds instead of bouncing off a `ZodError` * and costing a retry. Keeps `inputSchema` (the model-facing JSON schema) pristine — the alias is * applied at the adapter's parse boundary, never exposed. Must be pure + total (return the input * unchanged when nothing applies). */ normalizeInput?(raw: unknown): unknown; /** * Invoke the tool. `input` (LLM-supplied) comes first, `ctx` (worker-supplied) * second — see the `Tool` naming note in SPEC.md. Returns either the typed * `TOutput` body or a `ToolControlSignal` (sleep / workflows.call) the worker * intercepts before serialization. */ invoke(input: TInput, ctx: ToolContext): Promise; } /** * Discriminated union of "I'm not really returning a result; tell the engine * something." Worker checks `result instanceof ToolControlSignal` (well — * structurally on `__signal`) before serializing into the conversation. */ export type ToolControlSignal = SleepControlSignal | WaitForChildControlSignal; export interface SleepControlSignal { readonly __signal: "sleep"; /** Wall-clock time to wake at (ms since epoch). */ readonly wakeAtMs: number; } export interface WaitForChildControlSignal { readonly __signal: "wait_for_child"; /** The child run id the parent is now blocked on. */ readonly childRunId: string; } export declare function isControlSignal(value: unknown): value is ToolControlSignal; /** * Tool registry — maps a tool name to its concrete `BoardwalkTool`. Built once * per worker process; each `agent()` leaf sees a filtered view based on the * tools that call named (`AgentOptions.tools`), materialized via * `materializeFor(grants)`. */ export declare class ToolRegistry { private readonly tools; register(tool: BoardwalkTool): void; get(name: string): BoardwalkTool | undefined; has(name: string): boolean; /** Names of every registered tool. Used by the `list_tools` MCP tool. */ list(): string[]; /** * Filter the registry down to the tools granted by a manifest. Names absent * from the registry are returned in `missing` so the worker can fail-loud * before the LLM tries to call something that doesn't exist. */ materializeFor(grants: readonly { name: string; }[]): { tools: BoardwalkTool[]; missing: string[]; }; }