import type { z } from 'zod'; import type { Tool, ToolCall, ToolCallContext, ToolDefinitionOptions, ToolDefinitionSchema, ToolExecuteFn } from './types.js'; /** * Control chunk a server tool can `yield` from an `async function*` * execute to pause the enclosing agent loop and surface a set of CLIENT * tool calls to the caller — as if the model itself had emitted them. * * This is the sanctioned way for a server tool to inject client-tool * calls into the parent loop's pending list. The agent loop iterating * the execute generator recognizes the shape via the reserved `__rudderjs` * discriminator, appends `toolCalls` to the parent's * `pendingClientToolCalls`, sets the stop-for-client-tools flag, and — * critically — does NOT push an error tool_result / tool message for the * yielding tool call. The yielding tool's own call remains orphaned in * the parent's message history until the caller resolves it on * continuation (typically by computing a final result from whatever the * client-side tool execution returned). * * Why a yield instead of a throw: * - Symmetry with the existing `tool-update` yield protocol — no parallel * catch-based control path. * - Middleware can observe pauses through `runOnChunk`; a thrown error * would route through `onError` and muddle telemetry. * - Exceptions signal "something went wrong"; this is not an error. * - Platform-level feature: any server tool can yield this, not just * nested agent runners. E.g., a tool that wants the browser's * geolocation, clipboard, or a user file upload. * * **Primary use case today:** nested agent runners (`run_agent` in * panels). A runner that streams a sub-agent server-side can hit a pause * point when the sub-agent's model calls a client tool. Those calls have * to execute in the browser, not server-side, so the parent loop must * surface them to its own caller. * * Tools that yield this chunk are responsible for persisting any state * they need to resume the inner work on continuation (usually in a cache * or runStore). `@gemstack/ai-sdk` does not know or care about that state — * it just propagates the pause. The optional `resumeHandle` is an opaque * string the tool author owns; the agent loop never inspects it. * * Tool authors should construct this via {@link pauseForClientTools}, * not by hand, so future shape changes stay source-compatible. */ export interface PauseForClientToolsChunk { /** Reserved discriminator. Namespaced to avoid colliding with user data. */ readonly __rudderjs: 'pause_for_client_tools'; readonly toolCalls: ToolCall[]; readonly resumeHandle?: string; } /** * Construct a pause control chunk for `yield`ing from a server tool's * async-generator execute. * * @example * .server(async function* (input, ctx) { * const subRunId = await persistSubRunState(...) * yield pauseForClientTools(subAgentPending, subRunId) * // Unreachable — the loop halts iteration after the pause chunk. * return undefined as never * }) */ export declare function pauseForClientTools(toolCalls: ToolCall[], resumeHandle?: string): PauseForClientToolsChunk; /** * Structural typeguard for a pause chunk. Used by the agent loop to * detect pauses mid-execute without requiring tool authors to import any * `@gemstack/ai-sdk` symbol at the yield site — they can yield via * {@link pauseForClientTools} or construct the object literal directly. */ export declare function isPauseForClientToolsChunk(value: unknown): value is PauseForClientToolsChunk; /** * Control chunk a server tool can `yield` to pause the enclosing agent * loop on an APPROVAL gate inside a sub-agent — the sibling of * {@link PauseForClientToolsChunk} for the * `finishReason === 'tool_approval_required'` case. * * The agent loop iterating the execute generator recognizes the shape * via the reserved `__rudderjs` discriminator, sets * `loopCtx.pendingApprovalToolCall` from `toolCall`/`isClientTool`, * flips `loopCtx.loopFinishReason = 'tool_approval_required'` and * `loopCtx.stopForApproval = true`, and SKIPS the tool_result emission * for the yielding tool call — the sub-agent's call stays orphaned in * the parent message history until the approve/reject decision arrives * via {@link Agent.resumeAsTool}. * * The optional `resumeHandle` (typically the `subRunId` the wrapper just * persisted) is opaque to the framework — tool authors own the lookup. * * **Primary use case:** a sub-agent surfaced through * {@link Agent.asTool} whose model invokes a `.requireApproval(true)` * tool. The sub-agent loop emits `pending-approval`, the wrapping * `asTool` generator persists a snapshot with `pauseKind: 'approval'` * and yields this chunk so the parent loop halts the same way it does * for client-tool pauses. * * Tool authors should construct this via {@link pauseForApproval}, not * by hand, so future shape changes stay source-compatible. */ export interface PauseForApprovalChunk { /** Reserved discriminator. Namespaced to avoid colliding with user data. */ readonly __rudderjs: 'pause_for_approval'; readonly toolCall: ToolCall; /** * `true` when the gated call is a client tool (no `execute` on the * server side); `false` for a server tool the sub-agent would have * run if approval had been granted. */ readonly isClientTool: boolean; readonly resumeHandle?: string; } /** * Construct an approval-pause control chunk for `yield`ing from a server * tool's async-generator execute. * * @example * .server(async function* (input, ctx) { * const subRunId = await persistSubRunState(...) * yield pauseForApproval(innerToolCall, isClientTool, subRunId) * return undefined as never * }) */ export declare function pauseForApproval(toolCall: ToolCall, isClientTool: boolean, resumeHandle?: string): PauseForApprovalChunk; /** * Structural typeguard for an approval-pause chunk. Mirrors * {@link isPauseForClientToolsChunk} so the parent loop can recognize a * pause without forcing tool authors to import any `@gemstack/ai-sdk` symbol * at the yield site. */ export declare function isPauseForApprovalChunk(value: unknown): value is PauseForApprovalChunk; /** * Builder returned by {@link toolDefinition}. The builder itself is a valid * `Tool` — call `.server(execute)` to attach a server-side handler, or use * the builder as-is to register a client tool (no `execute`). */ export declare class ToolBuilder implements Tool, never> { readonly options: ToolDefinitionOptions; readonly definition: ToolDefinitionOptions; /** Builders are valid client tools — `execute` is intentionally absent. */ readonly execute?: undefined; constructor(options: ToolDefinitionOptions); /** * Attach a server-side handler. * * The handler may be a regular async function (single return value) or an * async generator (`async function*`). Generators can `yield` preliminary * progress payloads — each yield surfaces as a `tool-update` stream chunk * while the tool runs. The generator's `return` value becomes the final * tool result. The same tool definition works in both `agent.prompt()` * (yields are silently drained) and `agent.stream()`. */ server(execute: (input: z.infer, ctx?: ToolCallContext) => AsyncGenerator): ServerToolBuilder, TReturn>; server(execute: (input: z.infer, ctx?: ToolCallContext) => TReturn | Promise): ServerToolBuilder, TReturn>; /** Convert to provider-friendly JSON Schema format */ toSchema(): ToolDefinitionSchema; } /** * Builder returned by {@link ToolBuilder.server}. Itself a valid `Tool`, so * it can be passed directly into `tools()` arrays — `.toModelOutput(...)` is * an optional chained refinement, not a required `.build()` step. * * Future per-tool refinements (`.onError(...)`, `.middleware(...)`) would slot * in here in the same chained-builder style. */ export declare class ServerToolBuilder implements Tool { readonly definition: ToolDefinitionOptions; readonly execute: ToolExecuteFn; readonly toModelOutput?: ((result: TOutput) => string | Promise) | undefined; constructor(definition: ToolDefinitionOptions, execute: ToolExecuteFn, toModelOutput?: (result: TOutput) => string | Promise); /** * Declare a transform from this tool's structured result to the string the * **model** sees on its next step. The UI continues to receive the original * structured result. * * @example * toolDefinition({...}) * .server(async (input) => bigStructuredResult) * .modelOutput((result) => `${result.items.length} items found`) */ modelOutput(fn: (result: TOutput) => string | Promise): ServerToolBuilder; } /** * Define a tool with typed input/output schemas. * * @example Server tool * const weatherTool = toolDefinition({ * name: 'get_weather', * description: 'Get weather for a location', * inputSchema: z.object({ location: z.string() }), * }).server(async ({ location }) => ({ temp: 72, unit: 'F' })) * * @example Client tool (no `.server()`) * const readFormStateTool = toolDefinition({ * name: 'read_form_state', * description: 'Read the user\'s current local form values.', * inputSchema: z.object({ fields: z.array(z.string()).optional() }), * }) */ export declare function toolDefinition(options: ToolDefinitionOptions): ToolBuilder; /** * Build a tool whose input/output types are not known at compile time. * * Use this when the tool's schema is constructed dynamically from user data * (e.g. a resource agent that exposes one tool per agent definition). * * @example * const t = dynamicTool({ * name: agentDef.slug, * description: agentDef.description, * inputSchema: z.object({}), * }).server(async () => agentDef.run()) */ export declare function dynamicTool(options: ToolDefinitionOptions): ToolBuilder; /** Convert any Tool to a ToolDefinitionSchema for provider consumption */ export declare function toolToSchema(tool: { definition: ToolDefinitionOptions; }): ToolDefinitionSchema; //# sourceMappingURL=tool.d.ts.map