@standardagents/spec 0.11.0-next.22e39d0 → 0.11.0-next.24f9ff1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1113 -246
- package/dist/index.js +67 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,10 +9,9 @@ function defineModel(options) {
|
|
|
9
9
|
if (!options.model) {
|
|
10
10
|
throw new Error("Model ID is required");
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
if (!validProviders.includes(options.provider)) {
|
|
12
|
+
if (typeof options.provider !== "function") {
|
|
14
13
|
throw new Error(
|
|
15
|
-
|
|
14
|
+
"Provider must be a ProviderFactory function imported from a provider package (e.g., @standardagents/openai)"
|
|
16
15
|
);
|
|
17
16
|
}
|
|
18
17
|
if (options.inputPrice !== void 0 && options.inputPrice < 0) {
|
|
@@ -24,23 +23,26 @@ function defineModel(options) {
|
|
|
24
23
|
if (options.cachedPrice !== void 0 && options.cachedPrice < 0) {
|
|
25
24
|
throw new Error("cachedPrice must be non-negative");
|
|
26
25
|
}
|
|
26
|
+
if (options.provider.providerOptions && options.providerOptions) {
|
|
27
|
+
const result = options.provider.providerOptions.safeParse(options.providerOptions);
|
|
28
|
+
if (!result.success) {
|
|
29
|
+
const issues = result.error.issues.map((i) => `${String(i.path.join("."))}: ${i.message}`).join(", ");
|
|
30
|
+
throw new Error(`Invalid providerOptions for model '${options.name}': ${issues}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
27
33
|
return options;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
// src/tools.ts
|
|
31
|
-
function defineTool(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
toolDescription,
|
|
41
|
-
null,
|
|
42
|
-
argsOrTool
|
|
43
|
-
];
|
|
37
|
+
function defineTool(options) {
|
|
38
|
+
return {
|
|
39
|
+
description: options.description,
|
|
40
|
+
args: options.args ?? null,
|
|
41
|
+
execute: options.execute,
|
|
42
|
+
tenvs: options.tenvs ?? null,
|
|
43
|
+
executionMode: options.executionMode,
|
|
44
|
+
executionProvider: options.executionProvider
|
|
45
|
+
};
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
// src/prompts.ts
|
|
@@ -62,10 +64,20 @@ function definePrompt(options) {
|
|
|
62
64
|
`Invalid toolChoice '${options.toolChoice}'. Must be one of: auto, none, required`
|
|
63
65
|
);
|
|
64
66
|
}
|
|
65
|
-
if (options.reasoning
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
if (options.reasoning) {
|
|
68
|
+
if (options.reasoning.level !== void 0) {
|
|
69
|
+
if (typeof options.reasoning.level !== "number" || options.reasoning.level < 0 || options.reasoning.level > 100) {
|
|
70
|
+
throw new Error("reasoning.level must be a number between 0 and 100");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (options.reasoning.effort && !["low", "medium", "high"].includes(options.reasoning.effort)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Invalid reasoning.effort '${options.reasoning.effort}'. Must be one of: low, medium, high`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (options.reasoning.level !== void 0 && options.reasoning.effort !== void 0) {
|
|
79
|
+
console.warn("Both reasoning.level and reasoning.effort provided. level takes precedence. effort is deprecated.");
|
|
80
|
+
}
|
|
69
81
|
}
|
|
70
82
|
if (options.recentImageThreshold !== void 0 && (options.recentImageThreshold <= 0 || !Number.isInteger(options.recentImageThreshold))) {
|
|
71
83
|
throw new Error("recentImageThreshold must be a positive integer");
|
|
@@ -164,7 +176,40 @@ function defineController(controller) {
|
|
|
164
176
|
function defineThreadEndpoint(handler) {
|
|
165
177
|
return handler;
|
|
166
178
|
}
|
|
179
|
+
|
|
180
|
+
// src/providers.ts
|
|
181
|
+
var ProviderError = class extends Error {
|
|
182
|
+
constructor(message, code, statusCode, retryAfter) {
|
|
183
|
+
super(message);
|
|
184
|
+
this.code = code;
|
|
185
|
+
this.statusCode = statusCode;
|
|
186
|
+
this.retryAfter = retryAfter;
|
|
187
|
+
this.name = "ProviderError";
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Whether this error is retryable
|
|
191
|
+
*/
|
|
192
|
+
get isRetryable() {
|
|
193
|
+
return this.code === "rate_limit" || this.code === "server_error" || this.code === "timeout";
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
function mapReasoningLevel(level, reasoningLevels) {
|
|
197
|
+
if (!reasoningLevels) return null;
|
|
198
|
+
const breakpoints = Object.keys(reasoningLevels).map(Number).sort((a, b) => a - b);
|
|
199
|
+
if (breakpoints.length === 0) return null;
|
|
200
|
+
let nearest = breakpoints[0];
|
|
201
|
+
let minDistance = Math.abs(level - nearest);
|
|
202
|
+
for (const bp of breakpoints) {
|
|
203
|
+
const distance = Math.abs(level - bp);
|
|
204
|
+
if (distance < minDistance) {
|
|
205
|
+
minDistance = distance;
|
|
206
|
+
nearest = bp;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return reasoningLevels[nearest];
|
|
210
|
+
}
|
|
167
211
|
export {
|
|
212
|
+
ProviderError,
|
|
168
213
|
defineAgent,
|
|
169
214
|
defineController,
|
|
170
215
|
defineEffect,
|
|
@@ -172,6 +217,7 @@ export {
|
|
|
172
217
|
defineModel,
|
|
173
218
|
definePrompt,
|
|
174
219
|
defineThreadEndpoint,
|
|
175
|
-
defineTool
|
|
220
|
+
defineTool,
|
|
221
|
+
mapReasoningLevel
|
|
176
222
|
};
|
|
177
223
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/models.ts","../src/tools.ts","../src/prompts.ts","../src/hooks.ts","../src/effects.ts","../src/agents.ts","../src/endpoints.ts"],"sourcesContent":["/**\n * Model definition types for Standard Agents.\n *\n * Models define LLM configurations including provider, model ID, pricing,\n * and fallback chains. Models are referenced by name from prompts.\n *\n * @module\n */\n\n/**\n * Supported LLM provider identifiers.\n *\n * Each provider requires a corresponding API key environment variable:\n * - `openai` → `OPENAI_API_KEY`\n * - `openrouter` → `OPENROUTER_API_KEY`\n * - `anthropic` → `ANTHROPIC_API_KEY`\n * - `google` → `GOOGLE_API_KEY`\n * - `test` → No API key required (for testing with scripted responses)\n */\nexport type ModelProvider = 'openai' | 'openrouter' | 'anthropic' | 'google' | 'test';\n\n/**\n * Model capability flags indicating supported features.\n */\nexport interface ModelCapabilities {\n /**\n * Whether the model supports vision (image understanding).\n * When true, image attachments will be sent to the model as part of the request.\n * Models like GPT-4o, Claude 3, and Gemini support vision.\n */\n vision?: boolean;\n\n /**\n * Whether the model supports function calling (tool use).\n * Most modern models support this, defaults to true if not specified.\n */\n functionCalling?: boolean;\n\n /**\n * Whether the model supports structured outputs (JSON mode).\n */\n structuredOutputs?: boolean;\n}\n\n/**\n * Model definition configuration.\n *\n * Defines an LLM model with its provider, pricing, capabilities, and fallback chain.\n *\n * @template N - The model name as a string literal type for type inference\n *\n * @example\n * ```typescript\n * import { defineModel } from '@standardagents/spec';\n *\n * export default defineModel({\n * name: 'gpt-4o',\n * provider: 'openai',\n * model: 'gpt-4o',\n * fallbacks: ['gpt-4-turbo', 'gpt-3.5-turbo'],\n * inputPrice: 2.5,\n * outputPrice: 10,\n * });\n * ```\n */\nexport interface ModelDefinition<N extends string = string> {\n /**\n * Unique name for this model definition.\n * Used as the identifier when referencing from prompts.\n * Should be descriptive and consistent (e.g., 'gpt-4o', 'claude-3-opus').\n */\n name: N;\n\n /**\n * The LLM provider to use for API calls.\n * The corresponding API key environment variable must be set.\n */\n provider: ModelProvider;\n\n /**\n * The actual model identifier sent to the provider API.\n *\n * For OpenAI: 'gpt-4o', 'gpt-4-turbo', 'gpt-3.5-turbo', etc.\n * For OpenRouter: 'openai/gpt-4o', 'anthropic/claude-3-opus', etc.\n * For Anthropic: 'claude-3-opus-20240229', 'claude-3-sonnet-20240229', etc.\n * For Google: 'gemini-1.5-pro', 'gemini-1.5-flash', etc.\n */\n model: string;\n\n /**\n * Optional list of additional provider prefixes for OpenRouter.\n * Allows routing through specific providers when using OpenRouter.\n *\n * @example ['anthropic', 'google'] - prefer Anthropic, fallback to Google\n */\n includedProviders?: string[];\n\n /**\n * Fallback model names to try if this model fails.\n * Referenced by model name (must be defined in agents/models/).\n * Tried in order after primary model exhausts retries.\n *\n * @example ['gpt-4', 'gpt-3.5-turbo']\n */\n fallbacks?: string[];\n\n /**\n * Cost per 1 million input tokens in USD.\n * Used for cost tracking and reporting in logs.\n */\n inputPrice?: number;\n\n /**\n * Cost per 1 million output tokens in USD.\n * Used for cost tracking and reporting in logs.\n */\n outputPrice?: number;\n\n /**\n * Cost per 1 million cached input tokens in USD.\n * Some providers offer reduced pricing for cached/repeated prompts.\n */\n cachedPrice?: number;\n\n /**\n * Model capabilities - features this model supports.\n */\n capabilities?: ModelCapabilities;\n}\n\n/**\n * Defines an LLM model configuration.\n *\n * Models are the foundation of the agent system - they specify which\n * AI model to use and how to connect to it. Models can have fallbacks\n * for reliability and include pricing for cost tracking.\n *\n * @template N - The model name as a string literal type\n * @param options - Model configuration options\n * @returns The model definition for registration\n *\n * @example\n * ```typescript\n * // agents/models/gpt_4o.ts\n * import { defineModel } from '@standardagents/spec';\n *\n * export default defineModel({\n * name: 'gpt-4o',\n * provider: 'openai',\n * model: 'gpt-4o',\n * fallbacks: ['gpt-4-turbo'],\n * inputPrice: 2.5,\n * outputPrice: 10,\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Using OpenRouter with provider preferences\n * export default defineModel({\n * name: 'claude-3-opus',\n * provider: 'openrouter',\n * model: 'anthropic/claude-3-opus',\n * includedProviders: ['anthropic'],\n * });\n * ```\n */\nexport function defineModel<N extends string>(\n options: ModelDefinition<N>\n): ModelDefinition<N> {\n // Validate required fields at runtime\n if (!options.name) {\n throw new Error('Model name is required');\n }\n if (!options.provider) {\n throw new Error('Model provider is required');\n }\n if (!options.model) {\n throw new Error('Model ID is required');\n }\n\n // Validate provider is a known type\n const validProviders: ModelProvider[] = ['openai', 'openrouter', 'anthropic', 'google', 'test'];\n if (!validProviders.includes(options.provider)) {\n throw new Error(\n `Invalid provider '${options.provider}'. Must be one of: ${validProviders.join(', ')}`\n );\n }\n\n // Validate pricing is non-negative if provided\n if (options.inputPrice !== undefined && options.inputPrice < 0) {\n throw new Error('inputPrice must be non-negative');\n }\n if (options.outputPrice !== undefined && options.outputPrice < 0) {\n throw new Error('outputPrice must be non-negative');\n }\n if (options.cachedPrice !== undefined && options.cachedPrice < 0) {\n throw new Error('cachedPrice must be non-negative');\n }\n\n return options;\n}\n","/**\n * Tool definition types for Standard Agents.\n *\n * Tools are callable functions that agents can invoke during execution.\n * They receive the current ThreadState and validated arguments,\n * and return results that are included in the conversation.\n *\n * @module\n */\n\nimport type { ThreadState } from './threads.js';\nimport type {\n ZodString,\n ZodNumber,\n ZodBoolean,\n ZodEnum,\n ZodArray,\n ZodObject,\n ZodOptional,\n ZodNullable,\n ZodUnion,\n ZodRecord,\n ZodNull,\n ZodLiteral,\n ZodDefault,\n} from 'zod';\nimport { z } from 'zod';\n\n/**\n * Text content item returned by tools.\n */\nexport interface TextContent {\n type: 'text';\n text: string;\n}\n\n/**\n * Image content item returned by tools.\n */\nexport interface ImageContent {\n type: 'image';\n /** Base64-encoded image data. */\n data: string;\n /** MIME type of the image (e.g., 'image/png', 'image/jpeg'). */\n mimeType: string;\n}\n\n/**\n * Content types that can be returned by tools.\n */\nexport type ToolContent = TextContent | ImageContent;\n\n/**\n * File attachment generated by a tool.\n *\n * Attachments are stored in the thread's file system and linked to\n * the tool result message. Unlike user uploads, tool attachments are\n * not subject to image downsampling.\n */\nexport interface ToolAttachment {\n /** File name for the attachment. */\n name: string;\n /** MIME type of the attachment. */\n mimeType: string;\n /** Base64-encoded file data. */\n data: string;\n /** Width in pixels (for images). */\n width?: number;\n /** Height in pixels (for images). */\n height?: number;\n}\n\n/**\n * Reference to a pre-existing attachment in the thread file system.\n */\nexport interface AttachmentRef {\n /** Unique identifier for the attachment. */\n id: string;\n /** Attachment type. */\n type: 'file';\n /** Path in the thread file system. */\n path: string;\n /** File name. */\n name: string;\n /** MIME type. */\n mimeType: string;\n /** File size in bytes. */\n size: number;\n /** Width in pixels (for images). */\n width?: number;\n /** Height in pixels (for images). */\n height?: number;\n /** AI-generated description. */\n description?: string;\n}\n\n/**\n * Result returned by a tool execution.\n */\nexport interface ToolResult {\n /** Status of the tool execution. */\n status: 'success' | 'error';\n /**\n * Text representation of the tool output.\n *\n * For tools that return structured content, this is derived by\n * concatenating all text parts. For simple tools, this is the\n * direct result string.\n */\n result?: string;\n /** Error message if status is 'error'. */\n error?: string;\n /** Stack trace for error debugging. */\n stack?: string;\n /**\n * File attachments returned by the tool.\n *\n * Can contain either:\n * - ToolAttachment: New files with base64 data to be stored\n * - AttachmentRef: References to existing files in the thread filesystem\n *\n * Implementations MUST store new attachments under /attachments/ directory.\n */\n attachments?: Array<ToolAttachment | AttachmentRef>;\n}\n\n// ============================================================================\n// Tool Argument Types (Zod-based)\n// ============================================================================\n\n/** Decrement helper to limit recursion depth. */\ntype Dec<N extends number> = N extends 10\n ? 9\n : N extends 9\n ? 8\n : N extends 8\n ? 7\n : N extends 7\n ? 6\n : N extends 6\n ? 5\n : N extends 5\n ? 4\n : N extends 4\n ? 3\n : N extends 3\n ? 2\n : N extends 2\n ? 1\n : N extends 1\n ? 0\n : 0;\n\n/**\n * Allowed Zod types for tool argument nodes.\n *\n * This is the single source of truth for which Zod types can be used\n * in tool argument schemas. The depth parameter limits recursion to\n * prevent infinite type expansion.\n *\n * @template D - Maximum nesting depth (default: 7)\n */\nexport type ToolArgsNode<D extends number = 7> =\n // Primitives and literals\n | ZodString\n | ZodNumber\n | ZodBoolean\n | ZodNull\n | ZodLiteral<string | number | boolean | null>\n // Enums (Zod v4 uses Record<string, string> for enum type parameter)\n | ZodEnum<Readonly<Record<string, string>>>\n // Wrappers (with depth check)\n | (D extends 0 ? never : ZodOptional<ToolArgsNode<Dec<D>>>)\n | (D extends 0 ? never : ZodNullable<ToolArgsNode<Dec<D>>>)\n | (D extends 0 ? never : ZodDefault<ToolArgsNode<Dec<D>>>)\n // Arrays (with depth check)\n | (D extends 0 ? never : ZodArray<ToolArgsNode<Dec<D>>>)\n // Objects and records (with depth check)\n | (D extends 0 ? never : ZodObject<Record<string, ToolArgsNode<Dec<D>>>>)\n | (D extends 0 ? never : ZodRecord<ZodString, ToolArgsNode<Dec<D>>>)\n // Unions (with depth check)\n | (D extends 0\n ? never\n : ZodUnion<\n [\n ToolArgsNode<Dec<D>>,\n ToolArgsNode<Dec<D>>,\n ...ToolArgsNode<Dec<D>>[],\n ]\n >);\n\n/**\n * Raw shape for a Zod object schema containing tool argument nodes.\n *\n * @template D - Maximum nesting depth (default: 7)\n */\nexport type ToolArgsRawShape<D extends number = 7> = Record<\n string,\n ToolArgsNode<D>\n>;\n\n/**\n * Top-level tool argument schema.\n *\n * Tool arguments MUST be defined as a Zod object schema.\n * This is required for compatibility with OpenAI's function calling API.\n *\n * @template D - Maximum nesting depth (default: 7)\n */\nexport type ToolArgs<D extends number = 7> = z.ZodObject<ToolArgsRawShape<D>>;\n\n\n// ============================================================================\n// Tool Function Types\n// ============================================================================\n\n/**\n * Tool function signature.\n *\n * Tools are async functions that receive a ThreadState and\n * optionally validated arguments, returning a ToolResult.\n *\n * @template State - The state type (defaults to ThreadState)\n * @template Args - The Zod schema for tool arguments, or null for no args\n */\nexport type Tool<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n> = Args extends ToolArgs\n ? (state: State, args: z.infer<Args>) => Promise<ToolResult>\n : (state: State) => Promise<ToolResult>;\n\n/**\n * Return type of defineTool function.\n *\n * A tuple containing:\n * - Tool description string\n * - Argument schema (or null)\n * - Tool implementation function\n */\nexport type ToolDefinition<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n> = [string, Args, Tool<State, Args>];\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Define a tool with arguments.\n *\n * @param toolDescription - Description of what the tool does (shown to the LLM)\n * @param args - Zod schema for validating tool arguments\n * @param tool - The tool implementation function\n * @returns A tuple containing the tool definition\n */\nexport function defineTool<State = ThreadState, Args extends ToolArgs = ToolArgs>(\n toolDescription: string,\n args: Args,\n tool: Tool<State, Args>\n): ToolDefinition<State, Args>;\n\n/**\n * Define a tool without arguments.\n *\n * @param toolDescription - Description of what the tool does (shown to the LLM)\n * @param tool - The tool implementation function\n * @returns A tuple containing the tool definition\n */\nexport function defineTool<State = ThreadState>(\n toolDescription: string,\n tool: Tool<State, null>\n): ToolDefinition<State, null>;\n\n/**\n * Defines a tool that agents can call during execution.\n *\n * Tools are the primary way agents interact with external systems.\n * Each tool has a description (shown to the LLM), optional argument\n * schema (validated at runtime), and an implementation function.\n *\n * @example\n * ```typescript\n * import { defineTool } from '@standardagents/spec';\n * import { z } from 'zod';\n *\n * export default defineTool(\n * 'Search the knowledge base for relevant information',\n * z.object({\n * query: z.string().describe('Search query'),\n * limit: z.number().optional().describe('Max results'),\n * }),\n * async (state, args) => {\n * const results = await search(args.query, args.limit);\n * return {\n * status: 'success',\n * result: JSON.stringify(results),\n * };\n * }\n * );\n * ```\n */\nexport function defineTool<\n State = ThreadState,\n Args extends ToolArgs = ToolArgs,\n>(\n toolDescription: string,\n argsOrTool: Args | Tool<State, null>,\n maybeTool?: Tool<State, Args>\n): ToolDefinition<State, Args | null> {\n if (maybeTool) {\n return [\n toolDescription,\n argsOrTool as Args,\n maybeTool,\n ];\n }\n return [\n toolDescription,\n null,\n argsOrTool as Tool<State, null>,\n ];\n}\n","/**\n * Prompt definition types for Standard Agents.\n *\n * Prompts define LLM interaction configurations including the system prompt,\n * model selection, available tools, and various behavioral options.\n *\n * @module\n */\n\nimport type { z } from 'zod';\nimport type { ToolArgs } from './tools.js';\n\n// ============================================================================\n// Structured Prompt Types\n// ============================================================================\n\n/**\n * A text part of a prompt - static text content.\n *\n * @example\n * ```typescript\n * { type: 'text', content: 'You are a helpful assistant.\\n\\n' }\n * ```\n */\nexport interface PromptTextPart {\n type: 'text';\n /** The text content */\n content: string;\n}\n\n/**\n * A prompt inclusion part - includes another prompt's content.\n *\n * @example\n * ```typescript\n * { type: 'include', prompt: 'responder_rules' }\n * ```\n */\nexport interface PromptIncludePart {\n type: 'include';\n /** The name of the prompt to include */\n prompt: string;\n}\n\n/**\n * A single part of a structured prompt.\n * Discriminated union on `type` field for TypeScript narrowing.\n */\nexport type PromptPart = PromptTextPart | PromptIncludePart;\n\n/**\n * A structured prompt is an array of prompt parts.\n * Provides composition with other prompts via includes.\n *\n * @example\n * ```typescript\n * prompt: [\n * { type: 'text', content: 'You are a helpful assistant.\\n\\n' },\n * { type: 'include', prompt: 'common_rules' },\n * { type: 'text', content: '\\n\\nBe concise.' },\n * ]\n * ```\n */\nexport type StructuredPrompt = PromptPart[];\n\n/**\n * The prompt content can be either a plain string or a structured array.\n *\n * @example\n * ```typescript\n * // Simple string prompt:\n * prompt: 'You are a helpful assistant.'\n *\n * // Structured prompt with includes:\n * prompt: [\n * { type: 'text', content: 'You are a helpful assistant.\\n\\n' },\n * { type: 'include', prompt: 'common_rules' },\n * ]\n * ```\n */\nexport type PromptContent = string | StructuredPrompt;\n\n// ============================================================================\n// Sub-Prompt Configuration\n// ============================================================================\n\n/**\n * Configuration for sub-prompts used as tools.\n * These options control how results from sub-prompts are returned to the caller.\n *\n * @template T - The sub-prompt name type (for type-safe references)\n */\nexport interface SubpromptConfig<T extends string = string> {\n /**\n * Name of the sub-prompt to call.\n * Must be a prompt defined in agents/prompts/.\n */\n name: T;\n\n /**\n * Include text response content from sub-prompt execution in the result string.\n * @default true\n */\n includeTextResponse?: boolean;\n\n /**\n * Serialize tool calls made by the sub-prompt (and their results) into the result string.\n * @default true\n */\n includeToolCalls?: boolean;\n\n /**\n * Serialize any errors from the sub-prompt into the result string.\n * @default true\n */\n includeErrors?: boolean;\n\n /**\n * Property from the tool call arguments to use as the initial user message\n * when invoking the sub-prompt.\n *\n * @example\n * If the tool is called with `{ query: \"search term\", limit: 10 }` and\n * `initUserMessageProperty: 'query'`, the sub-prompt will receive\n * \"search term\" as the initial user message.\n */\n initUserMessageProperty?: string;\n}\n\n/**\n * @deprecated Use SubpromptConfig instead\n */\nexport type ToolConfig<T extends string = string> = SubpromptConfig<T>;\n\n// ============================================================================\n// Reasoning Configuration\n// ============================================================================\n\n/**\n * Reasoning configuration for models that support extended thinking.\n * Applies to models like OpenAI o1, Anthropic Claude with extended thinking,\n * Google Gemini with thinking, and Qwen with reasoning.\n */\nexport interface ReasoningConfig {\n /**\n * Effort level for reasoning models.\n * Higher effort = more thinking tokens = potentially better results.\n *\n * - `low`: Minimal reasoning, faster responses\n * - `medium`: Balanced reasoning and speed\n * - `high`: Maximum reasoning, slower but more thorough\n *\n * @default undefined (use model defaults)\n */\n effort?: 'low' | 'medium' | 'high';\n\n /**\n * Maximum tokens to allocate for reasoning.\n * Applies to models that support token limits on reasoning.\n */\n maxTokens?: number;\n\n /**\n * Use reasoning internally but exclude from the response.\n * Model thinks through the problem but only returns the final answer.\n * Useful for cleaner outputs while maintaining reasoning quality.\n */\n exclude?: boolean;\n\n /**\n * Include reasoning content in the message history for multi-turn context.\n * When true, reasoning is preserved and visible to subsequent turns.\n * @default false\n */\n include?: boolean;\n}\n\n// ============================================================================\n// Prompt Definition\n// ============================================================================\n\n/**\n * Prompt definition configuration.\n *\n * @template N - The prompt name as a string literal type\n * @template S - The Zod schema type for requiredSchema (inferred automatically)\n *\n * @example\n * ```typescript\n * import { definePrompt } from '@standardagents/spec';\n * import { z } from 'zod';\n *\n * export default definePrompt({\n * name: 'customer_support',\n * toolDescription: 'Handle customer support inquiries',\n * model: 'gpt-4o',\n * prompt: 'You are a helpful customer support agent.',\n * tools: ['search_knowledge_base', 'create_ticket'],\n * requiredSchema: z.object({\n * query: z.string().describe('The customer inquiry'),\n * }),\n * });\n * ```\n */\nexport interface PromptDefinition<\n N extends string = string,\n S extends ToolArgs = ToolArgs,\n> {\n /**\n * Unique name for this prompt.\n * Used as the identifier when referencing from agents or as a tool.\n * Should be snake_case (e.g., 'customer_support', 'data_analyst').\n */\n name: N;\n\n /**\n * Description shown when this prompt is exposed as a tool.\n * Should clearly describe what this prompt does for LLM tool selection.\n */\n toolDescription: string;\n\n /**\n * The system prompt content sent to the LLM.\n * Can be either a plain string or a structured array for composition.\n */\n prompt: PromptContent;\n\n /**\n * Model to use for this prompt.\n * Must reference a model defined in agents/models/.\n */\n model: string;\n\n /**\n * Include full chat history in the LLM context.\n * @default false\n */\n includeChat?: boolean;\n\n /**\n * Include results from past tool calls in the LLM context.\n * @default false\n */\n includePastTools?: boolean;\n\n /**\n * Allow parallel execution of multiple tool calls.\n * @default false\n */\n parallelToolCalls?: boolean;\n\n /**\n * Tool calling strategy for the LLM.\n *\n * - `auto`: Model decides when to call tools (default)\n * - `none`: Disable tool calling entirely\n * - `required`: Force the model to call at least one tool\n *\n * @default 'auto'\n */\n toolChoice?: 'auto' | 'none' | 'required';\n\n /**\n * Zod schema for validating inputs when this prompt is called as a tool.\n */\n requiredSchema?: S;\n\n /**\n * Tools available to this prompt.\n * Can be simple tool names or sub-prompt configurations.\n * To enable handoffs, include ai_human agent names in this array.\n */\n tools?: (string | SubpromptConfig)[];\n\n /**\n * Reasoning configuration for models that support extended thinking.\n */\n reasoning?: ReasoningConfig;\n\n /**\n * Number of recent messages to keep actual images for in context.\n * @default 10\n */\n recentImageThreshold?: number;\n}\n\n/**\n * Helper type to extract the inferred input type from a prompt's Zod schema.\n *\n * @template T - The prompt definition type\n */\nexport type PromptInput<T extends PromptDefinition<string, ToolArgs>> =\n T['requiredSchema'] extends ToolArgs\n ? z.infer<T['requiredSchema']>\n : never;\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Defines a prompt configuration for LLM interactions.\n *\n * Prompts are the primary way to configure how agents interact with LLMs.\n * They specify the system prompt, available tools, input validation,\n * and various behavioral options.\n *\n * @template N - The prompt name as a string literal type\n * @template S - The Zod schema type for requiredSchema\n * @param options - Prompt configuration options\n * @returns The prompt definition for registration\n *\n * @example\n * ```typescript\n * import { definePrompt } from '@standardagents/spec';\n * import { z } from 'zod';\n *\n * export default definePrompt({\n * name: 'customer_support',\n * toolDescription: 'Handle customer support inquiries',\n * model: 'gpt-4o',\n * prompt: 'You are a helpful customer support agent.',\n * tools: ['search_knowledge_base', 'create_ticket'],\n * includeChat: true,\n * requiredSchema: z.object({\n * query: z.string().describe('The customer inquiry'),\n * }),\n * });\n * ```\n */\nexport function definePrompt<N extends string, S extends ToolArgs = never>(\n options: PromptDefinition<N, S>\n): PromptDefinition<N, S> {\n // Validate required fields at runtime\n if (!options.name) {\n throw new Error('Prompt name is required');\n }\n if (!options.toolDescription) {\n throw new Error('Prompt toolDescription is required');\n }\n if (!options.model) {\n throw new Error('Prompt model is required');\n }\n if (!options.prompt) {\n throw new Error('Prompt content is required');\n }\n\n // Validate toolChoice is a known value\n if (\n options.toolChoice &&\n !['auto', 'none', 'required'].includes(options.toolChoice)\n ) {\n throw new Error(\n `Invalid toolChoice '${options.toolChoice}'. Must be one of: auto, none, required`\n );\n }\n\n // Validate reasoning effort is a known value\n if (\n options.reasoning?.effort &&\n !['low', 'medium', 'high'].includes(options.reasoning.effort)\n ) {\n throw new Error(\n `Invalid reasoning.effort '${options.reasoning.effort}'. Must be one of: low, medium, high`\n );\n }\n\n // Validate recentImageThreshold is a positive number\n if (\n options.recentImageThreshold !== undefined &&\n (options.recentImageThreshold <= 0 ||\n !Number.isInteger(options.recentImageThreshold))\n ) {\n throw new Error('recentImageThreshold must be a positive integer');\n }\n\n return options;\n}\n","/**\n * Hook definition types for Standard Agents.\n *\n * Hooks allow intercepting and modifying agent behavior at key points\n * in the execution lifecycle. They enable logging, validation,\n * transformation, and side effects.\n *\n * Hooks receive ThreadState as their first parameter, providing full\n * access to thread operations and execution state.\n *\n * @module\n */\n\nimport type { ThreadState, Message } from './threads.js';\n\n// ============================================================================\n// Hook Context Types\n// ============================================================================\n\n/**\n * Hook context is ThreadState.\n *\n * Hooks receive the full ThreadState, which includes identity, message access,\n * resource loading, event emission, and execution state (when available).\n *\n * @example\n * ```typescript\n * const hook = defineHook('filter_messages', async (state, messages) => {\n * console.log(`Thread: ${state.threadId}`);\n * if (state.execution) {\n * console.log(`Turn: ${state.execution.turnCount}`);\n * }\n * return messages.slice(-10);\n * });\n * ```\n */\nexport type HookContext = ThreadState;\n\n/**\n * Message structure for hook processing.\n *\n * Re-exported from threads.ts for convenience.\n * @see Message\n */\nexport type HookMessage = Message;\n\n/**\n * Tool call structure for hook processing.\n */\nexport interface HookToolCall {\n /** Unique tool call identifier */\n id: string;\n /** Always 'function' for tool calls */\n type: 'function';\n /** Function details */\n function: {\n /** Tool name */\n name: string;\n /** JSON-encoded arguments */\n arguments: string;\n };\n}\n\n/**\n * Tool result structure for hook processing.\n */\nexport interface HookToolResult {\n /** Execution status */\n status: 'success' | 'error';\n /** Result string (for successful executions) */\n result?: string;\n /** Error message (for failed executions) */\n error?: string;\n /** Stack trace (for debugging) */\n stack?: string;\n}\n\n/**\n * LLM message format for prefilter hook.\n */\nexport interface LLMMessage {\n /** Message role */\n role: string;\n /** Message content */\n content: string | null;\n /** Tool calls (parsed) */\n tool_calls?: unknown;\n /** Tool call ID */\n tool_call_id?: string;\n /** Tool name */\n name?: string;\n}\n\n// ============================================================================\n// Hook Signatures\n// ============================================================================\n\n/**\n * Hook signatures for all available hooks.\n *\n * Each hook has a specific signature that defines when it's called\n * and what data it receives. Hooks can modify data by returning\n * transformed values or perform side effects.\n *\n * All hooks receive ThreadState as their first parameter.\n *\n * @template State - The state type (defaults to ThreadState)\n * @template Msg - The message type (defaults to HookMessage)\n * @template ToolCall - The tool call type (defaults to HookToolCall)\n * @template ToolResult - The tool result type (defaults to HookToolResult)\n */\nexport interface HookSignatures<\n State = ThreadState,\n Msg = HookMessage,\n ToolCall = HookToolCall,\n ToolResult = HookToolResult,\n> {\n /**\n * Called before messages are filtered and sent to the LLM.\n *\n * Receives raw message rows from storage before any transformation.\n * Use for filtering, sorting, or augmenting message history.\n *\n * @param state - Thread state\n * @param messages - Array of messages from storage\n * @returns Modified message array\n *\n * @example\n * ```typescript\n * defineHook('filter_messages', async (state, messages) => {\n * // Only include messages from last 10 turns\n * return messages.slice(-10);\n * });\n * ```\n */\n filter_messages: (state: State, messages: Msg[]) => Promise<Msg[]>;\n\n /**\n * Called after message history is loaded and before sending to LLM.\n *\n * Receives messages already transformed into chat completion format.\n * Use for final adjustments before LLM request.\n *\n * @param state - Thread state\n * @param messages - Array of LLM-formatted messages\n * @returns Modified LLM message array\n *\n * @example\n * ```typescript\n * defineHook('prefilter_llm_history', async (state, messages) => {\n * // Add a reminder to the last user message\n * return messages;\n * });\n * ```\n */\n prefilter_llm_history: (\n state: State,\n messages: LLMMessage[]\n ) => Promise<LLMMessage[]>;\n\n /**\n * Called before a message is created in the database.\n *\n * Receives the message data before insertion. Return modified data\n * to transform the message before storage.\n *\n * @param state - Thread state\n * @param message - Message data to be created\n * @returns Modified message data\n *\n * @example\n * ```typescript\n * defineHook('before_create_message', async (state, message) => {\n * // Add metadata to all messages\n * return { ...message, metadata: { processed: true } };\n * });\n * ```\n */\n before_create_message: (\n state: State,\n message: Record<string, unknown>\n ) => Promise<Record<string, unknown>>;\n\n /**\n * Called after a message is created in the database.\n *\n * Use for logging, analytics, or triggering side effects after\n * message creation. Cannot modify the message.\n *\n * @param state - Thread state\n * @param message - The created message data\n *\n * @example\n * ```typescript\n * defineHook('after_create_message', async (state, message) => {\n * console.log(`Message created: ${message.id}`);\n * });\n * ```\n */\n after_create_message: (\n state: State,\n message: Record<string, unknown>\n ) => Promise<void>;\n\n /**\n * Called before a message is updated in the database.\n *\n * Receives the message ID and update data. Return modified updates\n * to transform the changes before storage.\n *\n * @param state - Thread state\n * @param messageId - ID of message being updated\n * @param updates - Update data\n * @returns Modified update data\n *\n * @example\n * ```typescript\n * defineHook('before_update_message', async (state, messageId, updates) => {\n * return { ...updates, updated_at: Date.now() };\n * });\n * ```\n */\n before_update_message: (\n state: State,\n messageId: string,\n updates: Record<string, unknown>\n ) => Promise<Record<string, unknown>>;\n\n /**\n * Called after a message is updated in the database.\n *\n * Use for logging, analytics, or triggering side effects after\n * message update. Cannot modify the message.\n *\n * @param state - Thread state\n * @param message - The updated message\n *\n * @example\n * ```typescript\n * defineHook('after_update_message', async (state, message) => {\n * console.log(`Message updated: ${message.id}`);\n * });\n * ```\n */\n after_update_message: (state: State, message: Msg) => Promise<void>;\n\n /**\n * Called before a tool result is stored in the database.\n *\n * Receives the tool call and result before storage. Return modified\n * result to transform before storage.\n *\n * @param state - Thread state\n * @param toolCall - The tool call that was executed\n * @param toolResult - The result from tool execution\n * @returns Modified tool result data\n *\n * @example\n * ```typescript\n * defineHook('before_store_tool_result', async (state, toolCall, toolResult) => {\n * // Sanitize sensitive data from results\n * return { ...toolResult, result: sanitize(toolResult.result) };\n * });\n * ```\n */\n before_store_tool_result: (\n state: State,\n toolCall: Record<string, unknown>,\n toolResult: Record<string, unknown>\n ) => Promise<Record<string, unknown>>;\n\n /**\n * Called after a successful tool call.\n *\n * Receives the tool call and its result. Can return a modified result\n * or null to use the original. Use for logging, transformation, or\n * post-processing of successful tool executions.\n *\n * @param state - Thread state\n * @param toolCall - The executed tool call\n * @param toolResult - The successful result\n * @returns Modified result or null for original\n *\n * @example\n * ```typescript\n * defineHook('after_tool_call_success', async (state, toolCall, toolResult) => {\n * console.log(`Tool ${toolCall.function.name} succeeded`);\n * return null; // Use original result\n * });\n * ```\n */\n after_tool_call_success: (\n state: State,\n toolCall: ToolCall,\n toolResult: ToolResult\n ) => Promise<ToolResult | null>;\n\n /**\n * Called after a failed tool call.\n *\n * Receives the tool call and error result. Can return a modified result\n * or null to use the original. Use for error handling, logging, or\n * recovery attempts.\n *\n * @param state - Thread state\n * @param toolCall - The failed tool call\n * @param toolResult - The error result\n * @returns Modified result or null for original\n *\n * @example\n * ```typescript\n * defineHook('after_tool_call_failure', async (state, toolCall, toolResult) => {\n * console.error(`Tool ${toolCall.function.name} failed: ${toolResult.error}`);\n * return null; // Use original error\n * });\n * ```\n */\n after_tool_call_failure: (\n state: State,\n toolCall: ToolCall,\n toolResult: ToolResult\n ) => Promise<ToolResult | null>;\n}\n\n/**\n * Valid hook names.\n */\nexport type HookName = keyof HookSignatures;\n\n// ============================================================================\n// Hook Definition\n// ============================================================================\n\n/**\n * Hook definition tuple.\n *\n * A hook definition is a tuple containing the hook name and implementation.\n *\n * @template K - The hook name\n * @template State - The state type (defaults to ThreadState)\n * @template Msg - The message type\n * @template ToolCall - The tool call type\n * @template ToolResult - The tool result type\n */\nexport type HookDefinition<\n K extends HookName = HookName,\n State = ThreadState,\n Msg = HookMessage,\n ToolCall = HookToolCall,\n ToolResult = HookToolResult,\n> = [K, HookSignatures<State, Msg, ToolCall, ToolResult>[K]];\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Define a hook with strict typing based on hook name.\n *\n * Hooks intercept specific points in the agent execution lifecycle.\n * The hook name determines the function signature and when it's called.\n * All hooks receive ThreadState as their first parameter.\n *\n * @template K - The hook name (determines signature)\n * @param hookName - Name of the hook to define\n * @param implementation - Hook implementation function\n * @returns The implementation function (for registration)\n *\n * @example\n * ```typescript\n * // Filter messages to last 10\n * export default defineHook('filter_messages', async (state, messages) => {\n * return messages.slice(-10);\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Log all tool successes\n * export default defineHook('after_tool_call_success', async (state, toolCall, result) => {\n * console.log(`Tool ${toolCall.function.name} completed`);\n * return null; // Use original result\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Transform messages before LLM\n * export default defineHook('prefilter_llm_history', async (state, messages) => {\n * // Add instruction to last message\n * const last = messages[messages.length - 1];\n * if (last?.role === 'user' && typeof last.content === 'string') {\n * last.content += '\\n\\nRemember to be concise.';\n * }\n * return messages;\n * });\n * ```\n */\nexport function defineHook<K extends keyof HookSignatures>(\n hookName: K,\n implementation: HookSignatures[K]\n): HookSignatures[K] {\n // Validate hook name at runtime\n const validHooks: HookName[] = [\n 'filter_messages',\n 'prefilter_llm_history',\n 'before_create_message',\n 'after_create_message',\n 'before_update_message',\n 'after_update_message',\n 'before_store_tool_result',\n 'after_tool_call_success',\n 'after_tool_call_failure',\n ];\n\n if (!validHooks.includes(hookName)) {\n throw new Error(\n `Invalid hook name '${hookName}'. Valid hooks: ${validHooks.join(', ')}`\n );\n }\n\n return implementation;\n}\n","/**\n * Effect definition types for Standard Agents.\n *\n * Effects are scheduled operations that run outside the tool execution context.\n * They enable implementation-specific side effects with optional delay support,\n * leveraging the runtime's scheduling mechanism (e.g., Cloudflare Durable Object alarms).\n *\n * Unlike tools, effects:\n * - Do not return results to the LLM\n * - Can be delayed for future execution\n * - Run independently of the conversation flow\n * - Are ideal for notifications, cleanup, and async operations\n *\n * @module\n */\n\nimport type { z } from 'zod';\nimport type { ThreadState } from './threads.js';\nimport type { ToolArgs } from './tools.js';\n\n// ============================================================================\n// Effect Function Types\n// ============================================================================\n\n/**\n * Effect function signature.\n *\n * Effects are async functions that receive a ThreadState and\n * optionally validated arguments. Unlike tools, they return void\n * since results are not included in the conversation.\n *\n * @template State - The state type (defaults to ThreadState)\n * @template Args - The Zod schema for effect arguments, or null for no args\n */\nexport type Effect<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n> = Args extends ToolArgs\n ? (state: State, args: z.infer<Args>) => Promise<void> | void\n : (state: State) => Promise<void> | void;\n\n/**\n * Return type of defineEffect function.\n *\n * A tuple containing:\n * - Effect description string\n * - Argument schema (or null)\n * - Effect implementation function\n */\nexport type EffectDefinition<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n> = [string, Args, Effect<State, Args>];\n\n// ============================================================================\n// Scheduled Effect Types\n// ============================================================================\n\n/**\n * Record of a scheduled effect.\n *\n * Returned by getScheduledEffects() to inspect pending effects.\n */\nexport interface ScheduledEffect {\n /** Unique identifier for this scheduled effect. */\n id: string;\n /** Name of the effect to execute. */\n name: string;\n /** Arguments to pass to the effect handler. */\n args: Record<string, unknown>;\n /** When the effect is scheduled to run (microseconds since epoch). */\n scheduledAt: number;\n /** When the effect was created (microseconds since epoch). */\n createdAt: number;\n}\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Define an effect with arguments.\n *\n * @param description - Description of what the effect does\n * @param args - Zod schema for validating effect arguments\n * @param handler - The effect implementation function\n * @returns A tuple containing the effect definition\n */\nexport function defineEffect<State = ThreadState, Args extends ToolArgs = ToolArgs>(\n description: string,\n args: Args,\n handler: Effect<State, Args>\n): EffectDefinition<State, Args>;\n\n/**\n * Define an effect without arguments.\n *\n * @param description - Description of what the effect does\n * @param handler - The effect implementation function\n * @returns A tuple containing the effect definition\n */\nexport function defineEffect<State = ThreadState>(\n description: string,\n handler: Effect<State, null>\n): EffectDefinition<State, null>;\n\n/**\n * Defines an effect that can be scheduled for execution.\n *\n * Effects are ideal for operations that should happen outside the\n * main conversation flow, such as:\n * - Sending notification emails after a delay\n * - Triggering webhooks\n * - Scheduling cleanup tasks\n * - Running background data processing\n *\n * @example\n * ```typescript\n * import { defineEffect } from '@standardagents/spec';\n * import { z } from 'zod';\n *\n * export default defineEffect(\n * 'Send a reminder email after a delay',\n * z.object({\n * to: z.string().email().describe('Recipient email'),\n * subject: z.string().describe('Email subject'),\n * body: z.string().describe('Email body'),\n * }),\n * async (state, args) => {\n * const env = state._notPackableRuntimeContext?.env as any;\n * await env.EMAIL_SERVICE.send({\n * to: args.to,\n * subject: args.subject,\n * body: args.body,\n * });\n * }\n * );\n * ```\n *\n * @example\n * ```typescript\n * // Schedule an effect from a tool\n * const effectId = state.scheduleEffect(\n * 'send_reminder_email',\n * { to: 'user@example.com', subject: 'Reminder', body: 'Hello!' },\n * 30 * 60 * 1000 // 30 minutes\n * );\n * ```\n */\nexport function defineEffect<\n State = ThreadState,\n Args extends ToolArgs = ToolArgs,\n>(\n description: string,\n argsOrHandler: Args | Effect<State, null>,\n maybeHandler?: Effect<State, Args>\n): EffectDefinition<State, Args | null> {\n if (maybeHandler) {\n return [\n description,\n argsOrHandler as Args,\n maybeHandler,\n ];\n }\n return [\n description,\n null,\n argsOrHandler as Effect<State, null>,\n ];\n}\n","/**\n * Agent definition types for Standard Agents.\n *\n * Agents orchestrate conversations between AI models (dual_ai) or between\n * AI and human users (ai_human). They define the prompts, stop conditions,\n * and behavioral rules for each side of the conversation.\n *\n * @module\n */\n\n// ============================================================================\n// Agent Types\n// ============================================================================\n\n/**\n * Agent conversation type.\n *\n * - `ai_human`: AI conversing with a human user (most common)\n * - `dual_ai`: Two AI participants conversing with each other\n */\nexport type AgentType = 'ai_human' | 'dual_ai';\n\n// ============================================================================\n// Side Configuration\n// ============================================================================\n\n/**\n * Configuration for one side of an agent conversation.\n *\n * Each side has a prompt, stop conditions, and turn limits.\n * For `ai_human` agents, only sideA (the AI) needs configuration.\n * For `dual_ai` agents, both sides need configuration.\n *\n * @template Prompt - The prompt reference type (string or type-safe union)\n * @template Callable - The callable reference type (string or type-safe union)\n *\n * @example\n * ```typescript\n * const sideConfig: SideConfig = {\n * label: 'Support Agent',\n * prompt: 'customer_support',\n * stopOnResponse: true,\n * maxSteps: 10,\n * };\n * ```\n */\nexport interface SideConfig<\n Prompt extends string = string,\n Callable extends string = string,\n> {\n /**\n * Custom label for this side of the conversation.\n * Used in UI and logs for clarity.\n *\n * @example 'Support Agent', 'Customer', 'ATC', 'Pilot'\n */\n label?: string;\n\n /**\n * The prompt to use for this side.\n * Must reference a prompt defined in agents/prompts/.\n */\n prompt: Prompt;\n\n /**\n * Stop this side's turn when it returns a text response (no tool calls).\n * When true, the side's turn ends after producing a message without tools.\n * @default true\n */\n stopOnResponse?: boolean;\n\n /**\n * Stop this side's turn when a specific tool is called.\n * Overrides stopOnResponse when the named tool is invoked.\n * Requires stopToolResponseProperty to extract the result.\n */\n stopTool?: Callable;\n\n /**\n * Property to extract from the stop tool's result.\n * Required when stopTool is set.\n * The extracted value is used to determine the conversation outcome.\n */\n stopToolResponseProperty?: string;\n\n /**\n * Maximum steps for this side before forcing a stop.\n * Safety limit to prevent runaway execution.\n * A step is one complete LLM request/response cycle.\n */\n maxSteps?: number;\n\n /**\n * Tool that ends the entire session when called.\n * Different from stopTool - this ends the session for both sides,\n * not just this side's turn.\n */\n endSessionTool?: Callable;\n}\n\n// ============================================================================\n// Agent Definition\n// ============================================================================\n\n/**\n * Agent definition configuration.\n *\n * @template N - The agent name as a string literal type\n * @template Prompt - The prompt reference type (string or type-safe union)\n * @template Callable - The callable reference type (string or type-safe union)\n *\n * @example\n * ```typescript\n * import { defineAgent } from '@standardagents/spec';\n *\n * export default defineAgent({\n * name: 'support_agent',\n * title: 'Customer Support Agent',\n * type: 'ai_human',\n * sideA: {\n * label: 'Support',\n * prompt: 'customer_support',\n * stopOnResponse: true,\n * },\n * });\n * ```\n */\nexport interface AgentDefinition<\n N extends string = string,\n Prompt extends string = string,\n Callable extends string = string,\n> {\n /**\n * Unique name for this agent.\n * Used as the identifier for thread creation and handoffs.\n * Should be snake_case (e.g., 'support_agent', 'research_flow').\n */\n name: N;\n\n /**\n * Human-readable title for the agent.\n * Optional - if not provided, the name will be used.\n * @deprecated Use name instead. Title will be removed in a future version.\n */\n title?: string;\n\n /**\n * Agent conversation type.\n *\n * - `ai_human`: AI conversing with a human user (default)\n * - `dual_ai`: Two AI participants conversing\n *\n * @default 'ai_human'\n */\n type?: AgentType;\n\n /**\n * Maximum total turns across both sides.\n * Only applies to `dual_ai` agents.\n * Prevents infinite loops in AI-to-AI conversations.\n */\n maxSessionTurns?: number;\n\n /**\n * Configuration for Side A.\n * For `ai_human`: This is the AI side.\n * For `dual_ai`: This is the first AI participant.\n */\n sideA: SideConfig<Prompt, Callable>;\n\n /**\n * Configuration for Side B.\n * For `ai_human`: Optional, the human side doesn't need config.\n * For `dual_ai`: Required, the second AI participant.\n */\n sideB?: SideConfig<Prompt, Callable>;\n\n /**\n * Expose this agent as a tool for other prompts.\n * Enables agent composition and handoffs.\n * When true, other prompts can invoke this agent as a tool.\n * @default false\n */\n exposeAsTool?: boolean;\n\n /**\n * Description shown when agent is used as a tool.\n * Required if exposeAsTool is true.\n * Should clearly describe what this agent does.\n */\n toolDescription?: string;\n\n /**\n * Brief description of what this agent does.\n * Useful for UIs and documentation.\n *\n * @example 'Handles customer support inquiries and resolves issues'\n */\n description?: string;\n\n /**\n * Icon URL or absolute path for the agent.\n * Absolute paths (starting with `/`) are converted to full URLs in API responses.\n *\n * @example 'https://example.com/icon.svg' or '/icons/support.svg'\n */\n icon?: string;\n}\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Defines an agent configuration.\n *\n * Agents orchestrate conversations between AI models, or between AI and\n * human users. They use prompts to configure each side of the conversation\n * and define stop conditions to control conversation flow.\n *\n * @template N - The agent name as a string literal type\n * @param options - Agent configuration options\n * @returns The agent definition for registration\n *\n * @example\n * ```typescript\n * // agents/agents/support_agent.ts\n * import { defineAgent } from '@standardagents/spec';\n *\n * export default defineAgent({\n * name: 'support_agent',\n * title: 'Customer Support Agent',\n * type: 'ai_human',\n * sideA: {\n * label: 'Support',\n * prompt: 'customer_support',\n * stopOnResponse: true,\n * endSessionTool: 'close_ticket',\n * },\n * exposeAsTool: true,\n * toolDescription: 'Hand off to customer support',\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Dual AI agent (two AIs conversing)\n * export default defineAgent({\n * name: 'debate_agent',\n * title: 'AI Debate',\n * type: 'dual_ai',\n * maxSessionTurns: 10,\n * sideA: {\n * label: 'Pro',\n * prompt: 'debate_pro',\n * stopOnResponse: true,\n * },\n * sideB: {\n * label: 'Con',\n * prompt: 'debate_con',\n * stopOnResponse: true,\n * endSessionTool: 'conclude_debate',\n * },\n * });\n * ```\n */\nexport function defineAgent<N extends string>(\n options: AgentDefinition<N>\n): AgentDefinition<N> {\n // Validate required fields at runtime\n if (!options.name) {\n throw new Error('Agent name is required');\n }\n if (!options.sideA) {\n throw new Error('Agent sideA configuration is required');\n }\n if (!options.sideA.prompt) {\n throw new Error('Agent sideA.prompt is required');\n }\n\n // Set default type\n const type = options.type ?? 'ai_human';\n\n // Validate dual_ai requires sideB\n if (type === 'dual_ai') {\n if (!options.sideB) {\n throw new Error('Agent sideB configuration is required for dual_ai type');\n }\n if (!options.sideB.prompt) {\n throw new Error('Agent sideB.prompt is required for dual_ai type');\n }\n }\n\n // Validate exposeAsTool requires toolDescription\n if (options.exposeAsTool && !options.toolDescription) {\n throw new Error('toolDescription is required when exposeAsTool is true');\n }\n\n // Validate stopTool requires stopToolResponseProperty\n if (options.sideA.stopTool && !options.sideA.stopToolResponseProperty) {\n throw new Error('sideA.stopToolResponseProperty is required when sideA.stopTool is set');\n }\n if (options.sideB?.stopTool && !options.sideB.stopToolResponseProperty) {\n throw new Error('sideB.stopToolResponseProperty is required when sideB.stopTool is set');\n }\n\n // Validate maxSteps is positive\n if (options.sideA.maxSteps !== undefined && options.sideA.maxSteps <= 0) {\n throw new Error('sideA.maxSteps must be a positive number');\n }\n if (options.sideB?.maxSteps !== undefined && options.sideB.maxSteps <= 0) {\n throw new Error('sideB.maxSteps must be a positive number');\n }\n if (\n options.maxSessionTurns !== undefined &&\n options.maxSessionTurns !== null &&\n options.maxSessionTurns <= 0\n ) {\n throw new Error('maxSessionTurns must be a positive number');\n }\n\n // Validate type is a known value\n if (!['ai_human', 'dual_ai'].includes(type)) {\n throw new Error(`Invalid type '${type}'. Must be one of: ai_human, dual_ai`);\n }\n\n return {\n ...options,\n type,\n };\n}\n","/**\n * Endpoint definition types for Standard Agents.\n *\n * Endpoints expose HTTP APIs for interacting with agent threads.\n * They can be standard controllers or thread-specific endpoints\n * with automatic ThreadState injection.\n *\n * @module\n */\n\nimport type { ThreadState } from './threads.js';\n\n// ============================================================================\n// Virtual Module Types\n// ============================================================================\n\n/**\n * Virtual module loader function.\n *\n * Lazy-loads a module definition on demand.\n */\nexport type VirtualModuleLoader<T> = () => Promise<T>;\n\n/**\n * Registry of virtual modules by name.\n *\n * Maps module names to their lazy loaders.\n */\nexport type VirtualModuleRegistry<T> = Record<string, VirtualModuleLoader<T>>;\n\n// ============================================================================\n// Controller Types\n// ============================================================================\n\n/**\n * Controller context passed to route handlers.\n *\n * Contains the request, URL parameters, environment bindings,\n * and optionally the virtual module registries injected at runtime.\n *\n * Note: The `env` property contains implementation-specific bindings.\n * Cloudflare implementations may include Durable Object namespaces,\n * KV namespaces, etc. Other implementations may provide different bindings.\n */\nexport interface ControllerContext {\n /** The incoming HTTP request */\n req: Request;\n\n /** URL path parameters extracted by the router */\n params: Record<string, string>;\n\n /** Parsed URL object */\n url: URL;\n\n /**\n * Environment bindings (implementation-specific).\n *\n * Contains platform-specific bindings like Durable Objects, KV, etc.\n * The exact contents depend on the runtime implementation.\n */\n env: Record<string, unknown>;\n\n /** Registry of agent definitions (injected at runtime) */\n agents?: VirtualModuleRegistry<unknown>;\n\n /** Available agent names */\n agentNames?: string[];\n\n /** Registry of prompt definitions (injected at runtime) */\n prompts?: VirtualModuleRegistry<unknown>;\n\n /** Available prompt names */\n promptNames?: string[];\n\n /** Registry of model definitions (injected at runtime) */\n models?: VirtualModuleRegistry<unknown>;\n\n /** Available model names */\n modelNames?: string[];\n\n /** Registry of tool definitions (injected at runtime) */\n tools?: VirtualModuleRegistry<unknown>;\n\n /** Registry of hook definitions (injected at runtime) */\n hooks?: VirtualModuleRegistry<unknown>;\n\n /** Additional configuration (injected at runtime) */\n config?: Record<string, unknown>;\n}\n\n/**\n * Controller return types.\n *\n * Controllers can return various types that are automatically\n * converted to appropriate HTTP responses.\n */\nexport type ControllerReturn =\n | string\n | Promise<string>\n | Response\n | Promise<Response>\n | ReadableStream\n | Promise<ReadableStream>\n | null\n | Promise<null>\n | void\n | Promise<void>\n | Promise<object>\n | object;\n\n/**\n * Controller function type.\n *\n * Controllers handle HTTP requests and return responses.\n * The return value is automatically converted to a Response.\n *\n * @example\n * ```typescript\n * const handler: Controller = async ({ req, params, url }) => {\n * return { message: 'Hello, World!' };\n * };\n * ```\n */\nexport type Controller = (context: ControllerContext) => ControllerReturn;\n\n// ============================================================================\n// Thread Endpoint Types\n// ============================================================================\n\n/**\n * Thread endpoint handler function.\n *\n * Receives the HTTP request and the ThreadState for the requested thread.\n * The thread is automatically looked up by ID from URL parameters.\n */\nexport type ThreadEndpointHandler = (\n req: Request,\n state: ThreadState\n) => Response | Promise<Response>;\n\n// ============================================================================\n// Define Functions\n// ============================================================================\n\n/**\n * Define a standard HTTP controller.\n *\n * Controllers handle HTTP requests and return responses. They have\n * access to the controller context including virtual module registries.\n *\n * @param controller - The controller function\n * @returns The controller for registration\n *\n * @example\n * ```typescript\n * // agents/api/health.get.ts\n * import { defineController } from '@standardagents/spec';\n *\n * export default defineController(async () => {\n * return { status: 'ok', timestamp: Date.now() };\n * });\n * ```\n *\n * @example\n * ```typescript\n * // agents/api/agents/index.get.ts\n * import { defineController } from '@standardagents/spec';\n *\n * export default defineController(async ({ agentNames }) => {\n * return { agents: agentNames };\n * });\n * ```\n */\nexport function defineController(controller: Controller): Controller {\n return controller;\n}\n\n/**\n * Define a thread-specific endpoint.\n *\n * Thread endpoints automatically look up the thread by ID from URL params\n * and provide access to the ThreadState. The handler receives full\n * ThreadState with messages, logs, resource loading, event emission,\n * and (when executing) execution state.\n *\n * @param handler - The handler function receiving request and ThreadState\n * @returns A Controller that can be used with the router\n *\n * @example\n * ```typescript\n * // agents/api/threads/[id]/status.get.ts\n * import { defineThreadEndpoint } from '@standardagents/spec';\n *\n * export default defineThreadEndpoint(async (req, state) => {\n * const { messages, total } = await state.getMessages({ limit: 1 });\n * return Response.json({\n * threadId: state.threadId,\n * agent: state.agentId,\n * messageCount: total,\n * });\n * });\n * ```\n *\n * @example\n * ```typescript\n * // agents/api/threads/[id]/export.get.ts\n * import { defineThreadEndpoint } from '@standardagents/spec';\n *\n * export default defineThreadEndpoint(async (req, state) => {\n * const { messages } = await state.getMessages();\n * return Response.json({\n * thread: {\n * id: state.threadId,\n * agent: state.agentId,\n * user: state.userId,\n * createdAt: state.createdAt,\n * },\n * messages,\n * });\n * });\n * ```\n *\n * @example\n * ```typescript\n * // agents/api/threads/[id]/invoke.post.ts\n * import { defineThreadEndpoint } from '@standardagents/spec';\n *\n * export default defineThreadEndpoint(async (req, state) => {\n * const { tool, args } = await req.json();\n *\n * // Queue a tool for execution (starts execution if not running)\n * state.queueTool(tool, args);\n *\n * return Response.json({ queued: true });\n * });\n * ```\n */\nexport function defineThreadEndpoint(\n handler: ThreadEndpointHandler\n): Controller {\n // The actual implementation is provided by the runtime.\n // This is a marker function that the router recognizes.\n // The runtime wraps this to provide ThreadState.\n return (handler as unknown) as Controller;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/models.ts","../src/tools.ts","../src/prompts.ts","../src/hooks.ts","../src/effects.ts","../src/agents.ts","../src/endpoints.ts","../src/providers.ts"],"sourcesContent":["/**\n * Model definition types for Standard Agents.\n *\n * Models define LLM configurations including provider, model ID, pricing,\n * and fallback chains. Models are referenced by name from prompts.\n *\n * @module\n */\n\nimport type { z, ZodTypeAny } from 'zod';\nimport type {\n ProviderRequest,\n ProviderResponse,\n ProviderStreamChunk,\n InspectedRequest,\n ProviderModelInfo,\n ResponseSummary,\n} from './providers';\nimport type { ToolDefinition, ToolArgs, ToolTenvs } from './tools';\n\n/**\n * Legacy provider identifiers - kept for reference only.\n * New code should use ProviderFactory imports from provider packages.\n *\n * @deprecated Use ProviderFactory from provider packages instead\n * @internal\n */\nexport type ModelProvider = 'openai' | 'openrouter' | 'anthropic' | 'google' | 'test';\n\n/**\n * Provider interface for LLM providers.\n *\n * Provider packages export a factory function that creates instances\n * implementing this interface. This is structurally compatible with\n * LLMProviderInterface from providers.ts.\n */\nexport interface ProviderInstance {\n readonly name: string;\n readonly specificationVersion: '1';\n generate(request: ProviderRequest): Promise<ProviderResponse>;\n stream(request: ProviderRequest): Promise<AsyncIterable<ProviderStreamChunk>>;\n supportsModel?(modelId: string): boolean;\n /** List available models from this provider */\n getModels?(filter?: string): Promise<ProviderModelInfo[]>;\n /** Fetch capabilities for a specific model */\n getModelCapabilities?(modelId: string): Promise<ModelCapabilities | null>;\n /** Get provider-specific tools available for a model */\n getTools?(modelId?: string): Record<string, ToolDefinition<unknown, ToolArgs | null, ToolTenvs | null>> | Promise<Record<string, ToolDefinition<unknown, ToolArgs | null, ToolTenvs | null>>>;\n /** Get the icon URL for this provider or a specific model */\n getIcon?(modelId?: string): string | undefined;\n /** Inspect a raw request for debugging purposes */\n inspectRequest?(request: ProviderRequest): Promise<InspectedRequest>;\n /** Fetch additional metadata about a completed response */\n getResponseMetadata?(summary: ResponseSummary, signal?: AbortSignal): Promise<Record<string, unknown> | null>;\n}\n\n/**\n * Configuration passed to provider factory functions.\n */\nexport interface ProviderFactoryConfig {\n apiKey: string;\n baseUrl?: string;\n timeout?: number;\n}\n\n/**\n * Provider factory with optional typed providerOptions schema.\n *\n * The schema property allows type inference and runtime validation of\n * provider-specific options in model definitions.\n *\n * @template TOptions - Zod schema type for providerOptions (defaults to ZodTypeAny)\n *\n * @example\n * ```typescript\n * import { openai } from '@standardagents/openai';\n *\n * // openai is a ProviderFactoryWithOptions with typed schema\n * const provider = openai({ apiKey: 'sk-...' });\n *\n * // Access the schema for validation\n * openai.providerOptions?.parse({ service_tier: 'default' });\n * ```\n */\nexport interface ProviderFactoryWithOptions<\n TOptions extends ZodTypeAny = ZodTypeAny\n> {\n (config: ProviderFactoryConfig): ProviderInstance;\n /** Zod schema for provider-specific options */\n providerOptions?: TOptions;\n}\n\n/**\n * Factory function that creates provider instances.\n *\n * Provider packages (like @standardagents/openai) export this type.\n * This is an alias for ProviderFactoryWithOptions for backward compatibility.\n *\n * @example\n * ```typescript\n * import { openai } from '@standardagents/openai';\n *\n * // openai is a ProviderFactory\n * const provider = openai({ apiKey: 'sk-...' });\n * ```\n */\nexport type ProviderFactory = ProviderFactoryWithOptions<ZodTypeAny>;\n\n/**\n * Extract providerOptions type from a provider factory.\n *\n * Returns the inferred input type from the provider's Zod schema,\n * or Record<string, unknown> if no schema is defined.\n *\n * @template P - Provider factory type\n */\nexport type InferProviderOptions<P> =\n P extends ProviderFactoryWithOptions<infer S>\n ? S extends ZodTypeAny\n ? z.input<S> extends Record<string, unknown>\n ? z.input<S>\n : Record<string, unknown>\n : Record<string, unknown>\n : Record<string, unknown>;\n\n/**\n * Model capability flags indicating supported features.\n *\n * These capabilities are used by the FlowEngine to determine how to\n * interact with the model and what features are available.\n */\nexport interface ModelCapabilities {\n /**\n * Reasoning level mapping (0-100 scale → model-specific values).\n * Keys are breakpoints, values are model's native reasoning strings.\n *\n * @example\n * // OpenAI o-series\n * reasoningLevels: { 0: null, 33: 'low', 66: 'medium', 100: 'high' }\n *\n * @example\n * // Binary reasoning (Claude extended thinking)\n * reasoningLevels: { 0: null, 100: 'enabled' }\n *\n * @example\n * // No reasoning support\n * reasoningLevels: { 0: null }\n */\n reasoningLevels?: Record<number, string | null>;\n\n /**\n * Whether the model supports vision (image understanding).\n * When true, image attachments will be sent to the model as part of the request.\n * Models like GPT-4o, Claude 3, and Gemini support vision.\n */\n supportsImages?: boolean;\n\n /**\n * @deprecated Use supportsImages instead\n */\n vision?: boolean;\n\n /**\n * Whether the model supports function calling (tool use).\n * Most modern models support this, defaults to true if not specified.\n */\n supportsToolCalls?: boolean;\n\n /**\n * @deprecated Use supportsToolCalls instead\n */\n functionCalling?: boolean;\n\n /**\n * Whether the model supports streaming responses.\n * @default true\n */\n supportsStreaming?: boolean;\n\n /**\n * Whether the model supports structured outputs (JSON mode).\n */\n supportsJsonMode?: boolean;\n\n /**\n * @deprecated Use supportsJsonMode instead\n */\n structuredOutputs?: boolean;\n\n /**\n * Maximum context window size in tokens.\n */\n maxContextTokens?: number;\n\n /**\n * Maximum output tokens the model can generate.\n */\n maxOutputTokens?: number;\n}\n\n/**\n * Model definition configuration.\n *\n * Defines an LLM model with its provider, pricing, capabilities, and fallback chain.\n *\n * @template N - The model name as a string literal type for type inference\n *\n * @example Basic usage\n * ```typescript\n * import { defineModel } from '@standardagents/spec';\n * import { openai } from '@standardagents/openai';\n *\n * export default defineModel({\n * name: 'gpt-4o',\n * provider: openai,\n * model: 'gpt-4o',\n * inputPrice: 2.5,\n * outputPrice: 10,\n * });\n * ```\n *\n * @example With capabilities and typed provider options\n * ```typescript\n * import { defineModel } from '@standardagents/spec';\n * import { openai } from '@standardagents/openai';\n *\n * export default defineModel({\n * name: 'gpt-4o',\n * provider: openai,\n * model: 'gpt-4o',\n * inputPrice: 2.5,\n * outputPrice: 10,\n * capabilities: {\n * supportsImages: true,\n * supportsToolCalls: true,\n * supportsJsonMode: true,\n * maxContextTokens: 128000,\n * },\n * providerOptions: {\n * service_tier: 'default', // TypeScript knows this is valid\n * },\n * });\n * ```\n */\nexport interface ModelDefinition<\n N extends string = string,\n P extends ProviderFactoryWithOptions<ZodTypeAny> = ProviderFactoryWithOptions<ZodTypeAny>\n> {\n /**\n * Unique name for this model definition.\n * Used as the identifier when referencing from prompts.\n * Should be descriptive and consistent (e.g., 'gpt-4o', 'claude-3-opus').\n */\n name: N;\n\n /**\n * The LLM provider factory function to use for API calls.\n *\n * Import from a provider package like @standardagents/openai or @standardagents/openrouter.\n * The provider's type determines what providerOptions are available.\n *\n * @example\n * ```typescript\n * import { openai } from '@standardagents/openai';\n * provider: openai\n * ```\n *\n * @example\n * ```typescript\n * import { openrouter } from '@standardagents/openrouter';\n * provider: openrouter\n * ```\n */\n provider: P;\n\n /**\n * The actual model identifier sent to the provider API.\n *\n * For OpenAI: 'gpt-4o', 'gpt-4-turbo', 'gpt-3.5-turbo', etc.\n * For OpenRouter: 'openai/gpt-4o', 'anthropic/claude-3-opus', etc.\n * For Anthropic: 'claude-3-opus-20240229', 'claude-3-sonnet-20240229', etc.\n * For Google: 'gemini-1.5-pro', 'gemini-1.5-flash', etc.\n */\n model: string;\n\n /**\n * Optional list of additional provider prefixes for OpenRouter.\n * Allows routing through specific providers when using OpenRouter.\n *\n * @example ['anthropic', 'google'] - prefer Anthropic, fallback to Google\n */\n includedProviders?: string[];\n\n /**\n * Fallback model names to try if this model fails.\n * Referenced by model name (must be defined in agents/models/).\n * Tried in order after primary model exhausts retries.\n *\n * @example ['gpt-4', 'gpt-3.5-turbo']\n */\n fallbacks?: string[];\n\n /**\n * Cost per 1 million input tokens in USD.\n * Used for cost tracking and reporting in logs.\n */\n inputPrice?: number;\n\n /**\n * Cost per 1 million output tokens in USD.\n * Used for cost tracking and reporting in logs.\n */\n outputPrice?: number;\n\n /**\n * Cost per 1 million cached input tokens in USD.\n * Some providers offer reduced pricing for cached/repeated prompts.\n */\n cachedPrice?: number;\n\n /**\n * Model capabilities - features this model supports.\n */\n capabilities?: ModelCapabilities;\n\n /**\n * Provider-specific options passed through to the provider.\n * These are merged with prompt-level providerOptions (model options are defaults).\n *\n * The type is automatically inferred from the provider's schema when available,\n * providing TypeScript autocompletion and runtime validation.\n *\n * @example\n * ```typescript\n * // With OpenAI provider\n * providerOptions: {\n * service_tier: 'default',\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With OpenRouter provider\n * providerOptions: {\n * provider: {\n * zdr: true,\n * max_price: { prompt: 1 },\n * },\n * }\n * ```\n */\n providerOptions?: InferProviderOptions<P>;\n\n /**\n * Provider tools available for this model.\n * References tool names from provider.getTools().\n *\n * Provider tools are built-in tools offered by the provider (e.g., OpenAI's\n * web_search, file_search, code_interpreter, image_generation). These tools\n * can be used in prompts alongside custom tools.\n *\n * @example\n * ```typescript\n * providerTools: ['web_search', 'code_interpreter'],\n * ```\n */\n providerTools?: string[];\n}\n\n/**\n * Defines an LLM model configuration.\n *\n * Models are the foundation of the agent system - they specify which\n * AI model to use and how to connect to it. Models can have fallbacks\n * for reliability and include pricing for cost tracking.\n *\n * @template N - The model name as a string literal type\n * @param options - Model configuration options\n * @returns The model definition for registration\n *\n * @example\n * ```typescript\n * // agents/models/gpt_4o.ts\n * import { defineModel } from '@standardagents/spec';\n * import { openai } from '@standardagents/openai';\n *\n * export default defineModel({\n * name: 'gpt-4o',\n * provider: openai,\n * model: 'gpt-4o',\n * fallbacks: ['gpt-4-turbo'],\n * inputPrice: 2.5,\n * outputPrice: 10,\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Using OpenRouter with typed provider options\n * import { openrouter } from '@standardagents/openrouter';\n *\n * export default defineModel({\n * name: 'claude-3-opus',\n * provider: openrouter,\n * model: 'anthropic/claude-3-opus',\n * providerOptions: {\n * provider: {\n * zdr: true,\n * only: ['anthropic'],\n * },\n * },\n * });\n * ```\n */\nexport function defineModel<\n N extends string,\n P extends ProviderFactoryWithOptions<ZodTypeAny>\n>(\n options: ModelDefinition<N, P>\n): ModelDefinition<N, P> {\n // Validate required fields at runtime\n if (!options.name) {\n throw new Error('Model name is required');\n }\n if (!options.provider) {\n throw new Error('Model provider is required');\n }\n if (!options.model) {\n throw new Error('Model ID is required');\n }\n\n // Validate provider is a ProviderFactory function\n if (typeof options.provider !== 'function') {\n throw new Error(\n 'Provider must be a ProviderFactory function imported from a provider package (e.g., @standardagents/openai)'\n );\n }\n\n // Validate pricing is non-negative if provided\n if (options.inputPrice !== undefined && options.inputPrice < 0) {\n throw new Error('inputPrice must be non-negative');\n }\n if (options.outputPrice !== undefined && options.outputPrice < 0) {\n throw new Error('outputPrice must be non-negative');\n }\n if (options.cachedPrice !== undefined && options.cachedPrice < 0) {\n throw new Error('cachedPrice must be non-negative');\n }\n\n // Runtime validation of providerOptions if schema exists on provider\n if (options.provider.providerOptions && options.providerOptions) {\n const result = options.provider.providerOptions.safeParse(options.providerOptions);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => `${String(i.path.join('.'))}: ${i.message}`)\n .join(', ');\n throw new Error(`Invalid providerOptions for model '${options.name}': ${issues}`);\n }\n }\n\n return options;\n}\n","/**\n * Tool definition types for Standard Agents.\n *\n * Tools are callable functions that agents can invoke during execution.\n * They receive the current ThreadState and validated arguments,\n * and return results that are included in the conversation.\n *\n * @module\n */\n\nimport type { ThreadState } from './threads.js';\nimport type {\n ZodString,\n ZodNumber,\n ZodBoolean,\n ZodEnum,\n ZodArray,\n ZodObject,\n ZodOptional,\n ZodNullable,\n ZodUnion,\n ZodRecord,\n ZodNull,\n ZodLiteral,\n ZodDefault,\n} from 'zod';\nimport { z } from 'zod';\n\n/**\n * Text content item returned by tools.\n */\nexport interface TextContent {\n type: 'text';\n text: string;\n}\n\n/**\n * Image content item returned by tools.\n */\nexport interface ImageContent {\n type: 'image';\n /** Base64-encoded image data. */\n data: string;\n /** MIME type of the image (e.g., 'image/png', 'image/jpeg'). */\n mimeType: string;\n}\n\n/**\n * Content types that can be returned by tools.\n */\nexport type ToolContent = TextContent | ImageContent;\n\n/**\n * File attachment generated by a tool.\n *\n * Attachments are stored in the thread's file system and linked to\n * the tool result message. Unlike user uploads, tool attachments are\n * not subject to image downsampling.\n */\nexport interface ToolAttachment {\n /** File name for the attachment. */\n name: string;\n /** MIME type of the attachment. */\n mimeType: string;\n /** Base64-encoded file data. */\n data: string;\n /** Width in pixels (for images). */\n width?: number;\n /** Height in pixels (for images). */\n height?: number;\n}\n\n/**\n * Reference to a pre-existing attachment in the thread file system.\n */\nexport interface AttachmentRef {\n /** Unique identifier for the attachment. */\n id: string;\n /** Attachment type. */\n type: 'file';\n /** Path in the thread file system. */\n path: string;\n /** File name. */\n name: string;\n /** MIME type. */\n mimeType: string;\n /** File size in bytes. */\n size: number;\n /** Width in pixels (for images). */\n width?: number;\n /** Height in pixels (for images). */\n height?: number;\n /** AI-generated description. */\n description?: string;\n}\n\n/**\n * Result returned by a tool execution.\n */\nexport interface ToolResult {\n /** Status of the tool execution. */\n status: 'success' | 'error';\n /**\n * Text representation of the tool output.\n *\n * For tools that return structured content, this is derived by\n * concatenating all text parts. For simple tools, this is the\n * direct result string.\n */\n result?: string;\n /** Error message if status is 'error'. */\n error?: string;\n /** Stack trace for error debugging. */\n stack?: string;\n /**\n * File attachments returned by the tool.\n *\n * Can contain either:\n * - ToolAttachment: New files with base64 data to be stored\n * - AttachmentRef: References to existing files in the thread filesystem\n *\n * Implementations MUST store new attachments under /attachments/ directory.\n */\n attachments?: Array<ToolAttachment | AttachmentRef>;\n}\n\n// ============================================================================\n// Tool Argument Types (Zod-based)\n// ============================================================================\n\n/** Decrement helper to limit recursion depth. */\ntype Dec<N extends number> = N extends 10\n ? 9\n : N extends 9\n ? 8\n : N extends 8\n ? 7\n : N extends 7\n ? 6\n : N extends 6\n ? 5\n : N extends 5\n ? 4\n : N extends 4\n ? 3\n : N extends 3\n ? 2\n : N extends 2\n ? 1\n : N extends 1\n ? 0\n : 0;\n\n/**\n * Allowed Zod types for tool argument nodes.\n *\n * This is the single source of truth for which Zod types can be used\n * in tool argument schemas. The depth parameter limits recursion to\n * prevent infinite type expansion.\n *\n * @template D - Maximum nesting depth (default: 7)\n */\nexport type ToolArgsNode<D extends number = 7> =\n // Primitives and literals\n | ZodString\n | ZodNumber\n | ZodBoolean\n | ZodNull\n | ZodLiteral<string | number | boolean | null>\n // Enums (Zod v4 uses Record<string, string> for enum type parameter)\n | ZodEnum<Readonly<Record<string, string>>>\n // Wrappers (with depth check)\n | (D extends 0 ? never : ZodOptional<ToolArgsNode<Dec<D>>>)\n | (D extends 0 ? never : ZodNullable<ToolArgsNode<Dec<D>>>)\n | (D extends 0 ? never : ZodDefault<ToolArgsNode<Dec<D>>>)\n // Arrays (with depth check)\n | (D extends 0 ? never : ZodArray<ToolArgsNode<Dec<D>>>)\n // Objects and records (with depth check)\n | (D extends 0 ? never : ZodObject<Record<string, ToolArgsNode<Dec<D>>>>)\n | (D extends 0 ? never : ZodRecord<ZodString, ToolArgsNode<Dec<D>>>)\n // Unions (with depth check)\n | (D extends 0\n ? never\n : ZodUnion<\n [\n ToolArgsNode<Dec<D>>,\n ToolArgsNode<Dec<D>>,\n ...ToolArgsNode<Dec<D>>[],\n ]\n >);\n\n/**\n * Raw shape for a Zod object schema containing tool argument nodes.\n *\n * @template D - Maximum nesting depth (default: 7)\n */\nexport type ToolArgsRawShape<D extends number = 7> = Record<\n string,\n ToolArgsNode<D>\n>;\n\n/**\n * Top-level tool argument schema.\n *\n * Tool arguments MUST be defined as a Zod object schema.\n * This is required for compatibility with OpenAI's function calling API.\n *\n * @template D - Maximum nesting depth (default: 7)\n */\nexport type ToolArgs<D extends number = 7> = z.ZodObject<ToolArgsRawShape<D>>;\n\n// ============================================================================\n// Thread Environment Variable Types (Zod-based)\n// ============================================================================\n\n/**\n * Raw shape for tenv schema - uses same pattern as ToolArgsRawShape.\n * Each key is a tenv name, value is a Zod schema for validation.\n *\n * @template D - Maximum nesting depth (default: 7)\n */\nexport type TenvRawShape<D extends number = 7> = Record<string, ToolArgsNode<D>>;\n\n/**\n * Top-level thread environment variable schema.\n *\n * Defines which thread environment variables a tool requires.\n * Required tenvs are non-optional fields, optional tenvs use .optional().\n *\n * @example\n * ```typescript\n * z.object({\n * vectorStoreId: z.string().describe('OpenAI Vector Store ID'), // Required\n * userLocation: z.string().optional().describe('User location'), // Optional\n * })\n * ```\n *\n * @template D - Maximum nesting depth (default: 7)\n */\nexport type ToolTenvs<D extends number = 7> = z.ZodObject<TenvRawShape<D>>;\n\n// ============================================================================\n// Tool Function Types\n// ============================================================================\n\n/**\n * Tool function signature.\n *\n * Tools are async functions that receive a ThreadState and\n * optionally validated arguments, returning a ToolResult.\n *\n * @template State - The state type (defaults to ThreadState)\n * @template Args - The Zod schema for tool arguments, or null for no args\n */\nexport type Tool<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n> = Args extends ToolArgs\n ? (state: State, args: z.infer<Args>) => Promise<ToolResult>\n : (state: State) => Promise<ToolResult>;\n\n// ============================================================================\n// Tool Definition Types (Object-based API)\n// ============================================================================\n\n/**\n * Options for defining a tool.\n *\n * @template State - The state type (defaults to ThreadState)\n * @template Args - The Zod schema for tool arguments, or null for no args\n * @template Tenvs - The Zod schema for thread environment variables, or null\n */\nexport interface DefineToolOptions<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n Tenvs extends ToolTenvs | null = null,\n> {\n /** Description of what the tool does (shown to the LLM). */\n description: string;\n /** Zod schema for validating tool arguments. Omit for tools with no args. */\n args?: Args;\n /** The tool implementation function. */\n execute: Tool<State, Args>;\n /** Zod schema for thread environment variables the tool requires. */\n tenvs?: Tenvs;\n /**\n * Where this tool is executed:\n * - 'local': Execute locally by the execution engine (default)\n * - 'provider': Executed by the LLM provider, results come in response\n */\n executionMode?: 'local' | 'provider';\n /**\n * Which provider executes this tool (when executionMode='provider').\n * e.g., 'openai', 'anthropic'\n */\n executionProvider?: string;\n}\n\n/**\n * Tool definition object returned by defineTool().\n *\n * Contains all the metadata and implementation needed to register\n * and execute a tool.\n *\n * @template State - The state type (defaults to ThreadState)\n * @template Args - The Zod schema for tool arguments, or null for no args\n * @template Tenvs - The Zod schema for thread environment variables, or null\n */\nexport interface ToolDefinition<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n Tenvs extends ToolTenvs | null = null,\n> {\n /** Description of what the tool does (shown to the LLM). */\n description: string;\n /** Zod schema for validating tool arguments, or null for no args. */\n args: Args;\n /** The tool implementation function. */\n execute: Tool<State, Args>;\n /** Zod schema for thread environment variables, or null if none. */\n tenvs: Tenvs;\n /**\n * Where this tool is executed:\n * - 'local': Execute locally by the execution engine (default)\n * - 'provider': Executed by the LLM provider, results come in response\n */\n executionMode?: 'local' | 'provider';\n /**\n * Which provider executes this tool (when executionMode='provider').\n */\n executionProvider?: string;\n}\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Defines a tool that agents can call during execution.\n *\n * Tools are the primary way agents interact with external systems.\n * Each tool has a description (shown to the LLM), optional argument\n * schema (validated at runtime), an implementation function, and\n * optional thread environment variable (tenv) requirements.\n *\n * @example\n * ```typescript\n * import { defineTool } from '@standardagents/spec';\n * import { z } from 'zod';\n *\n * // Tool with arguments\n * export default defineTool({\n * description: 'Search the knowledge base for relevant information',\n * args: z.object({\n * query: z.string().describe('Search query'),\n * limit: z.number().optional().describe('Max results'),\n * }),\n * execute: async (state, args) => {\n * const results = await search(args.query, args.limit);\n * return { status: 'success', result: JSON.stringify(results) };\n * },\n * });\n *\n * // Tool without arguments\n * export default defineTool({\n * description: 'Get the current server time',\n * execute: async (state) => {\n * return { status: 'success', result: new Date().toISOString() };\n * },\n * });\n *\n * // Tool with tenvs (thread environment variables)\n * export default defineTool({\n * description: 'Search through uploaded files',\n * args: z.object({ query: z.string() }),\n * execute: async (state, args) => ({ status: 'success', result: 'done' }),\n * tenvs: z.object({\n * vectorStoreId: z.string().describe('OpenAI Vector Store ID'),\n * }),\n * });\n *\n * // Provider-executed tool (e.g., OpenAI built-in tools)\n * export default defineTool({\n * description: 'Generate images using DALL-E',\n * args: z.object({ prompt: z.string() }),\n * execute: async () => ({ status: 'success', result: 'Handled by provider' }),\n * executionMode: 'provider',\n * executionProvider: 'openai',\n * });\n * ```\n *\n * @param options - Tool definition options\n * @returns A tool definition object\n */\nexport function defineTool<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n Tenvs extends ToolTenvs | null = null,\n>(\n options: DefineToolOptions<State, Args, Tenvs>\n): ToolDefinition<State, Args, Tenvs> {\n return {\n description: options.description,\n args: (options.args ?? null) as Args,\n execute: options.execute,\n tenvs: (options.tenvs ?? null) as Tenvs,\n executionMode: options.executionMode,\n executionProvider: options.executionProvider,\n };\n}\n","/**\n * Prompt definition types for Standard Agents.\n *\n * Prompts define LLM interaction configurations including the system prompt,\n * model selection, available tools, and various behavioral options.\n *\n * @module\n */\n\nimport type { z } from 'zod';\nimport type { ToolArgs } from './tools.js';\n\n// ============================================================================\n// Structured Prompt Types\n// ============================================================================\n\n/**\n * A text part of a prompt - static text content.\n *\n * @example\n * ```typescript\n * { type: 'text', content: 'You are a helpful assistant.\\n\\n' }\n * ```\n */\nexport interface PromptTextPart {\n type: 'text';\n /** The text content */\n content: string;\n}\n\n/**\n * A prompt inclusion part - includes another prompt's content.\n *\n * @example\n * ```typescript\n * { type: 'include', prompt: 'responder_rules' }\n * ```\n */\nexport interface PromptIncludePart {\n type: 'include';\n /** The name of the prompt to include */\n prompt: string;\n}\n\n/**\n * A single part of a structured prompt.\n * Discriminated union on `type` field for TypeScript narrowing.\n */\nexport type PromptPart = PromptTextPart | PromptIncludePart;\n\n/**\n * A structured prompt is an array of prompt parts.\n * Provides composition with other prompts via includes.\n *\n * @example\n * ```typescript\n * prompt: [\n * { type: 'text', content: 'You are a helpful assistant.\\n\\n' },\n * { type: 'include', prompt: 'common_rules' },\n * { type: 'text', content: '\\n\\nBe concise.' },\n * ]\n * ```\n */\nexport type StructuredPrompt = PromptPart[];\n\n/**\n * The prompt content can be either a plain string or a structured array.\n *\n * @example\n * ```typescript\n * // Simple string prompt:\n * prompt: 'You are a helpful assistant.'\n *\n * // Structured prompt with includes:\n * prompt: [\n * { type: 'text', content: 'You are a helpful assistant.\\n\\n' },\n * { type: 'include', prompt: 'common_rules' },\n * ]\n * ```\n */\nexport type PromptContent = string | StructuredPrompt;\n\n// ============================================================================\n// Sub-Prompt Configuration\n// ============================================================================\n\n/**\n * Configuration for sub-prompts used as tools.\n * These options control how results from sub-prompts are returned to the caller.\n *\n * @template T - The sub-prompt name type (for type-safe references)\n */\nexport interface SubpromptConfig<T extends string = string> {\n /**\n * Name of the sub-prompt to call.\n * Must be a prompt defined in agents/prompts/.\n */\n name: T;\n\n /**\n * Include text response content from sub-prompt execution in the result string.\n * @default true\n */\n includeTextResponse?: boolean;\n\n /**\n * Serialize tool calls made by the sub-prompt (and their results) into the result string.\n * @default true\n */\n includeToolCalls?: boolean;\n\n /**\n * Serialize any errors from the sub-prompt into the result string.\n * @default true\n */\n includeErrors?: boolean;\n\n /**\n * Property from the tool call arguments to use as the initial user message\n * when invoking the sub-prompt.\n *\n * @example\n * If the tool is called with `{ query: \"search term\", limit: 10 }` and\n * `initUserMessageProperty: 'query'`, the sub-prompt will receive\n * \"search term\" as the initial user message.\n */\n initUserMessageProperty?: string;\n\n /**\n * Property containing attachment path(s) to include as multimodal content\n * when invoking the sub-prompt.\n *\n * Supports both a single path string or an array of paths.\n *\n * @example\n * If the tool is called with `{ image: \"/attachments/123.jpg\" }` and\n * `initAttachmentsProperty: 'image'`, the sub-prompt will receive\n * the image as an attachment in the user message.\n *\n * @example\n * If the tool is called with `{ images: [\"/attachments/a.jpg\", \"/attachments/b.jpg\"] }` and\n * `initAttachmentsProperty: 'images'`, the sub-prompt will receive\n * both images as attachments.\n */\n initAttachmentsProperty?: string;\n}\n\n/**\n * @deprecated Use SubpromptConfig instead\n */\nexport type ToolConfig<T extends string = string> = SubpromptConfig<T>;\n\n// ============================================================================\n// Prompt Tool Configuration\n// ============================================================================\n\n/**\n * Configuration for a tool used in a prompt.\n * Allows specifying tenv values and static options for the tool.\n *\n * @example\n * ```typescript\n * // Tool with tenv values\n * { name: 'file_search', tenvs: { vectorStoreId: 'vs_abc123' } }\n *\n * // Tool with options\n * { name: 'web_search', options: { searchContextSize: 'high' } }\n * ```\n */\nexport interface PromptToolConfig {\n /**\n * Name of the tool (custom tool or provider tool).\n */\n name: string;\n\n /**\n * Thread environment variable values for this tool.\n * These values are merged with agent and thread tenvs (prompt tenvs are lowest priority).\n */\n tenvs?: Record<string, unknown>;\n\n /**\n * Static options for this tool.\n * Passed to the tool handler at execution time.\n */\n options?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Reasoning Configuration\n// ============================================================================\n\n/**\n * Reasoning configuration for models that support extended thinking.\n * Applies to models like OpenAI o1/o3, Anthropic Claude with extended thinking,\n * Google Gemini with thinking, and Qwen with reasoning.\n */\nexport interface ReasoningConfig {\n /**\n * Numeric reasoning level on a 0-100 scale.\n * The FlowEngine maps this to the model's nearest supported reasoning option.\n *\n * @example\n * // Typical breakpoints:\n * // 0 = No reasoning\n * // 33 = Low effort\n * // 66 = Medium effort\n * // 100 = Maximum effort\n *\n * reasoning: { level: 75 } // Maps to 'high' on most models\n */\n level?: number;\n\n /**\n * @deprecated Use `level` instead. Will be removed in future versions.\n *\n * Effort level for reasoning models.\n * Higher effort = more thinking tokens = potentially better results.\n *\n * - `low`: Minimal reasoning, faster responses (equivalent to level ~33)\n * - `medium`: Balanced reasoning and speed (equivalent to level ~66)\n * - `high`: Maximum reasoning, slower but more thorough (equivalent to level ~100)\n *\n * @default undefined (use model defaults)\n */\n effort?: 'low' | 'medium' | 'high';\n\n /**\n * Maximum tokens to allocate for reasoning.\n * Applies to models that support token limits on reasoning.\n */\n maxTokens?: number;\n\n /**\n * Use reasoning internally but exclude from the response.\n * Model thinks through the problem but only returns the final answer.\n * Useful for cleaner outputs while maintaining reasoning quality.\n */\n exclude?: boolean;\n\n /**\n * Include reasoning content in the message history for multi-turn context.\n * When true, reasoning is preserved and visible to subsequent turns.\n * @default false\n */\n include?: boolean;\n}\n\n// ============================================================================\n// Prompt Definition\n// ============================================================================\n\n/**\n * Prompt definition configuration.\n *\n * @template N - The prompt name as a string literal type\n * @template S - The Zod schema type for requiredSchema (inferred automatically)\n *\n * @example\n * ```typescript\n * import { definePrompt } from '@standardagents/spec';\n * import { z } from 'zod';\n *\n * export default definePrompt({\n * name: 'customer_support',\n * toolDescription: 'Handle customer support inquiries',\n * model: 'gpt-4o',\n * prompt: 'You are a helpful customer support agent.',\n * tools: ['search_knowledge_base', 'create_ticket'],\n * requiredSchema: z.object({\n * query: z.string().describe('The customer inquiry'),\n * }),\n * });\n * ```\n */\nexport interface PromptDefinition<\n N extends string = string,\n S extends ToolArgs = ToolArgs,\n> {\n /**\n * Unique name for this prompt.\n * Used as the identifier when referencing from agents or as a tool.\n * Should be snake_case (e.g., 'customer_support', 'data_analyst').\n */\n name: N;\n\n /**\n * Description shown when this prompt is exposed as a tool.\n * Should clearly describe what this prompt does for LLM tool selection.\n */\n toolDescription: string;\n\n /**\n * The system prompt content sent to the LLM.\n * Can be either a plain string or a structured array for composition.\n */\n prompt: PromptContent;\n\n /**\n * Model to use for this prompt.\n * Must reference a model defined in agents/models/.\n */\n model: string;\n\n /**\n * Include full chat history in the LLM context.\n * @default false\n */\n includeChat?: boolean;\n\n /**\n * Include results from past tool calls in the LLM context.\n * @default false\n */\n includePastTools?: boolean;\n\n /**\n * Allow parallel execution of multiple tool calls.\n * @default false\n */\n parallelToolCalls?: boolean;\n\n /**\n * Tool calling strategy for the LLM.\n *\n * - `auto`: Model decides when to call tools (default)\n * - `none`: Disable tool calling entirely\n * - `required`: Force the model to call at least one tool\n *\n * @default 'auto'\n */\n toolChoice?: 'auto' | 'none' | 'required';\n\n /**\n * Zod schema for validating inputs when this prompt is called as a tool.\n */\n requiredSchema?: S;\n\n /**\n * Tools available to this prompt.\n * Can be:\n * - string: Simple tool name (custom or provider tool)\n * - SubpromptConfig: Sub-prompt used as a tool\n * - PromptToolConfig: Tool with tenv values and/or options\n *\n * To enable handoffs, include ai_human agent names in this array.\n *\n * @example\n * ```typescript\n * tools: [\n * 'custom_tool', // Simple tool name\n * { name: 'other_prompt' }, // Sub-prompt as tool\n * { name: 'file_search', tenvs: { vectorStoreId: 'vs_123' } }, // Tool with tenvs\n * ]\n * ```\n */\n tools?: (string | SubpromptConfig | PromptToolConfig)[];\n\n /**\n * Thread environment variables for this prompt.\n * These values are merged with agent and thread tenvs when creating a thread.\n * Prompt tenvs have lowest priority (overridden by agent and thread tenvs).\n *\n * @example\n * ```typescript\n * tenvs: {\n * vectorStoreId: 'vs_default_123',\n * apiEndpoint: 'https://api.example.com',\n * }\n * ```\n */\n tenvs?: Record<string, unknown>;\n\n /**\n * Reasoning configuration for models that support extended thinking.\n */\n reasoning?: ReasoningConfig;\n\n /**\n * Number of recent messages to keep actual images for in context.\n * @default 10\n */\n recentImageThreshold?: number;\n\n /**\n * Provider-specific options passed through to the provider.\n * These override model-level providerOptions for this prompt.\n *\n * Options are merged in order (later wins):\n * 1. model.providerOptions (defaults)\n * 2. prompt.providerOptions (this field - overrides)\n *\n * @example\n * ```typescript\n * providerOptions: {\n * response_format: { type: 'json_object' },\n * }\n * ```\n */\n providerOptions?: Record<string, unknown>;\n}\n\n/**\n * Helper type to extract the inferred input type from a prompt's Zod schema.\n *\n * @template T - The prompt definition type\n */\nexport type PromptInput<T extends PromptDefinition<string, ToolArgs>> =\n T['requiredSchema'] extends ToolArgs\n ? z.infer<T['requiredSchema']>\n : never;\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Defines a prompt configuration for LLM interactions.\n *\n * Prompts are the primary way to configure how agents interact with LLMs.\n * They specify the system prompt, available tools, input validation,\n * and various behavioral options.\n *\n * @template N - The prompt name as a string literal type\n * @template S - The Zod schema type for requiredSchema\n * @param options - Prompt configuration options\n * @returns The prompt definition for registration\n *\n * @example\n * ```typescript\n * import { definePrompt } from '@standardagents/spec';\n * import { z } from 'zod';\n *\n * export default definePrompt({\n * name: 'customer_support',\n * toolDescription: 'Handle customer support inquiries',\n * model: 'gpt-4o',\n * prompt: 'You are a helpful customer support agent.',\n * tools: ['search_knowledge_base', 'create_ticket'],\n * includeChat: true,\n * requiredSchema: z.object({\n * query: z.string().describe('The customer inquiry'),\n * }),\n * });\n * ```\n */\nexport function definePrompt<N extends string, S extends ToolArgs = never>(\n options: PromptDefinition<N, S>\n): PromptDefinition<N, S> {\n // Validate required fields at runtime\n if (!options.name) {\n throw new Error('Prompt name is required');\n }\n if (!options.toolDescription) {\n throw new Error('Prompt toolDescription is required');\n }\n if (!options.model) {\n throw new Error('Prompt model is required');\n }\n if (!options.prompt) {\n throw new Error('Prompt content is required');\n }\n\n // Validate toolChoice is a known value\n if (\n options.toolChoice &&\n !['auto', 'none', 'required'].includes(options.toolChoice)\n ) {\n throw new Error(\n `Invalid toolChoice '${options.toolChoice}'. Must be one of: auto, none, required`\n );\n }\n\n // Validate reasoning configuration\n if (options.reasoning) {\n // Validate level is 0-100 if provided\n if (options.reasoning.level !== undefined) {\n if (typeof options.reasoning.level !== 'number' || options.reasoning.level < 0 || options.reasoning.level > 100) {\n throw new Error('reasoning.level must be a number between 0 and 100');\n }\n }\n\n // Validate effort is a known value (deprecated but still supported)\n if (options.reasoning.effort && !['low', 'medium', 'high'].includes(options.reasoning.effort)) {\n throw new Error(\n `Invalid reasoning.effort '${options.reasoning.effort}'. Must be one of: low, medium, high`\n );\n }\n\n // Warn if both level and effort are provided\n if (options.reasoning.level !== undefined && options.reasoning.effort !== undefined) {\n console.warn('Both reasoning.level and reasoning.effort provided. level takes precedence. effort is deprecated.');\n }\n }\n\n // Validate recentImageThreshold is a positive number\n if (\n options.recentImageThreshold !== undefined &&\n (options.recentImageThreshold <= 0 ||\n !Number.isInteger(options.recentImageThreshold))\n ) {\n throw new Error('recentImageThreshold must be a positive integer');\n }\n\n return options;\n}\n","/**\n * Hook definition types for Standard Agents.\n *\n * Hooks allow intercepting and modifying agent behavior at key points\n * in the execution lifecycle. They enable logging, validation,\n * transformation, and side effects.\n *\n * Hooks receive ThreadState as their first parameter, providing full\n * access to thread operations and execution state.\n *\n * @module\n */\n\nimport type { ThreadState, Message } from './threads.js';\n\n// ============================================================================\n// Hook Context Types\n// ============================================================================\n\n/**\n * Hook context is ThreadState.\n *\n * Hooks receive the full ThreadState, which includes identity, message access,\n * resource loading, event emission, and execution state (when available).\n *\n * @example\n * ```typescript\n * const hook = defineHook('filter_messages', async (state, messages) => {\n * console.log(`Thread: ${state.threadId}`);\n * if (state.execution) {\n * console.log(`Turn: ${state.execution.turnCount}`);\n * }\n * return messages.slice(-10);\n * });\n * ```\n */\nexport type HookContext = ThreadState;\n\n/**\n * Message structure for hook processing.\n *\n * Re-exported from threads.ts for convenience.\n * @see Message\n */\nexport type HookMessage = Message;\n\n/**\n * Tool call structure for hook processing.\n */\nexport interface HookToolCall {\n /** Unique tool call identifier */\n id: string;\n /** Always 'function' for tool calls */\n type: 'function';\n /** Function details */\n function: {\n /** Tool name */\n name: string;\n /** JSON-encoded arguments */\n arguments: string;\n };\n}\n\n/**\n * Tool result structure for hook processing.\n */\nexport interface HookToolResult {\n /** Execution status */\n status: 'success' | 'error';\n /** Result string (for successful executions) */\n result?: string;\n /** Error message (for failed executions) */\n error?: string;\n /** Stack trace (for debugging) */\n stack?: string;\n}\n\n/**\n * LLM message format for prefilter hook.\n */\nexport interface LLMMessage {\n /** Message role */\n role: string;\n /** Message content */\n content: string | null;\n /** Tool calls (parsed) */\n tool_calls?: unknown;\n /** Tool call ID */\n tool_call_id?: string;\n /** Tool name */\n name?: string;\n}\n\n// ============================================================================\n// Hook Signatures\n// ============================================================================\n\n/**\n * Hook signatures for all available hooks.\n *\n * Each hook has a specific signature that defines when it's called\n * and what data it receives. Hooks can modify data by returning\n * transformed values or perform side effects.\n *\n * All hooks receive ThreadState as their first parameter.\n *\n * @template State - The state type (defaults to ThreadState)\n * @template Msg - The message type (defaults to HookMessage)\n * @template ToolCall - The tool call type (defaults to HookToolCall)\n * @template ToolResult - The tool result type (defaults to HookToolResult)\n */\nexport interface HookSignatures<\n State = ThreadState,\n Msg = HookMessage,\n ToolCall = HookToolCall,\n ToolResult = HookToolResult,\n> {\n /**\n * Called before messages are filtered and sent to the LLM.\n *\n * Receives raw message rows from storage before any transformation.\n * Use for filtering, sorting, or augmenting message history.\n *\n * @param state - Thread state\n * @param messages - Array of messages from storage\n * @returns Modified message array\n *\n * @example\n * ```typescript\n * defineHook('filter_messages', async (state, messages) => {\n * // Only include messages from last 10 turns\n * return messages.slice(-10);\n * });\n * ```\n */\n filter_messages: (state: State, messages: Msg[]) => Promise<Msg[]>;\n\n /**\n * Called after message history is loaded and before sending to LLM.\n *\n * Receives messages already transformed into chat completion format.\n * Use for final adjustments before LLM request.\n *\n * @param state - Thread state\n * @param messages - Array of LLM-formatted messages\n * @returns Modified LLM message array\n *\n * @example\n * ```typescript\n * defineHook('prefilter_llm_history', async (state, messages) => {\n * // Add a reminder to the last user message\n * return messages;\n * });\n * ```\n */\n prefilter_llm_history: (\n state: State,\n messages: LLMMessage[]\n ) => Promise<LLMMessage[]>;\n\n /**\n * Called before a message is created in the database.\n *\n * Receives the message data before insertion. Return modified data\n * to transform the message before storage.\n *\n * @param state - Thread state\n * @param message - Message data to be created\n * @returns Modified message data\n *\n * @example\n * ```typescript\n * defineHook('before_create_message', async (state, message) => {\n * // Add metadata to all messages\n * return { ...message, metadata: { processed: true } };\n * });\n * ```\n */\n before_create_message: (\n state: State,\n message: Record<string, unknown>\n ) => Promise<Record<string, unknown>>;\n\n /**\n * Called after a message is created in the database.\n *\n * Use for logging, analytics, or triggering side effects after\n * message creation. Cannot modify the message.\n *\n * @param state - Thread state\n * @param message - The created message data\n *\n * @example\n * ```typescript\n * defineHook('after_create_message', async (state, message) => {\n * console.log(`Message created: ${message.id}`);\n * });\n * ```\n */\n after_create_message: (\n state: State,\n message: Record<string, unknown>\n ) => Promise<void>;\n\n /**\n * Called before a message is updated in the database.\n *\n * Receives the message ID and update data. Return modified updates\n * to transform the changes before storage.\n *\n * @param state - Thread state\n * @param messageId - ID of message being updated\n * @param updates - Update data\n * @returns Modified update data\n *\n * @example\n * ```typescript\n * defineHook('before_update_message', async (state, messageId, updates) => {\n * return { ...updates, updated_at: Date.now() };\n * });\n * ```\n */\n before_update_message: (\n state: State,\n messageId: string,\n updates: Record<string, unknown>\n ) => Promise<Record<string, unknown>>;\n\n /**\n * Called after a message is updated in the database.\n *\n * Use for logging, analytics, or triggering side effects after\n * message update. Cannot modify the message.\n *\n * @param state - Thread state\n * @param message - The updated message\n *\n * @example\n * ```typescript\n * defineHook('after_update_message', async (state, message) => {\n * console.log(`Message updated: ${message.id}`);\n * });\n * ```\n */\n after_update_message: (state: State, message: Msg) => Promise<void>;\n\n /**\n * Called before a tool result is stored in the database.\n *\n * Receives the tool call and result before storage. Return modified\n * result to transform before storage.\n *\n * @param state - Thread state\n * @param toolCall - The tool call that was executed\n * @param toolResult - The result from tool execution\n * @returns Modified tool result data\n *\n * @example\n * ```typescript\n * defineHook('before_store_tool_result', async (state, toolCall, toolResult) => {\n * // Sanitize sensitive data from results\n * return { ...toolResult, result: sanitize(toolResult.result) };\n * });\n * ```\n */\n before_store_tool_result: (\n state: State,\n toolCall: Record<string, unknown>,\n toolResult: Record<string, unknown>\n ) => Promise<Record<string, unknown>>;\n\n /**\n * Called after a successful tool call.\n *\n * Receives the tool call and its result. Can return a modified result\n * or null to use the original. Use for logging, transformation, or\n * post-processing of successful tool executions.\n *\n * @param state - Thread state\n * @param toolCall - The executed tool call\n * @param toolResult - The successful result\n * @returns Modified result or null for original\n *\n * @example\n * ```typescript\n * defineHook('after_tool_call_success', async (state, toolCall, toolResult) => {\n * console.log(`Tool ${toolCall.function.name} succeeded`);\n * return null; // Use original result\n * });\n * ```\n */\n after_tool_call_success: (\n state: State,\n toolCall: ToolCall,\n toolResult: ToolResult\n ) => Promise<ToolResult | null>;\n\n /**\n * Called after a failed tool call.\n *\n * Receives the tool call and error result. Can return a modified result\n * or null to use the original. Use for error handling, logging, or\n * recovery attempts.\n *\n * @param state - Thread state\n * @param toolCall - The failed tool call\n * @param toolResult - The error result\n * @returns Modified result or null for original\n *\n * @example\n * ```typescript\n * defineHook('after_tool_call_failure', async (state, toolCall, toolResult) => {\n * console.error(`Tool ${toolCall.function.name} failed: ${toolResult.error}`);\n * return null; // Use original error\n * });\n * ```\n */\n after_tool_call_failure: (\n state: State,\n toolCall: ToolCall,\n toolResult: ToolResult\n ) => Promise<ToolResult | null>;\n}\n\n/**\n * Valid hook names.\n */\nexport type HookName = keyof HookSignatures;\n\n// ============================================================================\n// Hook Definition\n// ============================================================================\n\n/**\n * Hook definition tuple.\n *\n * A hook definition is a tuple containing the hook name and implementation.\n *\n * @template K - The hook name\n * @template State - The state type (defaults to ThreadState)\n * @template Msg - The message type\n * @template ToolCall - The tool call type\n * @template ToolResult - The tool result type\n */\nexport type HookDefinition<\n K extends HookName = HookName,\n State = ThreadState,\n Msg = HookMessage,\n ToolCall = HookToolCall,\n ToolResult = HookToolResult,\n> = [K, HookSignatures<State, Msg, ToolCall, ToolResult>[K]];\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Define a hook with strict typing based on hook name.\n *\n * Hooks intercept specific points in the agent execution lifecycle.\n * The hook name determines the function signature and when it's called.\n * All hooks receive ThreadState as their first parameter.\n *\n * @template K - The hook name (determines signature)\n * @param hookName - Name of the hook to define\n * @param implementation - Hook implementation function\n * @returns The implementation function (for registration)\n *\n * @example\n * ```typescript\n * // Filter messages to last 10\n * export default defineHook('filter_messages', async (state, messages) => {\n * return messages.slice(-10);\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Log all tool successes\n * export default defineHook('after_tool_call_success', async (state, toolCall, result) => {\n * console.log(`Tool ${toolCall.function.name} completed`);\n * return null; // Use original result\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Transform messages before LLM\n * export default defineHook('prefilter_llm_history', async (state, messages) => {\n * // Add instruction to last message\n * const last = messages[messages.length - 1];\n * if (last?.role === 'user' && typeof last.content === 'string') {\n * last.content += '\\n\\nRemember to be concise.';\n * }\n * return messages;\n * });\n * ```\n */\nexport function defineHook<K extends keyof HookSignatures>(\n hookName: K,\n implementation: HookSignatures[K]\n): HookSignatures[K] {\n // Validate hook name at runtime\n const validHooks: HookName[] = [\n 'filter_messages',\n 'prefilter_llm_history',\n 'before_create_message',\n 'after_create_message',\n 'before_update_message',\n 'after_update_message',\n 'before_store_tool_result',\n 'after_tool_call_success',\n 'after_tool_call_failure',\n ];\n\n if (!validHooks.includes(hookName)) {\n throw new Error(\n `Invalid hook name '${hookName}'. Valid hooks: ${validHooks.join(', ')}`\n );\n }\n\n return implementation;\n}\n","/**\n * Effect definition types for Standard Agents.\n *\n * Effects are scheduled operations that run outside the tool execution context.\n * They enable implementation-specific side effects with optional delay support,\n * leveraging the runtime's scheduling mechanism (e.g., Cloudflare Durable Object alarms).\n *\n * Unlike tools, effects:\n * - Do not return results to the LLM\n * - Can be delayed for future execution\n * - Run independently of the conversation flow\n * - Are ideal for notifications, cleanup, and async operations\n *\n * @module\n */\n\nimport type { z } from 'zod';\nimport type { ThreadState } from './threads.js';\nimport type { ToolArgs } from './tools.js';\n\n// ============================================================================\n// Effect Function Types\n// ============================================================================\n\n/**\n * Effect function signature.\n *\n * Effects are async functions that receive a ThreadState and\n * optionally validated arguments. Unlike tools, they return void\n * since results are not included in the conversation.\n *\n * @template State - The state type (defaults to ThreadState)\n * @template Args - The Zod schema for effect arguments, or null for no args\n */\nexport type Effect<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n> = Args extends ToolArgs\n ? (state: State, args: z.infer<Args>) => Promise<void> | void\n : (state: State) => Promise<void> | void;\n\n/**\n * Return type of defineEffect function.\n *\n * A tuple containing:\n * - Effect description string\n * - Argument schema (or null)\n * - Effect implementation function\n */\nexport type EffectDefinition<\n State = ThreadState,\n Args extends ToolArgs | null = null,\n> = [string, Args, Effect<State, Args>];\n\n// ============================================================================\n// Scheduled Effect Types\n// ============================================================================\n\n/**\n * Record of a scheduled effect.\n *\n * Returned by getScheduledEffects() to inspect pending effects.\n */\nexport interface ScheduledEffect {\n /** Unique identifier for this scheduled effect. */\n id: string;\n /** Name of the effect to execute. */\n name: string;\n /** Arguments to pass to the effect handler. */\n args: Record<string, unknown>;\n /** When the effect is scheduled to run (microseconds since epoch). */\n scheduledAt: number;\n /** When the effect was created (microseconds since epoch). */\n createdAt: number;\n}\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Define an effect with arguments.\n *\n * @param description - Description of what the effect does\n * @param args - Zod schema for validating effect arguments\n * @param handler - The effect implementation function\n * @returns A tuple containing the effect definition\n */\nexport function defineEffect<State = ThreadState, Args extends ToolArgs = ToolArgs>(\n description: string,\n args: Args,\n handler: Effect<State, Args>\n): EffectDefinition<State, Args>;\n\n/**\n * Define an effect without arguments.\n *\n * @param description - Description of what the effect does\n * @param handler - The effect implementation function\n * @returns A tuple containing the effect definition\n */\nexport function defineEffect<State = ThreadState>(\n description: string,\n handler: Effect<State, null>\n): EffectDefinition<State, null>;\n\n/**\n * Defines an effect that can be scheduled for execution.\n *\n * Effects are ideal for operations that should happen outside the\n * main conversation flow, such as:\n * - Sending notification emails after a delay\n * - Triggering webhooks\n * - Scheduling cleanup tasks\n * - Running background data processing\n *\n * @example\n * ```typescript\n * import { defineEffect } from '@standardagents/spec';\n * import { z } from 'zod';\n *\n * export default defineEffect(\n * 'Send a reminder email after a delay',\n * z.object({\n * to: z.string().email().describe('Recipient email'),\n * subject: z.string().describe('Email subject'),\n * body: z.string().describe('Email body'),\n * }),\n * async (state, args) => {\n * const env = state._notPackableRuntimeContext?.env as any;\n * await env.EMAIL_SERVICE.send({\n * to: args.to,\n * subject: args.subject,\n * body: args.body,\n * });\n * }\n * );\n * ```\n *\n * @example\n * ```typescript\n * // Schedule an effect from a tool\n * const effectId = state.scheduleEffect(\n * 'send_reminder_email',\n * { to: 'user@example.com', subject: 'Reminder', body: 'Hello!' },\n * 30 * 60 * 1000 // 30 minutes\n * );\n * ```\n */\nexport function defineEffect<\n State = ThreadState,\n Args extends ToolArgs = ToolArgs,\n>(\n description: string,\n argsOrHandler: Args | Effect<State, null>,\n maybeHandler?: Effect<State, Args>\n): EffectDefinition<State, Args | null> {\n if (maybeHandler) {\n return [\n description,\n argsOrHandler as Args,\n maybeHandler,\n ];\n }\n return [\n description,\n null,\n argsOrHandler as Effect<State, null>,\n ];\n}\n","/**\n * Agent definition types for Standard Agents.\n *\n * Agents orchestrate conversations between AI models (dual_ai) or between\n * AI and human users (ai_human). They define the prompts, stop conditions,\n * and behavioral rules for each side of the conversation.\n *\n * @module\n */\n\n// ============================================================================\n// Agent Types\n// ============================================================================\n\n/**\n * Agent conversation type.\n *\n * - `ai_human`: AI conversing with a human user (most common)\n * - `dual_ai`: Two AI participants conversing with each other\n */\nexport type AgentType = 'ai_human' | 'dual_ai';\n\n// ============================================================================\n// Side Configuration\n// ============================================================================\n\n/**\n * Configuration for one side of an agent conversation.\n *\n * Each side has a prompt, stop conditions, and turn limits.\n * For `ai_human` agents, only sideA (the AI) needs configuration.\n * For `dual_ai` agents, both sides need configuration.\n *\n * @template Prompt - The prompt reference type (string or type-safe union)\n * @template Callable - The callable reference type (string or type-safe union)\n *\n * @example\n * ```typescript\n * const sideConfig: SideConfig = {\n * label: 'Support Agent',\n * prompt: 'customer_support',\n * stopOnResponse: true,\n * maxSteps: 10,\n * };\n * ```\n */\nexport interface SideConfig<\n Prompt extends string = string,\n Callable extends string = string,\n> {\n /**\n * Custom label for this side of the conversation.\n * Used in UI and logs for clarity.\n *\n * @example 'Support Agent', 'Customer', 'ATC', 'Pilot'\n */\n label?: string;\n\n /**\n * The prompt to use for this side.\n * Must reference a prompt defined in agents/prompts/.\n */\n prompt: Prompt;\n\n /**\n * Stop this side's turn when it returns a text response (no tool calls).\n * When true, the side's turn ends after producing a message without tools.\n * @default true\n */\n stopOnResponse?: boolean;\n\n /**\n * Stop this side's turn when a specific tool is called.\n * Overrides stopOnResponse when the named tool is invoked.\n * Requires stopToolResponseProperty to extract the result.\n */\n stopTool?: Callable;\n\n /**\n * Property to extract from the stop tool's result.\n * Required when stopTool is set.\n * The extracted value is used to determine the conversation outcome.\n */\n stopToolResponseProperty?: string;\n\n /**\n * Maximum steps for this side before forcing a stop.\n * Safety limit to prevent runaway execution.\n * A step is one complete LLM request/response cycle.\n */\n maxSteps?: number;\n\n /**\n * Tool that ends the entire session when called.\n * Different from stopTool - this ends the session for both sides,\n * not just this side's turn.\n */\n endSessionTool?: Callable;\n}\n\n// ============================================================================\n// Agent Definition\n// ============================================================================\n\n/**\n * Agent definition configuration.\n *\n * @template N - The agent name as a string literal type\n * @template Prompt - The prompt reference type (string or type-safe union)\n * @template Callable - The callable reference type (string or type-safe union)\n *\n * @example\n * ```typescript\n * import { defineAgent } from '@standardagents/spec';\n *\n * export default defineAgent({\n * name: 'support_agent',\n * title: 'Customer Support Agent',\n * type: 'ai_human',\n * sideA: {\n * label: 'Support',\n * prompt: 'customer_support',\n * stopOnResponse: true,\n * },\n * });\n * ```\n */\nexport interface AgentDefinition<\n N extends string = string,\n Prompt extends string = string,\n Callable extends string = string,\n> {\n /**\n * Unique name for this agent.\n * Used as the identifier for thread creation and handoffs.\n * Should be snake_case (e.g., 'support_agent', 'research_flow').\n */\n name: N;\n\n /**\n * Human-readable title for the agent.\n * Optional - if not provided, the name will be used.\n * @deprecated Use name instead. Title will be removed in a future version.\n */\n title?: string;\n\n /**\n * Agent conversation type.\n *\n * - `ai_human`: AI conversing with a human user (default)\n * - `dual_ai`: Two AI participants conversing\n *\n * @default 'ai_human'\n */\n type?: AgentType;\n\n /**\n * Maximum total turns across both sides.\n * Only applies to `dual_ai` agents.\n * Prevents infinite loops in AI-to-AI conversations.\n */\n maxSessionTurns?: number;\n\n /**\n * Configuration for Side A.\n * For `ai_human`: This is the AI side.\n * For `dual_ai`: This is the first AI participant.\n */\n sideA: SideConfig<Prompt, Callable>;\n\n /**\n * Configuration for Side B.\n * For `ai_human`: Optional, the human side doesn't need config.\n * For `dual_ai`: Required, the second AI participant.\n */\n sideB?: SideConfig<Prompt, Callable>;\n\n /**\n * Expose this agent as a tool for other prompts.\n * Enables agent composition and handoffs.\n * When true, other prompts can invoke this agent as a tool.\n * @default false\n */\n exposeAsTool?: boolean;\n\n /**\n * Description shown when agent is used as a tool.\n * Required if exposeAsTool is true.\n * Should clearly describe what this agent does.\n */\n toolDescription?: string;\n\n /**\n * Brief description of what this agent does.\n * Useful for UIs and documentation.\n *\n * @example 'Handles customer support inquiries and resolves issues'\n */\n description?: string;\n\n /**\n * Icon URL or absolute path for the agent.\n * Absolute paths (starting with `/`) are converted to full URLs in API responses.\n *\n * @example 'https://example.com/icon.svg' or '/icons/support.svg'\n */\n icon?: string;\n\n /**\n * Thread environment variables for this agent.\n * These values are merged with prompt and thread tenvs when creating a thread.\n *\n * Merge priority (later wins):\n * 1. Prompt tenvs (lowest)\n * 2. Agent tenvs (this field)\n * 3. Thread tenvs (highest)\n *\n * @example\n * ```typescript\n * tenvs: {\n * vectorStoreId: 'vs_agent_default',\n * apiEndpoint: 'https://api.example.com',\n * }\n * ```\n */\n tenvs?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Define Function\n// ============================================================================\n\n/**\n * Defines an agent configuration.\n *\n * Agents orchestrate conversations between AI models, or between AI and\n * human users. They use prompts to configure each side of the conversation\n * and define stop conditions to control conversation flow.\n *\n * @template N - The agent name as a string literal type\n * @param options - Agent configuration options\n * @returns The agent definition for registration\n *\n * @example\n * ```typescript\n * // agents/agents/support_agent.ts\n * import { defineAgent } from '@standardagents/spec';\n *\n * export default defineAgent({\n * name: 'support_agent',\n * title: 'Customer Support Agent',\n * type: 'ai_human',\n * sideA: {\n * label: 'Support',\n * prompt: 'customer_support',\n * stopOnResponse: true,\n * endSessionTool: 'close_ticket',\n * },\n * exposeAsTool: true,\n * toolDescription: 'Hand off to customer support',\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Dual AI agent (two AIs conversing)\n * export default defineAgent({\n * name: 'debate_agent',\n * title: 'AI Debate',\n * type: 'dual_ai',\n * maxSessionTurns: 10,\n * sideA: {\n * label: 'Pro',\n * prompt: 'debate_pro',\n * stopOnResponse: true,\n * },\n * sideB: {\n * label: 'Con',\n * prompt: 'debate_con',\n * stopOnResponse: true,\n * endSessionTool: 'conclude_debate',\n * },\n * });\n * ```\n */\nexport function defineAgent<N extends string>(\n options: AgentDefinition<N>\n): AgentDefinition<N> {\n // Validate required fields at runtime\n if (!options.name) {\n throw new Error('Agent name is required');\n }\n if (!options.sideA) {\n throw new Error('Agent sideA configuration is required');\n }\n if (!options.sideA.prompt) {\n throw new Error('Agent sideA.prompt is required');\n }\n\n // Set default type\n const type = options.type ?? 'ai_human';\n\n // Validate dual_ai requires sideB\n if (type === 'dual_ai') {\n if (!options.sideB) {\n throw new Error('Agent sideB configuration is required for dual_ai type');\n }\n if (!options.sideB.prompt) {\n throw new Error('Agent sideB.prompt is required for dual_ai type');\n }\n }\n\n // Validate exposeAsTool requires toolDescription\n if (options.exposeAsTool && !options.toolDescription) {\n throw new Error('toolDescription is required when exposeAsTool is true');\n }\n\n // Validate stopTool requires stopToolResponseProperty\n if (options.sideA.stopTool && !options.sideA.stopToolResponseProperty) {\n throw new Error('sideA.stopToolResponseProperty is required when sideA.stopTool is set');\n }\n if (options.sideB?.stopTool && !options.sideB.stopToolResponseProperty) {\n throw new Error('sideB.stopToolResponseProperty is required when sideB.stopTool is set');\n }\n\n // Validate maxSteps is positive\n if (options.sideA.maxSteps !== undefined && options.sideA.maxSteps <= 0) {\n throw new Error('sideA.maxSteps must be a positive number');\n }\n if (options.sideB?.maxSteps !== undefined && options.sideB.maxSteps <= 0) {\n throw new Error('sideB.maxSteps must be a positive number');\n }\n if (\n options.maxSessionTurns !== undefined &&\n options.maxSessionTurns !== null &&\n options.maxSessionTurns <= 0\n ) {\n throw new Error('maxSessionTurns must be a positive number');\n }\n\n // Validate type is a known value\n if (!['ai_human', 'dual_ai'].includes(type)) {\n throw new Error(`Invalid type '${type}'. Must be one of: ai_human, dual_ai`);\n }\n\n return {\n ...options,\n type,\n };\n}\n","/**\n * Endpoint definition types for Standard Agents.\n *\n * Endpoints expose HTTP APIs for interacting with agent threads.\n * They can be standard controllers or thread-specific endpoints\n * with automatic ThreadState injection.\n *\n * @module\n */\n\nimport type { ThreadState } from './threads.js';\n\n// ============================================================================\n// Virtual Module Types\n// ============================================================================\n\n/**\n * Virtual module loader function.\n *\n * Lazy-loads a module definition on demand.\n */\nexport type VirtualModuleLoader<T> = () => Promise<T>;\n\n/**\n * Registry of virtual modules by name.\n *\n * Maps module names to their lazy loaders.\n */\nexport type VirtualModuleRegistry<T> = Record<string, VirtualModuleLoader<T>>;\n\n// ============================================================================\n// Controller Types\n// ============================================================================\n\n/**\n * Controller context passed to route handlers.\n *\n * Contains the request, URL parameters, environment bindings,\n * and optionally the virtual module registries injected at runtime.\n *\n * Note: The `env` property contains implementation-specific bindings.\n * Cloudflare implementations may include Durable Object namespaces,\n * KV namespaces, etc. Other implementations may provide different bindings.\n */\nexport interface ControllerContext {\n /** The incoming HTTP request */\n req: Request;\n\n /** URL path parameters extracted by the router */\n params: Record<string, string>;\n\n /** Parsed URL object */\n url: URL;\n\n /**\n * Environment bindings (implementation-specific).\n *\n * Contains platform-specific bindings like Durable Objects, KV, etc.\n * The exact contents depend on the runtime implementation.\n */\n env: Record<string, unknown>;\n\n /** Registry of agent definitions (injected at runtime) */\n agents?: VirtualModuleRegistry<unknown>;\n\n /** Available agent names */\n agentNames?: string[];\n\n /** Registry of prompt definitions (injected at runtime) */\n prompts?: VirtualModuleRegistry<unknown>;\n\n /** Available prompt names */\n promptNames?: string[];\n\n /** Registry of model definitions (injected at runtime) */\n models?: VirtualModuleRegistry<unknown>;\n\n /** Available model names */\n modelNames?: string[];\n\n /** Registry of tool definitions (injected at runtime) */\n tools?: VirtualModuleRegistry<unknown>;\n\n /** Registry of hook definitions (injected at runtime) */\n hooks?: VirtualModuleRegistry<unknown>;\n\n /** Additional configuration (injected at runtime) */\n config?: Record<string, unknown>;\n}\n\n/**\n * Controller return types.\n *\n * Controllers can return various types that are automatically\n * converted to appropriate HTTP responses.\n */\nexport type ControllerReturn =\n | string\n | Promise<string>\n | Response\n | Promise<Response>\n | ReadableStream\n | Promise<ReadableStream>\n | null\n | Promise<null>\n | void\n | Promise<void>\n | Promise<object>\n | object;\n\n/**\n * Controller function type.\n *\n * Controllers handle HTTP requests and return responses.\n * The return value is automatically converted to a Response.\n *\n * @example\n * ```typescript\n * const handler: Controller = async ({ req, params, url }) => {\n * return { message: 'Hello, World!' };\n * };\n * ```\n */\nexport type Controller = (context: ControllerContext) => ControllerReturn;\n\n// ============================================================================\n// Thread Endpoint Types\n// ============================================================================\n\n/**\n * Thread endpoint handler function.\n *\n * Receives the HTTP request and the ThreadState for the requested thread.\n * The thread is automatically looked up by ID from URL parameters.\n */\nexport type ThreadEndpointHandler = (\n req: Request,\n state: ThreadState\n) => Response | Promise<Response>;\n\n// ============================================================================\n// Define Functions\n// ============================================================================\n\n/**\n * Define a standard HTTP controller.\n *\n * Controllers handle HTTP requests and return responses. They have\n * access to the controller context including virtual module registries.\n *\n * @param controller - The controller function\n * @returns The controller for registration\n *\n * @example\n * ```typescript\n * // agents/api/health.get.ts\n * import { defineController } from '@standardagents/spec';\n *\n * export default defineController(async () => {\n * return { status: 'ok', timestamp: Date.now() };\n * });\n * ```\n *\n * @example\n * ```typescript\n * // agents/api/agents/index.get.ts\n * import { defineController } from '@standardagents/spec';\n *\n * export default defineController(async ({ agentNames }) => {\n * return { agents: agentNames };\n * });\n * ```\n */\nexport function defineController(controller: Controller): Controller {\n return controller;\n}\n\n/**\n * Define a thread-specific endpoint.\n *\n * Thread endpoints automatically look up the thread by ID from URL params\n * and provide access to the ThreadState. The handler receives full\n * ThreadState with messages, logs, resource loading, event emission,\n * and (when executing) execution state.\n *\n * @param handler - The handler function receiving request and ThreadState\n * @returns A Controller that can be used with the router\n *\n * @example\n * ```typescript\n * // agents/api/threads/[id]/status.get.ts\n * import { defineThreadEndpoint } from '@standardagents/spec';\n *\n * export default defineThreadEndpoint(async (req, state) => {\n * const { messages, total } = await state.getMessages({ limit: 1 });\n * return Response.json({\n * threadId: state.threadId,\n * agent: state.agentId,\n * messageCount: total,\n * });\n * });\n * ```\n *\n * @example\n * ```typescript\n * // agents/api/threads/[id]/export.get.ts\n * import { defineThreadEndpoint } from '@standardagents/spec';\n *\n * export default defineThreadEndpoint(async (req, state) => {\n * const { messages } = await state.getMessages();\n * return Response.json({\n * thread: {\n * id: state.threadId,\n * agent: state.agentId,\n * user: state.userId,\n * createdAt: state.createdAt,\n * },\n * messages,\n * });\n * });\n * ```\n *\n * @example\n * ```typescript\n * // agents/api/threads/[id]/invoke.post.ts\n * import { defineThreadEndpoint } from '@standardagents/spec';\n *\n * export default defineThreadEndpoint(async (req, state) => {\n * const { tool, args } = await req.json();\n *\n * // Queue a tool for execution (starts execution if not running)\n * state.queueTool(tool, args);\n *\n * return Response.json({ queued: true });\n * });\n * ```\n */\nexport function defineThreadEndpoint(\n handler: ThreadEndpointHandler\n): Controller {\n // The actual implementation is provided by the runtime.\n // This is a marker function that the router recognizes.\n // The runtime wraps this to provide ThreadState.\n return (handler as unknown) as Controller;\n}\n","/**\n * Provider types for Standard Agents.\n *\n * These types define the interface between Standard Agents and LLM providers.\n * Provider packages implement these types to integrate with different LLM APIs.\n *\n * @module\n */\n\nimport type { ModelCapabilities } from './models';\nimport type { ToolDefinition, ToolArgs, ToolTenvs } from './tools';\n\n// =============================================================================\n// Response Summary (for async metadata fetching)\n// =============================================================================\n\n/**\n * Stripped-down response summary for async metadata fetching.\n * Intentionally excludes content/attachments to avoid passing large data.\n */\nexport interface ResponseSummary {\n /** Provider-specific response/generation ID */\n responseId?: string;\n /** Model that handled the request */\n model: string;\n /** How the response ended */\n finishReason: ProviderFinishReason;\n /** Token usage (without detailed breakdowns) */\n usage: {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n };\n}\n\n// =============================================================================\n// Provider Interface\n// =============================================================================\n\n/**\n * Model information returned by provider's getModels method.\n */\nexport interface ProviderModelInfo {\n /** The model identifier used in API calls (e.g., 'gpt-4o', 'anthropic/claude-3-opus') */\n id: string;\n /** Human-readable display name */\n name: string;\n /** Optional description of the model */\n description?: string;\n /** Optional context window size in tokens */\n contextLength?: number;\n /** Optional icon identifier for UI display */\n iconId?: string;\n /** Optional slug for additional lookups (e.g., OpenRouter endpoint queries) */\n slug?: string;\n}\n\n/**\n * Provider interface - thin translation layer between Standard Agents and LLM APIs\n */\nexport interface LLMProviderInterface {\n readonly name: string;\n readonly specificationVersion: '1';\n\n /**\n * Non-streaming generation\n */\n generate(request: ProviderRequest): Promise<ProviderResponse>;\n\n /**\n * Streaming generation\n */\n stream(request: ProviderRequest): Promise<AsyncIterable<ProviderStreamChunk>>;\n\n /**\n * Check if this provider supports a model\n */\n supportsModel?(modelId: string): boolean;\n\n /**\n * List available models from this provider.\n * Optional for backwards compatibility - providers may implement this\n * to enable model selection in the UI.\n *\n * @param filter - Optional search string to filter models by name/id\n * @returns Array of available models\n */\n getModels?(filter?: string): Promise<ProviderModelInfo[]>;\n\n /**\n * Fetch capabilities for a specific model.\n * Optional for backwards compatibility - providers may implement this\n * to enable auto-detection of model features in the UI.\n *\n * @param modelId - The model identifier (e.g., 'gpt-4o', 'anthropic/claude-3-opus')\n * @returns The model capabilities, or null if not available\n */\n getModelCapabilities?(modelId: string): Promise<ModelCapabilities | null>;\n\n /**\n * Get tools embedded in this provider.\n * Optional - providers may implement this to expose built-in tools\n * (e.g., OpenAI's web_search, file_search, code_interpreter).\n *\n * These tools are defined using defineTool() with optional tenv requirements.\n * Tools returned here can be referenced by name in model/prompt definitions.\n *\n * @param modelId - Optional filter to get tools available for a specific model\n * @returns Record of tool name to tool definition (sync or async)\n */\n getTools?(modelId?: string): Record<string, ToolDefinition<any, ToolArgs | null, ToolTenvs | null>> | Promise<Record<string, ToolDefinition<any, ToolArgs | null, ToolTenvs | null>>>;\n\n /**\n * Get an icon for this provider or a specific model.\n * Optional - providers may implement this to provide UI icons.\n *\n * **Preferred return format: SVG data URI** (e.g., 'data:image/svg+xml,...')\n *\n * The data URI format allows icons to be embedded directly in the UI without\n * additional network requests. Use the `svgToDataUri()` helper from provider\n * packages to convert SVG strings.\n *\n * Return value can be:\n * - A data URI (e.g., 'data:image/svg+xml,...') - **PREFERRED**\n * - A full URL (e.g., 'https://example.com/icon.svg')\n * - An icon identifier that the UI resolves (legacy)\n *\n * For providers like OpenRouter that host many models, the modelId parameter\n * allows returning different icons based on the model's origin lab\n * (e.g., 'anthropic/claude-3-opus' -> anthropic icon).\n *\n * @param modelId - Optional model ID to get a model-specific icon\n * @returns SVG data URI (preferred), URL, icon identifier, or undefined\n *\n * @example\n * ```typescript\n * // Provider returning its own icon\n * getIcon() {\n * return svgToDataUri(OPENAI_ICON);\n * }\n *\n * // Provider returning model-specific icon (e.g., OpenRouter)\n * getIcon(modelId?: string) {\n * if (modelId) {\n * const lab = modelId.split('/')[0]; // 'anthropic' from 'anthropic/claude-3'\n * return getLabIconDataUri(lab);\n * }\n * return svgToDataUri(OPENROUTER_ICON);\n * }\n * ```\n */\n getIcon?(modelId?: string): string | undefined;\n\n /**\n * Transform a ProviderRequest to the provider's native format for inspection/debugging.\n * Returns the transformed request as it would be sent to the provider's API.\n *\n * This is used by the admin UI to view logged requests in provider-specific format,\n * helping debug issues like message transformation or tool handling.\n *\n * Implementations should truncate base64 data to ~50 chars for readability.\n *\n * @param request - The Standard Agents format request to transform\n * @returns The transformed request in the provider's native format\n */\n inspectRequest?(request: ProviderRequest): Promise<InspectedRequest>;\n\n /**\n * Fetch additional metadata about a completed response.\n * Called asynchronously after the response is received - does not block the main execution flow.\n *\n * This is useful for providers (like aggregators) where complete metadata isn't available\n * immediately. The execution engine calls this in the background and uses the returned\n * data to update log records.\n *\n * @param summary - Stripped-down response info (no content/attachments)\n * @param signal - Optional abort signal for cancellation\n * @returns Additional metadata or null if unavailable\n */\n getResponseMetadata?(\n summary: ResponseSummary,\n signal?: AbortSignal\n ): Promise<Record<string, unknown> | null>;\n}\n\n// =============================================================================\n// Inspection Types\n// =============================================================================\n\n/**\n * Result from inspectRequest() - the transformed request in provider's native format\n */\nexport interface InspectedRequest {\n /** The transformed request body as it would be sent to the API */\n body: Record<string, unknown>;\n /** Path to the messages array within body (e.g., \"input\" for OpenAI, \"messages\" for others) */\n messagesPath: string;\n /** Optional: Additional metadata about the transformation */\n metadata?: {\n /** The API endpoint that would be called */\n endpoint?: string;\n /** Headers that would be sent (excluding auth) */\n headers?: Record<string, string>;\n };\n}\n\n// =============================================================================\n// Request Types\n// =============================================================================\n\n/**\n * Standard Agent request format - providers translate to their native format\n */\nexport interface ProviderRequest {\n model: string;\n messages: ProviderMessage[];\n tools?: ProviderTool[];\n toolChoice?: 'auto' | 'none' | 'required' | { name: string };\n parallelToolCalls?: boolean;\n maxOutputTokens?: number;\n temperature?: number;\n topP?: number;\n topK?: number;\n stopSequences?: string[];\n reasoning?: {\n level?: number; // 0-100 scale\n maxTokens?: number;\n exclude?: boolean;\n };\n responseFormat?: { type: 'text' } | { type: 'json'; schema?: Record<string, unknown> };\n signal?: AbortSignal;\n providerOptions?: Record<string, unknown>;\n}\n\n// =============================================================================\n// Message Types\n// =============================================================================\n\nexport type ProviderMessage =\n | ProviderSystemMessage\n | ProviderUserMessage\n | ProviderAssistantMessage\n | ProviderToolMessage;\n\nexport interface ProviderSystemMessage {\n role: 'system';\n content: string;\n}\n\nexport interface ProviderUserMessage {\n role: 'user';\n content: ProviderMessageContent;\n}\n\nexport interface ProviderAssistantMessage {\n role: 'assistant';\n content?: string | null;\n reasoning?: string | null;\n reasoningDetails?: ProviderReasoningDetail[];\n toolCalls?: ProviderToolCallPart[];\n}\n\nexport interface ProviderToolMessage {\n role: 'tool';\n toolCallId: string;\n toolName: string;\n content: ProviderToolResultContent;\n attachments?: ProviderAttachment[];\n}\n\n/**\n * Attachment on a provider message (loaded from storage, ready for provider-specific transformation)\n */\nexport interface ProviderAttachment {\n type: 'image' | 'file';\n data: string; // base64\n mediaType: string;\n name?: string;\n path?: string; // Original file path (for logging/debugging)\n}\n\n// =============================================================================\n// Content Types\n// =============================================================================\n\nexport type ProviderMessageContent = string | ContentPart[];\n\nexport type ContentPart =\n | TextPart\n | ImagePart\n | ImageUrlPart\n | FilePart;\n\nexport interface TextPart {\n type: 'text';\n text: string;\n}\n\nexport interface ImagePart {\n type: 'image';\n data: string; // base64 or URL\n mediaType: string; // 'image/jpeg', 'image/png', etc.\n detail?: 'auto' | 'low' | 'high';\n}\n\n/**\n * Image URL content part (OpenAI/OpenRouter format).\n * Used for passing image URLs directly to providers.\n */\nexport interface ImageUrlPart {\n type: 'image_url';\n image_url: {\n url: string; // Can be data URL (data:image/...) or http(s) URL\n detail?: 'auto' | 'low' | 'high';\n };\n}\n\nexport interface FilePart {\n type: 'file';\n data: string; // base64 or URL\n mediaType: string;\n filename?: string;\n}\n\n// =============================================================================\n// Tool Types\n// =============================================================================\n\nexport interface ProviderTool {\n type: 'function';\n function: {\n name: string;\n description: string;\n parameters?: Record<string, unknown>; // JSON Schema\n };\n /**\n * Where this tool is executed:\n * - 'local': Execute locally by the execution engine (default)\n * - 'provider': Executed by the LLM provider, results come in response\n */\n executionMode?: 'local' | 'provider';\n /**\n * Which provider executes this tool (when executionMode='provider')\n * e.g., 'openai', 'anthropic'\n */\n executionProvider?: string;\n}\n\nexport interface ProviderToolCallPart {\n id: string;\n name: string;\n arguments: Record<string, unknown>; // Parsed JSON\n}\n\nexport type ProviderToolResultContent =\n | string\n | { type: 'text'; text: string }\n | { type: 'error'; error: string }\n | ContentPart[];\n\n// =============================================================================\n// Response Types\n// =============================================================================\n\nexport interface ProviderResponse {\n content: string | null;\n reasoning?: string | null;\n reasoningDetails?: ProviderReasoningDetail[];\n toolCalls?: ProviderToolCallPart[];\n images?: ProviderGeneratedImage[];\n finishReason: ProviderFinishReason;\n usage: ProviderUsage;\n metadata?: Record<string, unknown>;\n}\n\nexport type ProviderFinishReason = 'stop' | 'length' | 'tool_calls' | 'content_filter' | 'error';\n\nexport interface ProviderUsage {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n reasoningTokens?: number;\n cachedTokens?: number;\n cost?: number;\n /** The actual provider that fulfilled the request (e.g., 'google', 'anthropic') */\n provider?: string;\n}\n\nexport interface ProviderGeneratedImage {\n /** Unique ID for this generated image (used to link to tool call) */\n id?: string;\n /** Name of the tool that generated this image (e.g., 'image_generation') */\n toolName?: string;\n data: string; // base64 or URL\n mediaType: string;\n revisedPrompt?: string;\n}\n\nexport interface ProviderReasoningDetail {\n type: 'summary' | 'encrypted' | 'text';\n /** Original reasoning ID from provider (e.g., rs_xxx for OpenAI) */\n id?: string;\n text?: string;\n data?: string;\n}\n\n// =============================================================================\n// Streaming Types\n// =============================================================================\n\n/**\n * Web search result from provider\n */\nexport interface ProviderWebSearchResult {\n /** Unique ID of the web search call */\n id: string;\n /** Status of the search */\n status: 'in_progress' | 'searching' | 'completed' | 'failed';\n /** Search actions performed */\n actions?: Array<{\n type: 'search' | 'open_page' | 'find';\n query?: string;\n url?: string;\n pattern?: string;\n sources?: Array<{ type: 'url'; url: string; title?: string }>;\n }>;\n}\n\nexport type ProviderStreamChunk =\n // Content streaming\n | { type: 'content-delta'; delta: string }\n | { type: 'content-done' }\n // Reasoning streaming\n | { type: 'reasoning-delta'; delta: string }\n | { type: 'reasoning-done' }\n // Tool call streaming\n | { type: 'tool-call-start'; id: string; name: string }\n | { type: 'tool-call-delta'; id: string; argumentsDelta: string }\n | { type: 'tool-call-done'; id: string; arguments: Record<string, unknown> }\n // Image generation (partial)\n | { type: 'image-delta'; index: number; data: string }\n | { type: 'image-done'; index: number; image: ProviderGeneratedImage }\n // Web search\n | { type: 'web-search-done'; result: ProviderWebSearchResult }\n // Completion\n | { type: 'finish'; finishReason: ProviderFinishReason; usage: ProviderUsage; reasoningDetails?: ProviderReasoningDetail[] }\n // Errors\n | { type: 'error'; error: string; code?: string };\n\n// =============================================================================\n// Error Types\n// =============================================================================\n\nexport type ProviderErrorCode =\n | 'rate_limit'\n | 'invalid_request'\n | 'auth_error'\n | 'server_error'\n | 'timeout'\n | 'unknown';\n\nexport class ProviderError extends Error {\n constructor(\n message: string,\n public code: ProviderErrorCode,\n public statusCode?: number,\n public retryAfter?: number\n ) {\n super(message);\n this.name = 'ProviderError';\n }\n\n /**\n * Whether this error is retryable\n */\n get isRetryable(): boolean {\n return this.code === 'rate_limit' || this.code === 'server_error' || this.code === 'timeout';\n }\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Maps a 0-100 reasoning level to the model's nearest supported level\n */\nexport function mapReasoningLevel(\n level: number,\n reasoningLevels: Record<number, string | null> | undefined\n): string | null {\n if (!reasoningLevels) return null;\n\n const breakpoints = Object.keys(reasoningLevels)\n .map(Number)\n .sort((a, b) => a - b);\n\n if (breakpoints.length === 0) return null;\n\n // Find nearest breakpoint\n let nearest = breakpoints[0];\n let minDistance = Math.abs(level - nearest);\n\n for (const bp of breakpoints) {\n const distance = Math.abs(level - bp);\n if (distance < minDistance) {\n minDistance = distance;\n nearest = bp;\n }\n }\n\n return reasoningLevels[nearest];\n}\n"],"mappings":"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","names":[]}
|