// src/types/tools.ts import type { StructuredToolInterface } from '@langchain/core/tools'; import type { RunnableToolLike } from '@langchain/core/runnables'; import type { ToolCall } from '@langchain/core/messages/tool'; import type { MessageContentComplex } from '@langchain/core/messages'; import type { ToolErrorData } from './stream'; import { EnvVar, ExecutionContext } from '@/common'; /** Replacement type for `import type { ToolCall } from '@langchain/core/messages/tool'` in order to have stringified args typed */ export type CustomToolCall = { name: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any args: string | Record; id?: string; type?: 'tool_call'; output?: string; }; export type GenericTool = (StructuredToolInterface | RunnableToolLike) & { mcp?: boolean; }; export type ToolMap = Map; export type ToolRefs = { tools: GenericTool[]; toolMap?: ToolMap; }; export type ToolRefGenerator = (tool_calls: ToolCall[]) => ToolRefs; export type ToolNodeOptions = { name?: string; tags?: string[]; handleToolErrors?: boolean; loadRuntimeTools?: ToolRefGenerator; toolCallStepIds?: Map; errorHandler?: ( data: ToolErrorData, metadata?: Record ) => Promise; /** Tool registry for lazy computation of programmatic tools and tool search */ toolRegistry?: LCToolRegistry; /** Reference to Graph's sessions map for automatic session injection */ sessions?: ToolSessionMap; /** When true, dispatches ON_TOOL_EXECUTE events instead of invoking tools directly */ eventDrivenMode?: boolean; /** Tool definitions for event-driven mode (used for context, not invocation) */ toolDefinitions?: Map; /** Agent ID for event-driven mode (used to identify which agent's context to use) */ agentId?: string; /** Tool names that must be executed directly (via runTool) even in event-driven mode (e.g., graph-managed handoff tools) */ directToolNames?: Set; /** * StreamingToolCallBuffer for recovering truncated tool call arguments. * When parsePartialJson loses content due to max_tokens truncation, * the ToolNode can fall back to extracting raw field values from the buffer. */ streamingToolCallBuffer?: import('@/tools/StreamingToolCallBuffer').StreamingToolCallBuffer; /** * HITL tool approval configuration. * When provided, tools matching the policy will call interrupt() before execution. */ toolApprovalConfig?: ToolApprovalConfig; /** * Optional hook registry threaded down from Run. When set, ToolNode * fires PreToolUse / PostToolUse / PostToolUseFailure / PermissionDenied * lifecycle events through executeHooks during event-driven dispatch. */ hookRegistry?: import('@/hooks').HookRegistry; /** * Maximum characters for a single tool result (head+tail truncation). * When omitted, derived from the agent's `maxContextTokens` (30% * heuristic), capped at HARD_MAX_TOOL_RESULT_CHARS. Upstream-aligned — * Ranger's truncation today is driven by other knobs, but accepting * this option keeps the test surface portable. */ maxToolResultChars?: number; /** * Run-scoped tool output reference configuration (upstream PR #114). * When `enabled` is true, ToolNode registers successful outputs and * substitutes `{{toolturn}}` placeholders found in string args. * Ignored when `toolOutputRegistry` is also provided (the host-supplied * registry wins). */ toolOutputReferences?: ToolOutputReferencesConfig; /** * Pre-constructed registry instance shared across ToolNodes for the run. * Graphs pass the same registry to every ToolNode they compile so * cross-agent `{{toolturn}}` substitutions resolve. Takes * precedence over `toolOutputReferences` when both are set. */ toolOutputRegistry?: import('@/tools/toolOutputReferences').ToolOutputReferenceRegistry; }; export type ToolNodeConstructorParams = ToolRefs & ToolNodeOptions; export type ToolEndEvent = { /** The Step Id of the Tool Call */ id: string; /** The Completed Tool Call */ tool_call: ToolCall; /** The content index of the tool call */ index: number; type?: 'tool_call'; }; export type CodeEnvFile = { id: string; name: string; session_id: string; }; export type CodeExecutionToolParams = | undefined | { session_id?: string; user_id?: string; apiKey?: string; files?: CodeEnvFile[]; [EnvVar.CODE_API_KEY]?: string; }; /** * Bash execution tool params. Same shape as CodeExecutionToolParams — * BashExecutor reuses the /exec endpoint with `lang: 'bash'`. */ export type BashExecutionToolParams = CodeExecutionToolParams; /** * Read-file tool params. Resolution of the `file_path` to actual bytes is * delegated to a host-supplied resolver in `RunConfig` (skill files, code * exec session files, etc.). */ export type ReadFileToolParams = | undefined | { apiKey?: string; [EnvVar.CODE_API_KEY]?: string; }; export type FileRef = { id: string; name: string; path?: string; /** Session ID this file belongs to (for multi-session file tracking) */ session_id?: string; }; export type FileRefs = FileRef[]; export type ExecuteResult = { session_id: string; stdout: string; stderr: string; files?: FileRefs; }; /** JSON Schema type definition for tool parameters */ export type JsonSchemaType = { type: | 'string' | 'number' | 'integer' | 'float' | 'boolean' | 'array' | 'object'; enum?: string[]; items?: JsonSchemaType; properties?: Record; required?: string[]; description?: string; additionalProperties?: boolean | JsonSchemaType; }; /** * Specifies which contexts can invoke a tool (inspired by Anthropic's allowed_callers) * - 'direct': Only callable directly by the LLM (default if omitted) * - 'code_execution': Only callable from within programmatic code execution */ export type AllowedCaller = 'direct' | 'code_execution'; /** Tool definition with optional deferred loading and caller restrictions */ export type LCTool = { name: string; description?: string; parameters?: JsonSchemaType; /** When true, tool is not loaded into context initially (for tool search) */ defer_loading?: boolean; /** * Which contexts can invoke this tool. * Default: ['direct'] (only callable directly by LLM) * Options: 'direct', 'code_execution' */ allowed_callers?: AllowedCaller[]; /** Response format for the tool output */ responseFormat?: 'content' | 'content_and_artifact'; /** Server name for MCP tools */ serverName?: string; /** Tool type classification */ toolType?: 'builtin' | 'mcp' | 'action'; }; /** Single tool call within a batch request for event-driven execution */ export type ToolCallRequest = { /** Tool call ID from the LLM */ id: string; /** Tool name */ name: string; /** Tool arguments */ args: Record; /** Step ID for tracking */ stepId?: string; /** Usage turn count for this tool */ turn?: number; /** Code execution session context for session continuity in event-driven mode */ codeSessionContext?: { session_id: string; files?: CodeEnvFile[]; }; }; /** Batch request containing ALL tool calls for a graph step */ export type ToolExecuteBatchRequest = { /** All tool calls from the AIMessage */ toolCalls: ToolCallRequest[]; /** User ID for context */ userId?: string; /** Agent ID for context */ agentId?: string; /** Runtime configurable from RunnableConfig (includes user, userMCPAuthMap, etc.) */ configurable?: Record; /** Runtime metadata from RunnableConfig (includes thread_id, run_id, provider, etc.) */ metadata?: Record; /** Promise resolver - handler calls this with ALL results */ resolve: (results: ToolExecuteResult[]) => void; /** Promise rejector - handler calls this on fatal error */ reject: (error: Error) => void; }; /** * Generic message-injection envelope used by SkillTool, hooks, and any other * tool execution handler that needs to push extra context into graph state * after the ToolMessage. The host's message formatter is responsible for * placement and any per-provider merging rules. */ export type InjectedMessage = { /** 'user' for skill body injection, 'system' for context hints. * Both are converted to HumanMessage at runtime; the original role * is preserved in additional_kwargs.role. */ role: 'user' | 'system'; /** Message content: string for simple text, array for complex multi-part content */ content: string | MessageContentComplex[]; /** When true, the message is framework-internal: not shown in UI, not counted as a user turn */ isMeta?: boolean; /** Origin tag for downstream consumers (UI, pruner, compaction) */ source?: 'skill' | 'hook' | 'system'; /** Only set when source is 'skill', for compaction preservation */ skillName?: string; }; /** Result for a single tool call in event-driven execution */ export type ToolExecuteResult = { /** Matches ToolCallRequest.id */ toolCallId: string; /** Tool output content */ content: string | unknown[]; /** Optional artifact (for content_and_artifact format) */ artifact?: unknown; /** Execution status */ status: 'success' | 'error'; /** Error message if status is 'error' */ errorMessage?: string; /** * Messages to inject into graph state after the ToolMessage for this call. * Placed after tool results to respect provider message ordering * (tool_call -> tool_result adjacency). The host's message formatter may * merge injected user messages with the preceding tool_result turn. * Generic mechanism: any tool execution handler can use this. */ injectedMessages?: InjectedMessage[]; }; /** Map of tool names to tool definitions */ export type LCToolRegistry = Map; /** * Configuration for the tool output reference feature (upstream PR #114). * * When `enabled: true`, ToolNode stores each successful tool output under * a stable key `toolturn` and the LLM can pipe a previous output * into a subsequent tool's args via the literal placeholder * `{{toolturn}}`. Substitution happens immediately before tool * invocation; the LLM-visible ToolMessage.content is annotated with a * `_ref` JSON field or a `[ref: toolturn]` prefix so the model * knows which key the output is stored under. * * Disabled by default. Hosts that want to opt in pass * `toolOutputReferences: { enabled: true }` on RunConfig and (optionally) * append the bash tool's tool-output-references guide to the bash tool * description so the LLM learns the substitution syntax. */ export type ToolOutputReferencesConfig = { /** Enable the registry and placeholder substitution. Defaults to `false`. */ enabled?: boolean; /** Maximum characters stored (and substituted) per registered output. */ maxOutputSize?: number; /** Hard cap on total characters retained across all registered outputs for the run. */ maxTotalSize?: number; }; export type ProgrammaticCache = { toolMap: ToolMap; toolDefs: LCTool[] }; // ============================================================================ // Human-in-the-Loop (HITL) Tool Approval Types // ============================================================================ /** * Execution context determines whether HITL approval is required. * - 'interactive': User is present in the chat, approval prompts are shown. * - 'scheduled': Automated/scheduled execution, approval is auto-granted. * * @see ExecutionContext enum in common/index.ts → tools/approval/constants.ts * Note: ExecutionContext is exported from common/index.ts, not re-exported here to avoid TS2308. */ /** * Policy for when a tool requires human approval. * - 'always': Always require approval (default for interactive context). * - 'never': Never require approval (used for safe read-only tools). * - A function: Custom logic based on tool name and args. * Receives the tool name as first arg so the host can classify tools dynamically * (e.g., only require approval for tools whose name includes "send", "create", "delete"). */ export type ToolApprovalRule = | 'always' | 'never' | ((toolName: string, args: Record) => boolean); /** * Per-tool approval overrides. Keys are tool names (or glob patterns). * When a tool is not listed, it falls back to the default policy. */ export type ToolApprovalOverrides = Record; /** * Configuration for the HITL approval system. * Passed via CompileOptions or graph config metadata. */ export type ToolApprovalConfig = { /** * Default policy for all tools when no override matches. * @default 'always' for interactive, 'never' for scheduled */ defaultPolicy?: ToolApprovalRule; /** * Per-tool overrides. Keys are tool names. * Example: { 'search': 'never', 'outlook_operations': 'always' } */ overrides?: ToolApprovalOverrides; /** * Execution context. When 'scheduled', all approvals are auto-granted. * When 'handoff', approvals are auto-granted for autonomous multi-agent pipelines. * @default 'interactive' */ executionContext?: `${ExecutionContext}`; /** * Per-agent execution context overrides. Keys are agent IDs. * Used to give handoff/delegate agents a different execution context than the * primary agent. For example, the primary agent stays 'interactive' (HITL enabled) * while all handoff agents are set to 'handoff' (HITL bypassed). * * When a ToolNode's agentId is found in this map, the override takes precedence * over the top-level `executionContext`. * * @example * { * executionContext: 'interactive', // primary agent: HITL on * agentExecutionContextOverrides: { * 'research-agent': 'handoff', // handoff agent: HITL off * 'data-agent': 'handoff', // handoff agent: HITL off * }, * } */ agentExecutionContextOverrides?: Record; }; /** * The interrupt payload sent to the host when a tool requires approval. * This is the value passed to LangGraph's interrupt() function. */ export type ToolApprovalRequest = { /** Discriminator for the interrupt type */ type: 'tool_approval_required'; /** Tool call ID from the LLM */ toolCallId: string; /** Tool name */ toolName: string; /** Tool arguments the LLM wants to execute */ toolArgs: Record; /** Agent ID that requested the tool call */ agentId?: string; /** Human-readable description of what the tool will do */ description?: string; }; /** * The response from the host after human review. * This is the resume value passed via Command({ resume: ... }). */ export type ToolApprovalResponse = { /** Whether the tool call was approved */ approved: boolean; /** Optional feedback from the human (e.g., reason for denial) */ feedback?: string; /** Optional modified args (if the human edited the tool call) */ modifiedArgs?: Record; }; /** * Notification payload dispatched via ON_TOOL_APPROVAL_REQUIRED. * Data-only — no resolve/reject callbacks. The approval mechanism is handled * by LangGraph's native interrupt()/Command({ resume }) pattern. * * The host persists this data (e.g., in MongoDB) and sends UI events. * The actual approval response comes back via Command({ resume: ToolApprovalResponse }). */ export type ToolApprovalNotification = ToolApprovalRequest; /** Search mode: code_interpreter uses external sandbox, local uses safe substring matching */ export type ToolSearchMode = 'code_interpreter' | 'local'; /** Format for MCP tool names in search results */ export type McpNameFormat = 'full' | 'base'; /** Parameters for creating a Tool Search tool */ export type ToolSearchParams = { apiKey?: string; toolRegistry?: LCToolRegistry; onlyDeferred?: boolean; baseUrl?: string; /** Search mode: 'code_interpreter' (default) uses sandbox for regex, 'local' uses safe substring matching */ mode?: ToolSearchMode; /** Filter tools to only those from specific MCP server(s). Can be a single name or array of names. */ mcpServer?: string | string[]; /** Format for MCP tool names: 'full' (tool_mcp_server) or 'base' (tool only). Default: 'full' */ mcpNameFormat?: McpNameFormat; [key: string]: unknown; }; /** Simplified tool metadata for search purposes */ export type ToolMetadata = { name: string; description: string; parameters?: JsonSchemaType; }; /** Individual search result for a matching tool */ export type ToolSearchResult = { tool_name: string; match_score: number; matched_field: string; snippet: string; }; /** Response from the tool search operation */ export type ToolSearchResponse = { tool_references: ToolSearchResult[]; total_tools_searched: number; pattern_used: string; }; /** Artifact returned alongside the formatted search results */ export type ToolSearchArtifact = { tool_references: ToolSearchResult[]; metadata: { total_searched: number; pattern: string; error?: string; }; }; // ============================================================================ // Programmatic Tool Calling Types // ============================================================================ /** * Tool call requested by the Code API during programmatic execution */ export type PTCToolCall = { /** Unique ID like "call_001" */ id: string; /** Tool name */ name: string; /** Input parameters */ // eslint-disable-next-line @typescript-eslint/no-explicit-any input: Record; }; /** * Tool result sent back to the Code API */ export type PTCToolResult = { /** Matches PTCToolCall.id */ call_id: string; /** Tool execution result (any JSON-serializable value) */ // eslint-disable-next-line @typescript-eslint/no-explicit-any result: any; /** Whether tool execution failed */ is_error: boolean; /** Error details if is_error=true */ error_message?: string; }; /** * Response from the Code API for programmatic execution */ export type ProgrammaticExecutionResponse = { status: 'tool_call_required' | 'completed' | 'error' | unknown; session_id?: string; /** Present when status='tool_call_required' */ continuation_token?: string; tool_calls?: PTCToolCall[]; /** Present when status='completed' */ stdout?: string; stderr?: string; files?: FileRefs; /** Present when status='error' */ error?: string; }; /** * Artifact returned by the PTC tool */ export type ProgrammaticExecutionArtifact = { session_id?: string; files?: FileRefs; }; /** * Initialization parameters for the PTC tool */ export type ProgrammaticToolCallingParams = { /** Code API key (or use CODE_API_KEY env var) */ apiKey?: string; /** Code API base URL (or use CODE_BASEURL env var) */ baseUrl?: string; /** Safety limit for round-trips (default: 20) */ maxRoundTrips?: number; /** HTTP proxy URL */ proxy?: string; /** Enable debug logging (or set PTC_DEBUG=true env var) */ debug?: boolean; /** Environment variable key for API key */ [key: string]: unknown; }; /** * Initialization parameters for the BashPTC tool. Same shape as * {@link ProgrammaticToolCallingParams}; the bash variant just runs * generated code through `lang: 'bash'` instead of `lang: 'python'`. */ export type BashProgrammaticToolCallingParams = ProgrammaticToolCallingParams; // ============================================================================ // Tool Session Context Types // ============================================================================ /** * Tracks code execution session state for automatic file persistence. * Stored in Graph.sessions and injected into subsequent tool invocations. */ export type CodeSessionContext = { /** Session ID from the code execution environment */ session_id: string; /** Files generated in this session (for context/tracking) */ files?: FileRefs; /** Timestamp of last update */ lastUpdated: number; }; /** * Artifact structure returned by code execution tools (CodeExecutor, PTC). * Used to extract session context after tool completion. */ export type CodeExecutionArtifact = { session_id?: string; files?: FileRefs; }; /** * Generic session context union type for different tool types. * Extend this as new tool session types are added. */ export type ToolSessionContext = CodeSessionContext; /** * Map of tool names to their session contexts. * Keys are tool constants (e.g., Constants.EXECUTE_CODE, Constants.PROGRAMMATIC_TOOL_CALLING). */ export type ToolSessionMap = Map;