@standardagents/spec 0.10.0-dev.ffffff → 0.11.0-next.99fb790
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/LICENSE +21 -0
- package/dist/index.d.ts +126 -2
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/LICENSE.txt +0 -48
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 FormKit Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ZodString, ZodNumber, ZodBoolean, ZodNull, ZodLiteral, ZodEnum, ZodOptional, ZodNullable, ZodDefault, ZodArray, ZodObject, ZodRecord, ZodUnion
|
|
1
|
+
import { z, ZodString, ZodNumber, ZodBoolean, ZodNull, ZodLiteral, ZodEnum, ZodOptional, ZodNullable, ZodDefault, ZodArray, ZodObject, ZodRecord, ZodUnion } from 'zod';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Model definition types for Standard Agents.
|
|
@@ -155,6 +155,77 @@ interface ModelDefinition<N extends string = string> {
|
|
|
155
155
|
*/
|
|
156
156
|
declare function defineModel<N extends string>(options: ModelDefinition<N>): ModelDefinition<N>;
|
|
157
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Effect definition types for Standard Agents.
|
|
160
|
+
*
|
|
161
|
+
* Effects are scheduled operations that run outside the tool execution context.
|
|
162
|
+
* They enable implementation-specific side effects with optional delay support,
|
|
163
|
+
* leveraging the runtime's scheduling mechanism (e.g., Cloudflare Durable Object alarms).
|
|
164
|
+
*
|
|
165
|
+
* Unlike tools, effects:
|
|
166
|
+
* - Do not return results to the LLM
|
|
167
|
+
* - Can be delayed for future execution
|
|
168
|
+
* - Run independently of the conversation flow
|
|
169
|
+
* - Are ideal for notifications, cleanup, and async operations
|
|
170
|
+
*
|
|
171
|
+
* @module
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Effect function signature.
|
|
176
|
+
*
|
|
177
|
+
* Effects are async functions that receive a ThreadState and
|
|
178
|
+
* optionally validated arguments. Unlike tools, they return void
|
|
179
|
+
* since results are not included in the conversation.
|
|
180
|
+
*
|
|
181
|
+
* @template State - The state type (defaults to ThreadState)
|
|
182
|
+
* @template Args - The Zod schema for effect arguments, or null for no args
|
|
183
|
+
*/
|
|
184
|
+
type Effect<State = ThreadState, Args extends ToolArgs | null = null> = Args extends ToolArgs ? (state: State, args: z.infer<Args>) => Promise<void> | void : (state: State) => Promise<void> | void;
|
|
185
|
+
/**
|
|
186
|
+
* Return type of defineEffect function.
|
|
187
|
+
*
|
|
188
|
+
* A tuple containing:
|
|
189
|
+
* - Effect description string
|
|
190
|
+
* - Argument schema (or null)
|
|
191
|
+
* - Effect implementation function
|
|
192
|
+
*/
|
|
193
|
+
type EffectDefinition<State = ThreadState, Args extends ToolArgs | null = null> = [string, Args, Effect<State, Args>];
|
|
194
|
+
/**
|
|
195
|
+
* Record of a scheduled effect.
|
|
196
|
+
*
|
|
197
|
+
* Returned by getScheduledEffects() to inspect pending effects.
|
|
198
|
+
*/
|
|
199
|
+
interface ScheduledEffect {
|
|
200
|
+
/** Unique identifier for this scheduled effect. */
|
|
201
|
+
id: string;
|
|
202
|
+
/** Name of the effect to execute. */
|
|
203
|
+
name: string;
|
|
204
|
+
/** Arguments to pass to the effect handler. */
|
|
205
|
+
args: Record<string, unknown>;
|
|
206
|
+
/** When the effect is scheduled to run (microseconds since epoch). */
|
|
207
|
+
scheduledAt: number;
|
|
208
|
+
/** When the effect was created (microseconds since epoch). */
|
|
209
|
+
createdAt: number;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Define an effect with arguments.
|
|
213
|
+
*
|
|
214
|
+
* @param description - Description of what the effect does
|
|
215
|
+
* @param args - Zod schema for validating effect arguments
|
|
216
|
+
* @param handler - The effect implementation function
|
|
217
|
+
* @returns A tuple containing the effect definition
|
|
218
|
+
*/
|
|
219
|
+
declare function defineEffect<State = ThreadState, Args extends ToolArgs = ToolArgs>(description: string, args: Args, handler: Effect<State, Args>): EffectDefinition<State, Args>;
|
|
220
|
+
/**
|
|
221
|
+
* Define an effect without arguments.
|
|
222
|
+
*
|
|
223
|
+
* @param description - Description of what the effect does
|
|
224
|
+
* @param handler - The effect implementation function
|
|
225
|
+
* @returns A tuple containing the effect definition
|
|
226
|
+
*/
|
|
227
|
+
declare function defineEffect<State = ThreadState>(description: string, handler: Effect<State, null>): EffectDefinition<State, null>;
|
|
228
|
+
|
|
158
229
|
/**
|
|
159
230
|
* Thread state types for Standard Agents.
|
|
160
231
|
*
|
|
@@ -590,6 +661,49 @@ interface ThreadState {
|
|
|
590
661
|
* @returns The tool result
|
|
591
662
|
*/
|
|
592
663
|
invokeTool(toolName: string, args: Record<string, unknown>): Promise<ToolResult>;
|
|
664
|
+
/**
|
|
665
|
+
* Schedule an effect for future execution.
|
|
666
|
+
*
|
|
667
|
+
* Effects are executed outside the current conversation flow, making them
|
|
668
|
+
* ideal for delayed operations like sending notifications, triggering
|
|
669
|
+
* webhooks, or running cleanup tasks.
|
|
670
|
+
*
|
|
671
|
+
* @param name - Name of the effect to schedule (must exist in agents/effects/)
|
|
672
|
+
* @param args - Arguments to pass to the effect handler
|
|
673
|
+
* @param delay - Delay in milliseconds before execution (default: 0 for immediate)
|
|
674
|
+
* @returns Unique ID of the scheduled effect (UUID)
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* ```typescript
|
|
678
|
+
* // Schedule a reminder email in 30 minutes
|
|
679
|
+
* const effectId = await state.scheduleEffect(
|
|
680
|
+
* 'send_reminder_email',
|
|
681
|
+
* { to: 'user@example.com', subject: 'Reminder' },
|
|
682
|
+
* 30 * 60 * 1000
|
|
683
|
+
* );
|
|
684
|
+
* ```
|
|
685
|
+
*/
|
|
686
|
+
scheduleEffect(name: string, args: Record<string, unknown>, delay?: number): Promise<string>;
|
|
687
|
+
/**
|
|
688
|
+
* Get scheduled effects for this thread.
|
|
689
|
+
*
|
|
690
|
+
* Returns effects that are pending execution. Can optionally filter
|
|
691
|
+
* by effect name.
|
|
692
|
+
*
|
|
693
|
+
* @param name - Optional effect name to filter by
|
|
694
|
+
* @returns Array of scheduled effect records
|
|
695
|
+
*/
|
|
696
|
+
getScheduledEffects(name?: string): Promise<ScheduledEffect[]>;
|
|
697
|
+
/**
|
|
698
|
+
* Remove a scheduled effect.
|
|
699
|
+
*
|
|
700
|
+
* Cancels a pending effect before it executes. Has no effect if the
|
|
701
|
+
* effect has already executed or doesn't exist.
|
|
702
|
+
*
|
|
703
|
+
* @param id - The effect ID returned by scheduleEffect
|
|
704
|
+
* @returns true if the effect was found and removed, false otherwise
|
|
705
|
+
*/
|
|
706
|
+
removeScheduledEffect(id: string): Promise<boolean>;
|
|
593
707
|
/**
|
|
594
708
|
* Emit a custom event to connected clients.
|
|
595
709
|
*
|
|
@@ -721,6 +835,16 @@ interface ThreadState {
|
|
|
721
835
|
* Use this to access step counts, force turns, or stop execution.
|
|
722
836
|
*/
|
|
723
837
|
execution: ExecutionState | null;
|
|
838
|
+
/**
|
|
839
|
+
* Runtime-specific context that cannot be packed or shared.
|
|
840
|
+
*
|
|
841
|
+
* This property allows runtime implementations to inject non-portable
|
|
842
|
+
* context (e.g., Cloudflare env bindings, database connections).
|
|
843
|
+
*
|
|
844
|
+
* WARNING: Tools using this property cannot be packed, shared, or
|
|
845
|
+
* published as they depend on runtime-specific context.
|
|
846
|
+
*/
|
|
847
|
+
readonly _notPackableRuntimeContext?: Record<string, unknown>;
|
|
724
848
|
}
|
|
725
849
|
|
|
726
850
|
/**
|
|
@@ -1911,4 +2035,4 @@ declare function defineController(controller: Controller): Controller;
|
|
|
1911
2035
|
*/
|
|
1912
2036
|
declare function defineThreadEndpoint(handler: ThreadEndpointHandler): Controller;
|
|
1913
2037
|
|
|
1914
|
-
export { type AgentDefinition, type AgentType, type AttachmentRef, type Controller, type ControllerContext, type ControllerReturn, type ExecutionState, type FileChunk, type FileRecord, type FileStats, type FileStorage, type FindResult, type GetLogsOptions, type GetMessagesOptions, type GrepResult, type HookContext, type HookDefinition, type HookMessage, type HookName, type HookSignatures, type HookToolCall, type HookToolResult, type ImageContent, type InjectMessageInput, type LLMMessage, type Log, type Message, type MessageUpdates, type MessagesResult, type ModelCapabilities, type ModelDefinition, type ModelProvider, type PromptContent, type PromptDefinition, type PromptIncludePart, type PromptInput, type PromptPart, type PromptTextPart, type ReadFileStreamOptions, type ReaddirResult, type ReasoningConfig, type SideConfig, type StructuredPrompt, type SubpromptConfig, type TextContent, type ThreadEndpointHandler, type ThreadMetadata, type ThreadState, type Tool, type ToolArgs, type ToolArgsNode, type ToolArgsRawShape, type ToolAttachment, type ToolConfig, type ToolContent, type ToolDefinition, type ToolResult, type VirtualModuleLoader, type VirtualModuleRegistry, type WriteFileOptions, defineAgent, defineController, defineHook, defineModel, definePrompt, defineThreadEndpoint, defineTool };
|
|
2038
|
+
export { type AgentDefinition, type AgentType, type AttachmentRef, type Controller, type ControllerContext, type ControllerReturn, type Effect, type EffectDefinition, type ExecutionState, type FileChunk, type FileRecord, type FileStats, type FileStorage, type FindResult, type GetLogsOptions, type GetMessagesOptions, type GrepResult, type HookContext, type HookDefinition, type HookMessage, type HookName, type HookSignatures, type HookToolCall, type HookToolResult, type ImageContent, type InjectMessageInput, type LLMMessage, type Log, type Message, type MessageUpdates, type MessagesResult, type ModelCapabilities, type ModelDefinition, type ModelProvider, type PromptContent, type PromptDefinition, type PromptIncludePart, type PromptInput, type PromptPart, type PromptTextPart, type ReadFileStreamOptions, type ReaddirResult, type ReasoningConfig, type ScheduledEffect, type SideConfig, type StructuredPrompt, type SubpromptConfig, type TextContent, type ThreadEndpointHandler, type ThreadMetadata, type ThreadState, type Tool, type ToolArgs, type ToolArgsNode, type ToolArgsRawShape, type ToolAttachment, type ToolConfig, type ToolContent, type ToolDefinition, type ToolResult, type VirtualModuleLoader, type VirtualModuleRegistry, type WriteFileOptions, defineAgent, defineController, defineEffect, defineHook, defineModel, definePrompt, defineThreadEndpoint, defineTool };
|
package/dist/index.js
CHANGED
|
@@ -94,6 +94,22 @@ function defineHook(hookName, implementation) {
|
|
|
94
94
|
return implementation;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// src/effects.ts
|
|
98
|
+
function defineEffect(description, argsOrHandler, maybeHandler) {
|
|
99
|
+
if (maybeHandler) {
|
|
100
|
+
return [
|
|
101
|
+
description,
|
|
102
|
+
argsOrHandler,
|
|
103
|
+
maybeHandler
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
return [
|
|
107
|
+
description,
|
|
108
|
+
null,
|
|
109
|
+
argsOrHandler
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
|
|
97
113
|
// src/agents.ts
|
|
98
114
|
function defineAgent(options) {
|
|
99
115
|
if (!options.name) {
|
|
@@ -151,6 +167,7 @@ function defineThreadEndpoint(handler) {
|
|
|
151
167
|
export {
|
|
152
168
|
defineAgent,
|
|
153
169
|
defineController,
|
|
170
|
+
defineEffect,
|
|
154
171
|
defineHook,
|
|
155
172
|
defineModel,
|
|
156
173
|
definePrompt,
|
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/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 * 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 * const logs = await state.getLogs();\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 * logs,\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"],"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 * const logs = await state.getLogs();\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 * logs,\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":[]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@standardagents/spec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0-next.99fb790",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
|
-
"access": "
|
|
6
|
+
"access": "public",
|
|
7
7
|
"registry": "https://registry.npmjs.org/"
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"types"
|
|
37
37
|
],
|
|
38
38
|
"author": "FormKit Inc.",
|
|
39
|
-
"license": "
|
|
39
|
+
"license": "MIT",
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsup",
|
|
42
42
|
"dev": "tsup --watch",
|
package/LICENSE.txt
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
PROPRIETARY SOFTWARE LICENSE
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024-2025 FormKit Inc. All Rights Reserved.
|
|
4
|
-
|
|
5
|
-
UNLICENSED - DO NOT USE
|
|
6
|
-
|
|
7
|
-
This software and associated documentation files (the "Software") are the sole
|
|
8
|
-
and exclusive property of FormKit Inc. ("FormKit").
|
|
9
|
-
|
|
10
|
-
USE RESTRICTIONS
|
|
11
|
-
|
|
12
|
-
The Software is UNLICENSED and proprietary. NO PERMISSION is granted to use,
|
|
13
|
-
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
14
|
-
the Software, or to permit persons to whom the Software is furnished to do so,
|
|
15
|
-
under any circumstances, without prior written authorization from FormKit Inc.
|
|
16
|
-
|
|
17
|
-
UNAUTHORIZED USE PROHIBITED
|
|
18
|
-
|
|
19
|
-
Any use of this Software without a valid, written license agreement signed by
|
|
20
|
-
authorized officers of FormKit Inc. is strictly prohibited and constitutes
|
|
21
|
-
unauthorized use and infringement of FormKit's intellectual property rights.
|
|
22
|
-
|
|
23
|
-
LICENSING INQUIRIES
|
|
24
|
-
|
|
25
|
-
Organizations interested in licensing this Software should contact:
|
|
26
|
-
enterprise@formkit.com
|
|
27
|
-
|
|
28
|
-
A written license agreement must be executed before any use of this Software
|
|
29
|
-
is authorized.
|
|
30
|
-
|
|
31
|
-
NO WARRANTY
|
|
32
|
-
|
|
33
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
34
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
35
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
36
|
-
FORMKIT INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
37
|
-
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
38
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
39
|
-
|
|
40
|
-
GOVERNING LAW
|
|
41
|
-
|
|
42
|
-
This license shall be governed by and construed in accordance with the laws
|
|
43
|
-
of the jurisdiction in which FormKit Inc. is incorporated, without regard to
|
|
44
|
-
its conflict of law provisions.
|
|
45
|
-
|
|
46
|
-
FormKit Inc.
|
|
47
|
-
https://formkit.com
|
|
48
|
-
enterprise@formkit.com
|