/** * Hooks System - Lifecycle hooks for agent customization * * Hooks provide extension points for customizing agent behavior without * subclassing. They enable middleware-like patterns for: * - Logging and tracing * - Input/output transformation * - Custom validation * - Metrics collection * - Integration with external systems */ import type { Message } from '../providers/types.js'; import type { ToolDefinition, ToolExecutionResult } from '../tools/types.js'; /** * Context available in all hook calls */ export interface HookContext { /** * Current session ID */ sessionId: string; /** * Current iteration number (1-indexed) */ iteration: number; /** * Abort signal for cancellation */ signal?: AbortSignal; /** * Custom metadata that can be passed between hooks */ metadata: Record; } /** * Context for iteration hooks */ export interface IterationHookContext extends HookContext { /** * Maximum iterations allowed */ maxIterations: number; /** * Current message history */ messages: Message[]; } /** * Hook called before each iteration starts. * * Can be used for: * - Logging iteration boundaries * - Custom iteration budget tracking * - Early termination checks * * @returns void, or { skip: true } to skip this iteration */ export type BeforeIterationHook = (context: IterationHookContext) => undefined | Promise | { skip: true; } | Promise<{ skip: true; }>; /** * Hook called after each iteration completes. * * Can be used for: * - Logging iteration results * - Metrics collection * - State snapshots */ export type AfterIterationHook = (context: IterationHookContext & { /** * Tool calls made in this iteration */ toolCalls: Array<{ name: string; input: Record; result: ToolExecutionResult; }>; /** * Whether LLM returned text without tool calls */ completedWithText: boolean; }) => undefined | Promise; /** * Context for LLM hooks */ export interface LLMHookContext extends HookContext { /** * Messages to be sent to LLM */ messages: Message[]; /** * Tool definitions available to LLM */ tools: ToolDefinition[]; /** * Current system prompt (fully assembled, including anchors). * Hooks can read this and return a modified version in BeforeLLMHookResult. */ systemPrompt: string; } /** * Result from before:llm hook that modifies messages or tools */ export interface BeforeLLMHookResult { /** * Modified messages (optional, original used if not provided) */ messages?: Message[]; /** * Modified tools (optional, original used if not provided) */ tools?: ToolDefinition[]; /** * Modified system prompt (optional, original used if not provided). * When returned, messages[0] (system message) is updated automatically. */ systemPrompt?: string; } /** * Hook called before LLM call. * * Can be used for: * - Prompt transformation/injection * - Tool filtering * - Request logging * - Caching checks * * @returns void to proceed unchanged, or modified messages/tools */ export type BeforeLLMHook = (context: LLMHookContext) => undefined | Promise | BeforeLLMHookResult | Promise; /** * Context for after:llm hook */ export interface AfterLLMHookContext extends LLMHookContext { /** * Text response from LLM */ text: string; /** * Tool uses requested by LLM */ toolUses: Array<{ id: string; name: string; input: Record; }>; /** * Token usage from the call */ usage?: { inputTokens: number; outputTokens: number; }; /** * Model that was used */ model?: string; /** * Duration of the LLM call in milliseconds */ durationMs: number; } /** * Hook called after LLM response is received. * * Can be used for: * - Response logging * - Token tracking * - Response validation * - Metrics collection */ export type AfterLLMHook = (context: AfterLLMHookContext) => undefined | Promise; /** * Context for tool hooks */ export interface ToolHookContext extends HookContext { /** * Name of the tool being executed */ toolName: string; /** * Input arguments for the tool */ input: Record; } /** * Result from before:tool hook that can skip or modify execution */ export interface BeforeToolHookResult { /** * Skip tool execution and use this result instead */ skip?: boolean; /** * Custom result to return (when skip is true) */ result?: ToolExecutionResult; /** * Modified input (when not skipping) */ input?: Record; } /** * Hook called before tool execution (after permissions and guardrails). * * Can be used for: * - Custom validation * - Input transformation * - Execution mocking for tests * - Rate limiting * * @returns void to proceed, or skip/modify options */ export type BeforeToolHook = (context: ToolHookContext) => undefined | Promise | BeforeToolHookResult | Promise; /** * Context for after:tool hook */ export interface AfterToolHookContext extends ToolHookContext { /** * Result from tool execution */ result: ToolExecutionResult; /** * Duration of tool execution in milliseconds */ durationMs: number; } /** * Result from after:tool hook that can modify the result */ export interface AfterToolHookResult { /** * Modified result (optional, original used if not provided) */ result?: ToolExecutionResult; } /** * Hook called after tool execution. * * Can be used for: * - Result transformation * - Logging and metrics * - Result validation * - Error enrichment */ export type AfterToolHook = (context: AfterToolHookContext) => undefined | Promise | AfterToolHookResult | Promise; /** * Context for error hooks */ export interface ErrorHookContext extends HookContext { /** * The error that occurred */ error: Error; /** * Phase where the error occurred */ phase: 'llm' | 'tool' | 'iteration'; /** * Tool name (if error occurred during tool execution) */ toolName?: string; } /** * Result from error hook that can recover or transform the error */ export interface ErrorHookResult { /** * Whether to suppress the error and continue */ handled?: boolean; /** * Transformed error to throw instead */ error?: Error; /** * Recovery result (for tool errors, replaces failed result) */ recovery?: ToolExecutionResult; } /** * Hook called when an error occurs. * * Can be used for: * - Error logging * - Error transformation * - Recovery strategies * - Alerting */ export type OnErrorHook = (context: ErrorHookContext) => undefined | Promise | ErrorHookResult | Promise; /** * Configuration for the hooks system */ export interface HooksConfig { /** * Hooks called before each iteration */ beforeIteration?: BeforeIterationHook[]; /** * Hooks called after each iteration */ afterIteration?: AfterIterationHook[]; /** * Hooks called before LLM calls */ beforeLLM?: BeforeLLMHook[]; /** * Hooks called after LLM responses */ afterLLM?: AfterLLMHook[]; /** * Hooks called before tool execution */ beforeTool?: BeforeToolHook[]; /** * Hooks called after tool execution */ afterTool?: AfterToolHook[]; /** * Hooks called when errors occur */ onError?: OnErrorHook[]; } /** * Internal representation of a registered hook */ export interface RegisteredHook { /** * Unique ID for this hook registration */ id: string; /** * The hook function */ hook: T; /** * Optional name for debugging */ name?: string; /** * Priority (lower = runs first, default: 0) */ priority?: number; } /** * Options for hook registration */ export interface HookRegistrationOptions { /** * Name for debugging/logging */ name?: string; /** * Priority (lower = runs first, default: 0) */ priority?: number; } /** * Event types for hook operations */ export type HookEventType = 'hook:registered' | 'hook:unregistered' | 'hook:started' | 'hook:completed' | 'hook:error'; /** * Hook execution event payload */ export interface HookEvent { type: HookEventType; hookType: keyof HooksConfig; hookId?: string; hookName?: string; durationMs?: number; error?: Error; } /** * Event handler for hook events */ export type HookEventHandler = (event: HookEvent) => void;