/** * `agent()` — a define-type for LLM agents with durable run state. * * ```ts * // functions/researcher.ts * export default agent({ * system: "You are a research assistant.", * tools: { * searchDocs: { * description: "Search the document library", * args: { query: v.string() }, * handler: async (ctx, { query }) => ctx.runQuery("findSimilar", { query }), * }, * }, * }); * ``` * * An agent compiles to an ordinary streaming ACTION (named after its * file, callable via `streamFn("researcher", { input })`), whose * handler runs the tool loop: * * 1. Create an `AgentRun` row (or load one when `runId` is passed — * that's how a conversation continues) and append the user's * input as an `AgentMessage`. * 2. `ctx.llm.stream` with the declared tools; text deltas flow to * `ctx.stream` (resumable — the run row records the stream id). * 3. On `stop_reason === "tool_use"`: validate each tool call's * input against its validators, run the handler, record the * call + result as messages, loop. * 4. Terminal: run marked completed/failed. * * `AgentRun` and `AgentMessage` are real synced entities (injected * into the manifest by the SDK when any agent exists), owner-scoped by * policy — so `db.useQuery("AgentMessage", { where: { runId } })` * shows the transcript live on every one of the user's devices, * including tool calls, with zero extra plumbing. */ import type { ActionCtx, AnyValidator, FnDefinition, ValidatorSchema } from "./types"; /** One tool an agent can call. */ export interface AgentTool { /** Shown to the model — say when to use the tool, not how it works. */ description: string; /** Argument validators (same `v.*` schema as functions). The model's * JSON is validated before the handler runs; invalid input becomes * a tool_result error the model can react to. Omit for no-arg tools. */ args?: ValidatorSchema; /** Runs with the agent action's ctx (runQuery/runMutation, llm, * email, …). The return value is JSON-serialized into the * tool_result the model sees. Throwing marks the result is_error — * the model sees the message and can recover. */ handler: (ctx: ActionCtx, input: Record) => unknown; } export interface AgentDefinition { /** System prompt. A function receives the ctx + call args for * per-user prompts. */ system?: string | ((ctx: ActionCtx, args: AgentCallArgs) => string); tools?: Record; /** Model override (subject to the server's allowlist). */ model?: string; /** Max model↔tool round-trips per invocation (default 64). Hitting * the cap fails the run rather than looping forever. Steering input * drained mid-invocation extends the same invocation, so this bounds * a steered turn too. The run row's cumulative `steps` counts every * invocation and is not capped. */ maxSteps?: number; /** max_tokens per completion (default: server default). */ maxTokens?: number; /** Auth gate for the action (default "user" — runs are owner-scoped, * so an authenticated caller is the natural default). */ auth?: "user" | "admin"; /** Idle timeout in seconds (default 600; activity extends it). */ timeout?: number; } /** The synthesized action's args. */ export interface AgentCallArgs { /** The user's message for this turn. Required unless `cancel`. */ input?: string; /** Continue an existing run (must belong to the caller and this * agent). Omit to start a new run. Sending while the run is already * generating queues the message for that generation rather than * refusing it — see {@link AgentResult.queued}. */ runId?: string; /** Optional display title, stored on new runs. */ title?: string; /** Ask the run to stop. Requires `runId`, ignores `input`, and * returns as soon as the request is recorded — a live generation * stops at its next turn boundary. */ cancel?: boolean; } /** What the agent action resolves with (also the `event: result` * payload on the SSE stream). */ export interface AgentResult { runId: string; /** Concatenated text of the final assistant message. */ text: string; /** Round-trips consumed by THIS invocation. */ steps: number; usage: { input_tokens: number; output_tokens: number; }; /** The message was queued onto a generation already in flight * instead of starting a turn. No model call happened on this call; * the running loop picks the message up at its next boundary. */ queued?: boolean; /** The run stopped because cancel was requested. `text` holds * whatever the model had produced by then. */ cancelled?: boolean; } /** Convert one `v.*` validator to a JSON-Schema fragment. */ export declare function validatorToJsonSchema(val: AnyValidator): Record; /** Convert a validator schema (a tool's `args`) to a JSON-Schema * object with `required` derived from non-optional fields. */ export declare function validatorSchemaToJsonSchema(schema: ValidatorSchema): Record; /** Marker so the SDK's discoverFunctions can detect agents and inject * the AgentRun/AgentMessage entities into the manifest. */ export declare const AGENT_MARKER = "__pylonAgent"; export declare function agent(def: AgentDefinition): FnDefinition; export declare function isAgentDefinition(value: unknown): boolean;