@librechat/agents 3.1.71-dev.1 → 3.1.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +3 -1
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/toolOutputReferences.cjs +22 -9
- package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +3 -1
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/toolOutputReferences.mjs +22 -9
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/types/llm/invoke.d.ts +25 -8
- package/package.json +1 -1
- package/src/llm/invoke.test.ts +10 -6
- package/src/llm/invoke.ts +27 -8
- package/src/tools/BashExecutor.ts +3 -1
- package/src/tools/__tests__/BashExecutor.test.ts +13 -0
- package/src/tools/__tests__/annotateMessagesForLLM.test.ts +60 -0
- package/src/tools/toolOutputReferences.ts +21 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke.cjs","sources":["../../../src/llm/invoke.ts"],"sourcesContent":["import { concat } from '@langchain/core/utils/stream';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type * as t from '@/types';\nimport { manualToolStreamProviders } from '@/llm/providers';\nimport { annotateMessagesForLLM } from '@/tools/toolOutputReferences';\nimport { modifyDeltaProperties } from '@/messages';\nimport { ChatModelStreamHandler } from '@/stream';\nimport { GraphEvents, Providers } from '@/common';\nimport { initializeModel } from '@/llm/init';\n\n/**\n * Context passed to `attemptInvoke
|
|
1
|
+
{"version":3,"file":"invoke.cjs","sources":["../../../src/llm/invoke.ts"],"sourcesContent":["import { concat } from '@langchain/core/utils/stream';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';\nimport type * as t from '@/types';\nimport { manualToolStreamProviders } from '@/llm/providers';\nimport { annotateMessagesForLLM } from '@/tools/toolOutputReferences';\nimport { modifyDeltaProperties } from '@/messages';\nimport { ChatModelStreamHandler } from '@/stream';\nimport { GraphEvents, Providers } from '@/common';\nimport { initializeModel } from '@/llm/init';\n\n/**\n * Context passed to `attemptInvoke`. Matches the subset of Graph that\n * `ChatModelStreamHandler.handle` needs *plus* the explicit\n * `getOrCreateToolOutputRegistry()` accessor that `attemptInvoke`\n * itself calls to pull the run-scoped tool-output registry off the\n * graph and project each relevant ToolMessage into a transient\n * annotated copy before the provider call.\n *\n * The intersection is intentional: `Parameters<...>[3]` resolves\n * indirectly through the stream handler's signature (which returns\n * `StandardGraph` and already exposes the accessor since #117), but\n * stating it explicitly here surfaces the contract at the call site —\n * a developer reading `attemptInvoke` doesn't have to chase the\n * upstream handler's parameter list to discover that\n * `context?.getOrCreateToolOutputRegistry()` is a real thing. Single\n * optional chain only — the method itself is required on the\n * `StandardGraph` branch of the intersection, so the second `?.` is\n * unnecessary at the call site.\n *\n * `NonNullable<...>` strips `undefined` from the upstream parameter\n * type so the intersection doesn't collapse to `never` on the\n * undefined branch; callers express optionality via `context?:\n * InvokeContext` on the function signature instead.\n *\n * Callers without a registry (e.g. summarization) simply pass no\n * `context` and the transform safely no-ops.\n */\nexport type InvokeContext = NonNullable<\n Parameters<ChatModelStreamHandler['handle']>[3]\n> & {\n getOrCreateToolOutputRegistry?(): ToolOutputReferenceRegistry | undefined;\n};\n\n/**\n * Per-chunk callback for custom stream processing.\n * When provided, replaces the default `ChatModelStreamHandler`.\n */\nexport type OnChunk = (chunk: AIMessageChunk) => void | Promise<void>;\n\n/**\n * Invokes a chat model with the given messages, handling both streaming and\n * non-streaming paths.\n *\n * By default, stream chunks are processed through a `ChatModelStreamHandler`\n * that dispatches run steps (MESSAGE_CREATION, TOOL_CALLS) for the graph.\n * Pass an `onChunk` callback to override this with custom chunk processing\n * (e.g. summarization delta events).\n */\nexport async function attemptInvoke(\n {\n model,\n messages,\n provider,\n context,\n onChunk,\n }: {\n model: t.ChatModel;\n messages: BaseMessage[];\n provider: Providers;\n context?: InvokeContext;\n onChunk?: OnChunk;\n },\n config?: RunnableConfig\n): Promise<Partial<t.BaseGraphState>> {\n /**\n * Pull the run-scoped tool output registry off the graph (when one\n * exists) and project ToolMessages carrying ref metadata into a\n * transient annotated copy. The original `messages` array stays\n * untouched so the graph state never sees `[ref: …]` / `_ref`\n * payload.\n */\n const registry = context?.getOrCreateToolOutputRegistry();\n const runId = config?.configurable?.run_id as string | undefined;\n const messagesForProvider = annotateMessagesForLLM(messages, registry, runId);\n\n if (model.stream) {\n const stream = await model.stream(messagesForProvider, config);\n let finalChunk: AIMessageChunk | undefined;\n\n if (onChunk) {\n for await (const chunk of stream) {\n await onChunk(chunk);\n finalChunk = finalChunk ? concat(finalChunk, chunk) : chunk;\n }\n } else {\n const metadata = config?.metadata as Record<string, unknown> | undefined;\n const streamHandler = new ChatModelStreamHandler();\n for await (const chunk of stream) {\n await streamHandler.handle(\n GraphEvents.CHAT_MODEL_STREAM,\n { chunk },\n metadata,\n context\n );\n finalChunk = finalChunk ? concat(finalChunk, chunk) : chunk;\n }\n }\n\n if (manualToolStreamProviders.has(provider)) {\n finalChunk = modifyDeltaProperties(provider, finalChunk);\n }\n\n if ((finalChunk?.tool_calls?.length ?? 0) > 0) {\n finalChunk!.tool_calls = finalChunk!.tool_calls?.filter(\n (tool_call: ToolCall) => !!tool_call.name\n );\n }\n\n return { messages: [finalChunk as AIMessageChunk] };\n }\n\n const finalMessage = await model.invoke(messagesForProvider, config);\n if ((finalMessage.tool_calls?.length ?? 0) > 0) {\n finalMessage.tool_calls = finalMessage.tool_calls?.filter(\n (tool_call: ToolCall) => !!tool_call.name\n );\n }\n return { messages: [finalMessage] };\n}\n\n/**\n * Attempts each fallback provider in order until one succeeds.\n * Throws the last error if all fallbacks fail.\n */\nexport async function tryFallbackProviders({\n fallbacks,\n tools,\n messages,\n config,\n primaryError,\n context,\n onChunk,\n}: {\n fallbacks: Array<{ provider: Providers; clientOptions?: t.ClientOptions }>;\n tools?: t.GraphTools;\n messages: BaseMessage[];\n config?: RunnableConfig;\n primaryError: unknown;\n context?: InvokeContext;\n onChunk?: OnChunk;\n}): Promise<Partial<t.BaseGraphState> | undefined> {\n let lastError: unknown = primaryError;\n for (const fb of fallbacks) {\n try {\n const fbModel = initializeModel({\n provider: fb.provider,\n clientOptions: fb.clientOptions,\n tools,\n });\n const result = await attemptInvoke(\n {\n model: fbModel as t.ChatModel,\n messages,\n provider: fb.provider,\n context,\n onChunk,\n },\n config\n );\n return result;\n } catch (e) {\n lastError = e;\n continue;\n }\n }\n if (lastError !== undefined) {\n throw lastError;\n }\n return undefined;\n}\n"],"names":["annotateMessagesForLLM","stream","concat","ChatModelStreamHandler","GraphEvents","manualToolStreamProviders","modifyDeltaProperties","initializeModel"],"mappings":"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"}
|
|
@@ -59,7 +59,9 @@ Referencing previous tool outputs:
|
|
|
59
59
|
- Every successful tool result is tagged with a reference key of the form \`tool<idx>turn<turn>\` (e.g., \`tool0turn0\`). The key appears either as a \`[ref: tool0turn0]\` prefix line or, when the output is a JSON object, as a \`_ref\` field on the object.
|
|
60
60
|
- To pipe a previous tool output into this tool, embed the placeholder \`{{tool<idx>turn<turn>}}\` literally anywhere in the \`command\` string (or any string arg). It will be substituted with the stored output verbatim before the command runs.
|
|
61
61
|
- The substituted value is the original output string (no \`[ref: …]\` prefix, no \`_ref\` key), so it is safe to pipe directly into \`jq\`, \`grep\`, \`awk\`, etc.
|
|
62
|
-
- Example: \`echo '{{tool0turn0}}' | jq '.foo'\` takes the full output of the first tool from the first turn and pipes it into jq.
|
|
62
|
+
- Example (simple ASCII output): \`echo '{{tool0turn0}}' | jq '.foo'\` takes the full output of the first tool from the first turn and pipes it into jq.
|
|
63
|
+
- For payloads that may contain quotes, parentheses, backticks, or arbitrary bytes (random/binary data, JSON with embedded quotes, multi-line strings), prefer a quoted-delimiter heredoc over \`echo '…'\`. The heredoc body is not interpreted by the shell, so substituted payloads pass through unchanged.
|
|
64
|
+
- Heredoc example: \`wc -c << 'EOF'\\n{{tool0turn0}}\\nEOF\` (the quotes around \`'EOF'\` disable interpolation inside the body).
|
|
63
65
|
- Unknown reference keys are left in place and surfaced as \`[unresolved refs: …]\` after the output.
|
|
64
66
|
`.trim();
|
|
65
67
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BashExecutor.cjs","sources":["../../../src/tools/BashExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { Constants } from '@/common';\n\nconfig();\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nconst baseEndpoint = getCodeBaseURL();\nconst EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\nexport const BashExecutionToolSchema = {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: `The bash command or script to execute.\n- The environment is stateless; variables and state don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- Use \\`echo\\`, \\`printf\\`, or \\`cat\\` for all outputs.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the command with. This should only be used if the input command requires additional arguments to run.',\n },\n },\n required: ['command'],\n} as const;\n\nexport const BashExecutionToolDescription = `\nRuns bash commands and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious commands.\n`.trim();\n\n/**\n * Supplemental prompt documenting the tool-output reference feature.\n *\n * Hosts should append this (separated by a blank line) to the base\n * {@link BashExecutionToolDescription} only when\n * `RunConfig.toolOutputReferences.enabled` is `true`. When the feature\n * is disabled, including this text would tell the LLM to emit\n * `{{tool0turn0}}` placeholders that pass through unsubstituted and\n * leak into the shell.\n */\nexport const BashToolOutputReferencesGuide = `\nReferencing previous tool outputs:\n- Every successful tool result is tagged with a reference key of the form \\`tool<idx>turn<turn>\\` (e.g., \\`tool0turn0\\`). The key appears either as a \\`[ref: tool0turn0]\\` prefix line or, when the output is a JSON object, as a \\`_ref\\` field on the object.\n- To pipe a previous tool output into this tool, embed the placeholder \\`{{tool<idx>turn<turn>}}\\` literally anywhere in the \\`command\\` string (or any string arg). It will be substituted with the stored output verbatim before the command runs.\n- The substituted value is the original output string (no \\`[ref: …]\\` prefix, no \\`_ref\\` key), so it is safe to pipe directly into \\`jq\\`, \\`grep\\`, \\`awk\\`, etc.\n- Example: \\`echo '{{tool0turn0}}' | jq '.foo'\\` takes the full output of the first tool from the first turn and pipes it into jq.\n- Unknown reference keys are left in place and surfaced as \\`[unresolved refs: …]\\` after the output.\n`.trim();\n\n/**\n * Composes the bash tool description, optionally appending the\n * tool-output references guide. Hosts that enable\n * `RunConfig.toolOutputReferences` should pass `enableToolOutputReferences: true`\n * when registering the tool so the LLM learns the `{{…}}` syntax it\n * will actually be able to use.\n */\nexport function buildBashExecutionToolDescription(options?: {\n enableToolOutputReferences?: boolean;\n}): string {\n if (options?.enableToolOutputReferences === true) {\n return `${BashExecutionToolDescription}\\n\\n${BashToolOutputReferencesGuide}`;\n }\n return BashExecutionToolDescription;\n}\n\nexport const BashExecutionToolName = Constants.BASH_TOOL;\n\n/**\n * Default bash tool definition using the base description.\n *\n * When `RunConfig.toolOutputReferences.enabled` is `true`, build a\n * reference-aware description with\n * {@link buildBashExecutionToolDescription}\n * (`{ enableToolOutputReferences: true }`) and construct a custom\n * definition using it — using this constant as-is leaves the LLM\n * unaware of the `{{tool<i>turn<n>}}` syntax.\n */\nexport const BashExecutionToolDefinition = {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n} as const;\n\nfunction createBashExecutionTool(\n params: t.BashExecutionToolParams = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n const { command, ...rest } = rawInput as {\n command: string;\n args?: string[];\n };\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang: 'bash',\n code: command,\n ...rest,\n ...params,\n };\n\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'LibreChat/1.0',\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error:\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createBashExecutionTool };\n"],"names":["config","getCodeBaseURL","Constants","tool","HttpsProxyAgent","imageExtRegex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BashExecutor.cjs","sources":["../../../src/tools/BashExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { Constants } from '@/common';\n\nconfig();\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nconst baseEndpoint = getCodeBaseURL();\nconst EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\nexport const BashExecutionToolSchema = {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: `The bash command or script to execute.\n- The environment is stateless; variables and state don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- Use \\`echo\\`, \\`printf\\`, or \\`cat\\` for all outputs.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the command with. This should only be used if the input command requires additional arguments to run.',\n },\n },\n required: ['command'],\n} as const;\n\nexport const BashExecutionToolDescription = `\nRuns bash commands and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious commands.\n`.trim();\n\n/**\n * Supplemental prompt documenting the tool-output reference feature.\n *\n * Hosts should append this (separated by a blank line) to the base\n * {@link BashExecutionToolDescription} only when\n * `RunConfig.toolOutputReferences.enabled` is `true`. When the feature\n * is disabled, including this text would tell the LLM to emit\n * `{{tool0turn0}}` placeholders that pass through unsubstituted and\n * leak into the shell.\n */\nexport const BashToolOutputReferencesGuide = `\nReferencing previous tool outputs:\n- Every successful tool result is tagged with a reference key of the form \\`tool<idx>turn<turn>\\` (e.g., \\`tool0turn0\\`). The key appears either as a \\`[ref: tool0turn0]\\` prefix line or, when the output is a JSON object, as a \\`_ref\\` field on the object.\n- To pipe a previous tool output into this tool, embed the placeholder \\`{{tool<idx>turn<turn>}}\\` literally anywhere in the \\`command\\` string (or any string arg). It will be substituted with the stored output verbatim before the command runs.\n- The substituted value is the original output string (no \\`[ref: …]\\` prefix, no \\`_ref\\` key), so it is safe to pipe directly into \\`jq\\`, \\`grep\\`, \\`awk\\`, etc.\n- Example (simple ASCII output): \\`echo '{{tool0turn0}}' | jq '.foo'\\` takes the full output of the first tool from the first turn and pipes it into jq.\n- For payloads that may contain quotes, parentheses, backticks, or arbitrary bytes (random/binary data, JSON with embedded quotes, multi-line strings), prefer a quoted-delimiter heredoc over \\`echo '…'\\`. The heredoc body is not interpreted by the shell, so substituted payloads pass through unchanged.\n- Heredoc example: \\`wc -c << 'EOF'\\\\n{{tool0turn0}}\\\\nEOF\\` (the quotes around \\`'EOF'\\` disable interpolation inside the body).\n- Unknown reference keys are left in place and surfaced as \\`[unresolved refs: …]\\` after the output.\n`.trim();\n\n/**\n * Composes the bash tool description, optionally appending the\n * tool-output references guide. Hosts that enable\n * `RunConfig.toolOutputReferences` should pass `enableToolOutputReferences: true`\n * when registering the tool so the LLM learns the `{{…}}` syntax it\n * will actually be able to use.\n */\nexport function buildBashExecutionToolDescription(options?: {\n enableToolOutputReferences?: boolean;\n}): string {\n if (options?.enableToolOutputReferences === true) {\n return `${BashExecutionToolDescription}\\n\\n${BashToolOutputReferencesGuide}`;\n }\n return BashExecutionToolDescription;\n}\n\nexport const BashExecutionToolName = Constants.BASH_TOOL;\n\n/**\n * Default bash tool definition using the base description.\n *\n * When `RunConfig.toolOutputReferences.enabled` is `true`, build a\n * reference-aware description with\n * {@link buildBashExecutionToolDescription}\n * (`{ enableToolOutputReferences: true }`) and construct a custom\n * definition using it — using this constant as-is leaves the LLM\n * unaware of the `{{tool<i>turn<n>}}` syntax.\n */\nexport const BashExecutionToolDefinition = {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n} as const;\n\nfunction createBashExecutionTool(\n params: t.BashExecutionToolParams = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n const { command, ...rest } = rawInput as {\n command: string;\n args?: string[];\n };\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang: 'bash',\n code: command,\n ...rest,\n ...params,\n };\n\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'LibreChat/1.0',\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error:\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createBashExecutionTool };\n"],"names":["config","getCodeBaseURL","Constants","tool","HttpsProxyAgent","imageExtRegex"],"mappings":"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"}
|
|
@@ -513,16 +513,20 @@ function annotateMessagesForLLM(messages, registry, runId) {
|
|
|
513
513
|
/**
|
|
514
514
|
* `additional_kwargs` is untyped at the LangChain layer
|
|
515
515
|
* (`Record<string, unknown>`), so persisted or client-supplied
|
|
516
|
-
* ToolMessages can carry arbitrary shapes
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
* provider call
|
|
516
|
+
* ToolMessages can carry arbitrary shapes — including primitives
|
|
517
|
+
* (a malformed serializer might write a string, or `null`).
|
|
518
|
+
* Guard with a runtime object check before the `in` probes
|
|
519
|
+
* because the `in` operator throws `TypeError` on primitives.
|
|
520
|
+
* A single malformed message must never crash the provider call
|
|
521
|
+
* path; skip its annotation/strip and continue.
|
|
521
522
|
*/
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const
|
|
523
|
+
const rawMeta = m.additional_kwargs;
|
|
524
|
+
if (rawMeta == null || typeof rawMeta !== 'object')
|
|
525
|
+
continue;
|
|
526
|
+
const meta = rawMeta;
|
|
527
|
+
const hasRefKey = '_refKey' in meta;
|
|
528
|
+
const hasRefScope = '_refScope' in meta;
|
|
529
|
+
const hasUnresolvedField = '_unresolvedRefs' in meta;
|
|
526
530
|
if (!hasRefKey && !hasRefScope && !hasUnresolvedField)
|
|
527
531
|
continue;
|
|
528
532
|
const refKey = readRefKey(meta);
|
|
@@ -550,6 +554,15 @@ function annotateMessagesForLLM(messages, registry, runId) {
|
|
|
550
554
|
type: 'text',
|
|
551
555
|
text: `[unresolved refs: ${unresolved.join(', ')}]`,
|
|
552
556
|
};
|
|
557
|
+
/**
|
|
558
|
+
* `as unknown as ToolMessage['content']` is unavoidable here:
|
|
559
|
+
* LangChain's content union (`MessageContentComplex[] |
|
|
560
|
+
* DataContentBlock[] | string`) does not accept a freshly built
|
|
561
|
+
* mixed array literal even though the structural shape is valid
|
|
562
|
+
* at runtime. The double-cast is structurally safe — we
|
|
563
|
+
* preserve every block from `tm.content` and prepend a single
|
|
564
|
+
* `{ type: 'text', text }` block that all providers accept.
|
|
565
|
+
*/
|
|
553
566
|
nextContent = [
|
|
554
567
|
warningBlock,
|
|
555
568
|
...tm.content,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolOutputReferences.cjs","sources":["../../../src/tools/toolOutputReferences.ts"],"sourcesContent":["/**\n * Tool output reference registry.\n *\n * When enabled via `RunConfig.toolOutputReferences.enabled`, ToolNode\n * stores each successful tool output under a stable key\n * (`tool<idx>turn<turn>`) where `idx` is the tool's position within a\n * ToolNode batch and `turn` is the batch index within the run\n * (incremented once per ToolNode invocation).\n *\n * Subsequent tool calls can pipe a previous output into their args by\n * embedding `{{tool<idx>turn<turn>}}` inside any string argument;\n * {@link ToolOutputReferenceRegistry.resolve} walks the args and\n * substitutes the placeholders immediately before invocation.\n *\n * The registry stores the *raw, untruncated* tool output so a later\n * `{{…}}` substitution pipes the full payload into the next tool —\n * even when the LLM only saw a head+tail-truncated preview in\n * `ToolMessage.content`. Outputs are stored without any annotation\n * (the `_ref` key or the `[ref: ...]` prefix seen by the LLM is\n * strictly a UX signal attached to `ToolMessage.content`). Keeping the\n * registry pristine means downstream bash/jq piping receives the\n * complete, verbatim output with no injected fields.\n */\n\nimport { ToolMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport {\n calculateMaxTotalToolOutputSize,\n HARD_MAX_TOOL_RESULT_CHARS,\n HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE,\n} from '@/utils/truncation';\n\n/**\n * Non-global matcher for a single `{{tool<i>turn<n>}}` placeholder.\n * Exported for consumers that want to detect references (e.g., syntax\n * highlighting, docs). The stateful `g` variant lives inside the\n * registry so nobody trips on `lastIndex`.\n */\nexport const TOOL_OUTPUT_REF_PATTERN = /\\{\\{(tool\\d+turn\\d+)\\}\\}/;\n\n/** Object key used when a parsed-object output has `_ref` injected. */\nexport const TOOL_OUTPUT_REF_KEY = '_ref';\n\n/**\n * Object key used to carry unresolved reference warnings on a parsed-\n * object output. Using a dedicated field instead of a trailing text\n * line keeps the annotated `ToolMessage.content` parseable as JSON for\n * downstream consumers that rely on the object shape.\n */\nexport const TOOL_OUTPUT_UNRESOLVED_KEY = '_unresolved_refs';\n\n/** Single-line prefix prepended to non-object tool outputs so the LLM sees the reference key. */\nexport function buildReferencePrefix(key: string): string {\n return `[ref: ${key}]`;\n}\n\n/** Stable registry key for a tool output. */\nexport function buildReferenceKey(toolIndex: number, turn: number): string {\n return `tool${toolIndex}turn${turn}`;\n}\n\nexport type ToolOutputReferenceRegistryOptions = {\n /** Maximum characters stored per registered output. */\n maxOutputSize?: number;\n /** Maximum total characters retained across all registered outputs. */\n maxTotalSize?: number;\n /**\n * Upper bound on the number of concurrently-tracked runs. When\n * exceeded, the oldest run bucket is evicted (FIFO). Defaults to 32.\n */\n maxActiveRuns?: number;\n};\n\n/**\n * Result of resolving placeholders in tool args.\n */\nexport type ResolveResult<T> = {\n /** Arguments with placeholders replaced. Same shape as the input. */\n resolved: T;\n /** Reference keys that were referenced but had no stored value. */\n unresolved: string[];\n};\n\n/**\n * Read-only view over a frozen registry snapshot. Returned by\n * {@link ToolOutputReferenceRegistry.snapshot} for callers that need\n * to resolve placeholders against the registry state at a specific\n * point in time, ignoring any subsequent registrations.\n */\nexport interface ToolOutputResolveView {\n resolve<T>(args: T): ResolveResult<T>;\n}\n\n/**\n * Pre-resolved arg map keyed by `toolCallId`. Used by the mixed\n * direct+event dispatch path to feed event calls' resolved args\n * (captured pre-batch) into the dispatcher without re-resolving\n * against the now-stale live registry.\n */\nexport type PreResolvedArgsMap = Map<\n string,\n { resolved: Record<string, unknown>; unresolved: string[] }\n>;\n\n/**\n * Per-call sink for resolved args, keyed by `toolCallId`. Threaded\n * as a per-batch local map so concurrent `ToolNode.run()` calls do\n * not race on shared sink state.\n */\nexport type ResolvedArgsByCallId = Map<string, Record<string, unknown>>;\n\nconst EMPTY_ENTRIES: ReadonlyMap<string, string> = new Map<string, string>();\n\n/**\n * Per-run state bucket held inside the registry. Each distinct\n * `run_id` gets its own bucket so overlapping concurrent runs on a\n * shared registry cannot leak outputs, turn counters, or warn-memos\n * into one another.\n */\nclass RunStateBucket {\n entries: Map<string, string> = new Map();\n totalSize: number = 0;\n turnCounter: number = 0;\n warnedNonStringTools: Set<string> = new Set();\n}\n\n/**\n * Anonymous (`run_id` absent) bucket key. Anonymous batches are\n * treated as fresh runs on every invocation — see `nextTurn`.\n */\nconst ANON_RUN_KEY = '\\0anon';\n\n/**\n * Default upper bound on the number of concurrently-tracked runs per\n * registry. When exceeded, the oldest run's bucket (by insertion\n * order) is evicted. Keeps memory bounded when a ToolNode is reused\n * across many runs without explicit `releaseRun` calls.\n */\nconst DEFAULT_MAX_ACTIVE_RUNS = 32;\n\n/**\n * Ordered map of reference-key → stored output, partitioned by run so\n * concurrent / interleaved runs sharing one registry cannot leak\n * outputs between each other.\n *\n * Each public method takes a `runId` which selects the run's bucket.\n * Hosts typically get one registry per run via `Graph`, in which\n * case only a single bucket is ever populated; the partitioning\n * exists so the registry also behaves correctly when a single\n * instance is reused directly.\n */\nexport class ToolOutputReferenceRegistry {\n private runStates: Map<string, RunStateBucket> = new Map();\n private readonly maxOutputSize: number;\n private readonly maxTotalSize: number;\n private readonly maxActiveRuns: number;\n /**\n * Local stateful matcher used only by `replaceInString`. Kept\n * off-module so callers of the exported `TOOL_OUTPUT_REF_PATTERN`\n * never see a stale `lastIndex`.\n */\n private static readonly PLACEHOLDER_MATCHER = /\\{\\{(tool\\d+turn\\d+)\\}\\}/g;\n\n constructor(options: ToolOutputReferenceRegistryOptions = {}) {\n /**\n * Per-output default is the same ~400 KB budget as the standard\n * tool-result truncation (`HARD_MAX_TOOL_RESULT_CHARS`). This\n * keeps a single `{{…}}` substitution at a size that is safe to\n * pass through typical shell `ARG_MAX` limits and matches what\n * the LLM would otherwise have seen. Hosts that want larger per-\n * output payloads (API consumers, long JSON streams) can raise\n * the cap explicitly up to the 5 MB total budget.\n */\n const perOutput =\n options.maxOutputSize != null && options.maxOutputSize > 0\n ? options.maxOutputSize\n : HARD_MAX_TOOL_RESULT_CHARS;\n /**\n * Clamp a caller-supplied `maxTotalSize` to\n * `HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE` (5 MB) so the documented\n * absolute cap is enforced regardless of host config —\n * `calculateMaxTotalToolOutputSize` already applies the same\n * upper bound on its computed default, but the user-provided\n * branch was bypassing it.\n */\n const totalRaw =\n options.maxTotalSize != null && options.maxTotalSize > 0\n ? Math.min(options.maxTotalSize, HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE)\n : calculateMaxTotalToolOutputSize(perOutput);\n this.maxTotalSize = totalRaw;\n /**\n * The per-output cap can never exceed the per-run aggregate cap:\n * if a single entry were allowed to be larger than `maxTotalSize`,\n * the eviction loop would either blow the cap (to keep the entry)\n * or self-evict a just-stored value. Clamping here turns\n * `maxTotalSize` into a hard upper bound on *any* state the\n * registry retains per run.\n */\n this.maxOutputSize = Math.min(perOutput, totalRaw);\n this.maxActiveRuns =\n options.maxActiveRuns != null && options.maxActiveRuns > 0\n ? options.maxActiveRuns\n : DEFAULT_MAX_ACTIVE_RUNS;\n }\n\n private keyFor(runId: string | undefined): string {\n return runId ?? ANON_RUN_KEY;\n }\n\n private getOrCreate(runId: string | undefined): RunStateBucket {\n const key = this.keyFor(runId);\n let state = this.runStates.get(key);\n if (state == null) {\n state = new RunStateBucket();\n this.runStates.set(key, state);\n if (this.runStates.size > this.maxActiveRuns) {\n const oldest = this.runStates.keys().next().value;\n if (oldest != null && oldest !== key) {\n this.runStates.delete(oldest);\n }\n }\n }\n return state;\n }\n\n /** Registers (or replaces) the output stored under `key` for `runId`. */\n set(runId: string | undefined, key: string, value: string): void {\n const bucket = this.getOrCreate(runId);\n const clipped =\n value.length > this.maxOutputSize\n ? value.slice(0, this.maxOutputSize)\n : value;\n const existing = bucket.entries.get(key);\n if (existing != null) {\n bucket.totalSize -= existing.length;\n bucket.entries.delete(key);\n }\n bucket.entries.set(key, clipped);\n bucket.totalSize += clipped.length;\n this.evictWithinBucket(bucket);\n }\n\n /** Returns the stored value for `key` in `runId`'s bucket, or `undefined`. */\n get(runId: string | undefined, key: string): string | undefined {\n return this.runStates.get(this.keyFor(runId))?.entries.get(key);\n }\n\n /**\n * Returns `true` when `key` is currently stored in `runId`'s bucket.\n * Used by {@link annotateMessagesForLLM} to gate transient annotation\n * on whether the registry still owns the referenced output (a stale\n * `_refKey` from a prior run silently no-ops here).\n */\n has(runId: string | undefined, key: string): boolean {\n return this.runStates.get(this.keyFor(runId))?.entries.has(key) ?? false;\n }\n\n /** Total number of registered outputs across every run bucket. */\n get size(): number {\n let n = 0;\n for (const bucket of this.runStates.values()) {\n n += bucket.entries.size;\n }\n return n;\n }\n\n /** Maximum characters retained per output (post-clip). */\n get perOutputLimit(): number {\n return this.maxOutputSize;\n }\n\n /** Maximum total characters retained *per run*. */\n get totalLimit(): number {\n return this.maxTotalSize;\n }\n\n /** Drops every run's state. */\n clear(): void {\n this.runStates.clear();\n }\n\n /**\n * Explicitly release `runId`'s state. Safe to call when a run has\n * finished. Hosts sharing one registry across runs should call this\n * to reclaim memory deterministically; otherwise LRU eviction kicks\n * in when `maxActiveRuns` runs accumulate.\n */\n releaseRun(runId: string | undefined): void {\n this.runStates.delete(this.keyFor(runId));\n }\n\n /**\n * Claims the next batch turn synchronously from `runId`'s bucket.\n *\n * Must be called once at the start of each ToolNode batch before\n * any `await`, so concurrent invocations within the same run see\n * distinct turn values (reads are effectively atomic by JS's\n * single-threaded execution of the sync prefix).\n *\n * If `runId` is missing the anonymous bucket is dropped and a\n * fresh one created so each anonymous call behaves as its own run.\n */\n nextTurn(runId: string | undefined): number {\n if (runId == null) {\n this.runStates.delete(ANON_RUN_KEY);\n }\n const bucket = this.getOrCreate(runId);\n return bucket.turnCounter++;\n }\n\n /**\n * Records that `toolName` has been warned about in `runId` (returns\n * `true` on the first call per run, `false` after). Used by\n * ToolNode to emit one log line per offending tool per run when a\n * `ToolMessage.content` isn't a string.\n */\n claimWarnOnce(runId: string | undefined, toolName: string): boolean {\n const bucket = this.getOrCreate(runId);\n if (bucket.warnedNonStringTools.has(toolName)) {\n return false;\n }\n bucket.warnedNonStringTools.add(toolName);\n return true;\n }\n\n /**\n * Walks `args` and replaces every `{{tool<i>turn<n>}}` placeholder in\n * string values with the stored output *from `runId`'s bucket*. Non-\n * string values and object keys are left untouched. Unresolved\n * references are left in-place and reported so the caller can\n * surface them to the LLM. When no placeholder appears anywhere in\n * the serialized args, the original input is returned without\n * walking the tree.\n */\n resolve<T>(runId: string | undefined, args: T): ResolveResult<T> {\n if (!hasAnyPlaceholder(args)) {\n return { resolved: args, unresolved: [] };\n }\n const bucket = this.runStates.get(this.keyFor(runId));\n return this.resolveAgainst(bucket?.entries ?? EMPTY_ENTRIES, args);\n }\n\n /**\n * Captures a frozen snapshot of `runId`'s current entries and\n * returns a view that resolves placeholders against *only* that\n * snapshot. The snapshot is decoupled from the live registry, so\n * subsequent `set()` calls (for example, same-turn direct outputs\n * registering while an event branch is still in flight) are\n * invisible to the snapshot's `resolve`. Used by the mixed\n * direct+event dispatch path to preserve same-turn isolation when\n * a `PreToolUse` hook rewrites event args after directs have\n * completed.\n */\n snapshot(runId: string | undefined): ToolOutputResolveView {\n const bucket = this.runStates.get(this.keyFor(runId));\n const entries: ReadonlyMap<string, string> = bucket\n ? new Map(bucket.entries)\n : EMPTY_ENTRIES;\n return {\n resolve: <T>(args: T): ResolveResult<T> =>\n this.resolveAgainst(entries, args),\n };\n }\n\n private resolveAgainst<T>(\n entries: ReadonlyMap<string, string>,\n args: T\n ): ResolveResult<T> {\n if (!hasAnyPlaceholder(args)) {\n return { resolved: args, unresolved: [] };\n }\n const unresolved = new Set<string>();\n const resolved = this.transform(entries, args, unresolved) as T;\n return { resolved, unresolved: Array.from(unresolved) };\n }\n\n private transform(\n entries: ReadonlyMap<string, string>,\n value: unknown,\n unresolved: Set<string>\n ): unknown {\n if (typeof value === 'string') {\n return this.replaceInString(entries, value, unresolved);\n }\n if (Array.isArray(value)) {\n return value.map((item) => this.transform(entries, item, unresolved));\n }\n if (value !== null && typeof value === 'object') {\n const source = value as Record<string, unknown>;\n const next: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(source)) {\n next[key] = this.transform(entries, item, unresolved);\n }\n return next;\n }\n return value;\n }\n\n private replaceInString(\n entries: ReadonlyMap<string, string>,\n input: string,\n unresolved: Set<string>\n ): string {\n if (input.indexOf('{{tool') === -1) {\n return input;\n }\n return input.replace(\n ToolOutputReferenceRegistry.PLACEHOLDER_MATCHER,\n (match, key: string) => {\n const stored = entries.get(key);\n if (stored == null) {\n unresolved.add(key);\n return match;\n }\n return stored;\n }\n );\n }\n\n private evictWithinBucket(bucket: RunStateBucket): void {\n if (bucket.totalSize <= this.maxTotalSize) {\n return;\n }\n for (const key of bucket.entries.keys()) {\n if (bucket.totalSize <= this.maxTotalSize) {\n return;\n }\n const entry = bucket.entries.get(key);\n if (entry == null) {\n continue;\n }\n bucket.totalSize -= entry.length;\n bucket.entries.delete(key);\n }\n }\n}\n\n/**\n * Cheap pre-check: returns true if any string value in `args` contains\n * the `{{tool` substring. Lets `resolve()` skip the deep tree walk (and\n * its object allocations) for the common case of plain args.\n */\nfunction hasAnyPlaceholder(value: unknown): boolean {\n if (typeof value === 'string') {\n return value.indexOf('{{tool') !== -1;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n if (hasAnyPlaceholder(item)) {\n return true;\n }\n }\n return false;\n }\n if (value !== null && typeof value === 'object') {\n for (const item of Object.values(value as Record<string, unknown>)) {\n if (hasAnyPlaceholder(item)) {\n return true;\n }\n }\n return false;\n }\n return false;\n}\n\n/**\n * Annotates `content` with a reference key and/or unresolved-ref\n * warnings so the LLM sees both alongside the tool output.\n *\n * Behavior:\n * - If `content` parses as a plain (non-array, non-null) JSON object\n * and the object does not already have a conflicting `_ref` key,\n * the reference key and (when present) `_unresolved_refs` array\n * are injected as object fields, preserving JSON validity for\n * downstream consumers that parse the output.\n * - Otherwise (string output, JSON array/primitive, parse failure,\n * or `_ref` collision), a `[ref: <key>]\\n` prefix line is\n * prepended and unresolved refs are appended as a trailing\n * `[unresolved refs: …]` line.\n *\n * The annotated string is what the LLM sees as `ToolMessage.content`.\n * The *original* (un-annotated) value is what gets stored in the\n * registry, so downstream piping remains pristine.\n *\n * @param content Raw (post-truncation) tool output.\n * @param key Reference key for this output, or undefined when\n * there is nothing to register (errors etc.).\n * @param unresolved Reference keys that failed to resolve during\n * argument substitution. Surfaced so the LLM can\n * self-correct its next tool call.\n */\nexport function annotateToolOutputWithReference(\n content: string,\n key: string | undefined,\n unresolved: string[] = []\n): string {\n const hasRefKey = key != null;\n const hasUnresolved = unresolved.length > 0;\n if (!hasRefKey && !hasUnresolved) {\n return content;\n }\n const trimmed = content.trimStart();\n if (trimmed.startsWith('{')) {\n const annotated = tryInjectRefIntoJsonObject(content, key, unresolved);\n if (annotated != null) {\n return annotated;\n }\n }\n const prefix = hasRefKey ? `${buildReferencePrefix(key!)}\\n` : '';\n const trailer = hasUnresolved\n ? `\\n[unresolved refs: ${unresolved.join(', ')}]`\n : '';\n return `${prefix}${content}${trailer}`;\n}\n\nfunction tryInjectRefIntoJsonObject(\n content: string,\n key: string | undefined,\n unresolved: string[]\n): string | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n return null;\n }\n\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return null;\n }\n\n const obj = parsed as Record<string, unknown>;\n const injectingRef = key != null;\n const injectingUnresolved = unresolved.length > 0;\n\n /**\n * Reject the JSON-injection path (fall back to prefix form) when\n * either of our keys collides with real payload data:\n * - `_ref` collision: existing value is non-null and differs from\n * the key we're about to inject.\n * - `_unresolved_refs` collision: existing value is non-null and\n * is not a deep-equal match for the array we'd inject.\n * This keeps us from silently overwriting legitimate tool output.\n */\n if (\n injectingRef &&\n TOOL_OUTPUT_REF_KEY in obj &&\n obj[TOOL_OUTPUT_REF_KEY] !== key &&\n obj[TOOL_OUTPUT_REF_KEY] != null\n ) {\n return null;\n }\n if (\n injectingUnresolved &&\n TOOL_OUTPUT_UNRESOLVED_KEY in obj &&\n obj[TOOL_OUTPUT_UNRESOLVED_KEY] != null &&\n !arraysShallowEqual(obj[TOOL_OUTPUT_UNRESOLVED_KEY], unresolved)\n ) {\n return null;\n }\n\n /**\n * Only strip the framework-owned key we're actually injecting —\n * leave everything else (including a pre-existing `_ref` on the\n * unresolved-only path, or a pre-existing `_unresolved_refs` on a\n * plain-annotation path) untouched so we annotate rather than\n * mutate downstream payload data. Our injected keys land first in\n * the serialized JSON so the LLM sees them before the body.\n */\n const omitKeys = new Set<string>();\n if (injectingRef) omitKeys.add(TOOL_OUTPUT_REF_KEY);\n if (injectingUnresolved) omitKeys.add(TOOL_OUTPUT_UNRESOLVED_KEY);\n const rest: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (!omitKeys.has(k)) {\n rest[k] = v;\n }\n }\n const injected: Record<string, unknown> = {};\n if (injectingRef) {\n injected[TOOL_OUTPUT_REF_KEY] = key;\n }\n if (injectingUnresolved) {\n injected[TOOL_OUTPUT_UNRESOLVED_KEY] = unresolved;\n }\n Object.assign(injected, rest);\n\n const pretty = /^\\{\\s*\\n/.test(content);\n return pretty ? JSON.stringify(injected, null, 2) : JSON.stringify(injected);\n}\n\nfunction arraysShallowEqual(a: unknown, b: readonly string[]): boolean {\n if (!Array.isArray(a) || a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Lazy projection that, given a registry and a runId, returns a new\n * `messages` array where each `ToolMessage` carrying ref metadata is\n * projected into a transient copy with annotated content (when the ref\n * is live in the registry) and with the framework-owned `additional_\n * kwargs` keys (`_refKey`, `_refScope`, `_unresolvedRefs`) stripped\n * regardless of whether annotation applied. The original input array\n * and its messages are never mutated.\n *\n * Annotation is gated on registry presence: a stale `_refKey` from a\n * prior run (e.g. one that survived in persisted history) silently\n * no-ops on the *content* side. The strip-metadata side still runs so\n * stale framework keys never leak onto the wire under any custom or\n * future provider serializer that might transmit `additional_kwargs`.\n * `_unresolvedRefs` is always meaningful and is not gated.\n *\n * **Feature-disabled fast path:** when the host hasn't enabled the\n * tool-output-reference feature, the registry is `undefined` and this\n * function returns the input array reference-equal *without iterating\n * a single message*. The loop is exclusive to the feature-enabled\n * code path.\n */\nexport function annotateMessagesForLLM(\n messages: BaseMessage[],\n registry: ToolOutputReferenceRegistry | undefined,\n runId: string | undefined\n): BaseMessage[] {\n if (registry == null) return messages;\n\n /**\n * Lazy-allocate the output array so the common case (no ToolMessage\n * carries framework metadata) returns the input reference-equal with\n * zero allocations beyond the per-message predicate checks.\n */\n let out: BaseMessage[] | undefined;\n for (let i = 0; i < messages.length; i++) {\n const m = messages[i];\n if (m._getType() !== 'tool') continue;\n /**\n * `additional_kwargs` is untyped at the LangChain layer\n * (`Record<string, unknown>`), so persisted or client-supplied\n * ToolMessages can carry arbitrary shapes under our framework\n * keys. Treat them as untrusted input and coerce defensively\n * before any array operation — a malformed field on a single\n * hydrated message must not crash `attemptInvoke` before the\n * provider call.\n */\n const meta = m.additional_kwargs as Record<string, unknown> | undefined;\n const hasRefKey = meta != null && '_refKey' in meta;\n const hasRefScope = meta != null && '_refScope' in meta;\n const hasUnresolvedField = meta != null && '_unresolvedRefs' in meta;\n if (!hasRefKey && !hasRefScope && !hasUnresolvedField) continue;\n\n const refKey = readRefKey(meta);\n const unresolved = readUnresolvedRefs(meta);\n\n /**\n * Prefer the message-stamped `_refScope` for the registry lookup.\n * For named runs it equals the current `runId`; for anonymous\n * invocations it carries the per-batch synthetic scope minted by\n * ToolNode (`\\0anon-<n>`), which `runId` from config cannot\n * recover. Falling back to `runId` keeps backward compatibility\n * with messages stamped before this field existed.\n */\n const lookupScope = readRefScope(meta) ?? runId;\n const liveRef =\n refKey != null && registry.has(lookupScope, refKey) ? refKey : undefined;\n const annotates = liveRef != null || unresolved.length > 0;\n\n const tm = m as ToolMessage;\n let nextContent: ToolMessage['content'] = tm.content;\n\n if (annotates && typeof tm.content === 'string') {\n nextContent = annotateToolOutputWithReference(\n tm.content,\n liveRef,\n unresolved\n );\n } else if (\n annotates &&\n Array.isArray(tm.content) &&\n unresolved.length > 0\n ) {\n const warningBlock = {\n type: 'text' as const,\n text: `[unresolved refs: ${unresolved.join(', ')}]`,\n };\n nextContent = [\n warningBlock,\n ...tm.content,\n ] as unknown as ToolMessage['content'];\n }\n\n /**\n * Project unconditionally: even when no annotation applies (stale\n * `_refKey` or non-annotatable content), `cloneToolMessageWithContent`\n * runs `stripFrameworkRefMetadata` on `additional_kwargs` so the\n * framework-owned keys never reach the wire.\n */\n out ??= messages.slice();\n out[i] = cloneToolMessageWithContent(tm, nextContent);\n }\n\n return out ?? messages;\n}\n\n/**\n * Reads `_refKey` defensively from untyped `additional_kwargs`. Returns\n * undefined for non-string values so a malformed field cannot poison\n * the registry lookup or downstream string operations.\n */\nfunction readRefKey(\n meta: Record<string, unknown> | undefined\n): string | undefined {\n const v = meta?._refKey;\n return typeof v === 'string' ? v : undefined;\n}\n\n/**\n * Reads `_refScope` defensively from untyped `additional_kwargs`.\n * Mirrors {@link readRefKey} — non-string scopes are dropped (the\n * caller falls back to the run-derived scope) rather than passed into\n * the registry as a malformed key.\n */\nfunction readRefScope(\n meta: Record<string, unknown> | undefined\n): string | undefined {\n const v = meta?._refScope;\n return typeof v === 'string' ? v : undefined;\n}\n\n/**\n * Reads `_unresolvedRefs` defensively from untyped `additional_kwargs`.\n * Returns an empty array for any non-array value, and filters out\n * non-string entries from a real array. Without this guard, a hydrated\n * ToolMessage carrying e.g. `_unresolvedRefs: 'tool0turn0'` would crash\n * `attemptInvoke` on the eventual `.length` / `.join(...)` call.\n */\nfunction readUnresolvedRefs(\n meta: Record<string, unknown> | undefined\n): string[] {\n const v = meta?._unresolvedRefs;\n if (!Array.isArray(v)) return [];\n const out: string[] = [];\n for (const item of v) {\n if (typeof item === 'string') out.push(item);\n }\n return out;\n}\n\n/**\n * Builds a fresh `ToolMessage` that mirrors `tm`'s identity fields with\n * the supplied `content`. Every `ToolMessage` field but `content` is\n * carried over so the projection is structurally identical to the\n * original from a LangChain serializer's perspective.\n *\n * `additional_kwargs` is rebuilt with the framework-owned ref keys\n * stripped. Defensive: LangChain's standard provider serializers do not\n * transmit `additional_kwargs` to provider HTTP APIs, but a custom\n * adapter or future LangChain change could. Stripping keeps the\n * implementation correct under any serializer behavior at the cost of a\n * shallow object spread per annotated message.\n */\nfunction cloneToolMessageWithContent(\n tm: ToolMessage,\n content: ToolMessage['content']\n): ToolMessage {\n return new ToolMessage({\n id: tm.id,\n name: tm.name,\n status: tm.status,\n artifact: tm.artifact,\n tool_call_id: tm.tool_call_id,\n response_metadata: tm.response_metadata,\n additional_kwargs: stripFrameworkRefMetadata(tm.additional_kwargs),\n content,\n });\n}\n\n/**\n * Returns a copy of `kwargs` with `_refKey`, `_refScope`, and\n * `_unresolvedRefs` removed. Returns the input reference-equal when\n * none of those keys are present so the no-strip path stays cheap;\n * returns `undefined` when stripping leaves the object empty so the\n * caller can drop the field entirely.\n */\nfunction stripFrameworkRefMetadata(\n kwargs: Record<string, unknown> | undefined\n): Record<string, unknown> | undefined {\n if (kwargs == null) return undefined;\n if (\n !('_refKey' in kwargs) &&\n !('_refScope' in kwargs) &&\n !('_unresolvedRefs' in kwargs)\n ) {\n return kwargs;\n }\n const { _refKey, _refScope, _unresolvedRefs, ...rest } = kwargs as Record<\n string,\n unknown\n > & {\n _refKey?: unknown;\n _refScope?: unknown;\n _unresolvedRefs?: unknown;\n };\n void _refKey;\n void _refScope;\n void _unresolvedRefs;\n return Object.keys(rest).length === 0 ? undefined : rest;\n}\n"],"names":["HARD_MAX_TOOL_RESULT_CHARS","HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE","calculateMaxTotalToolOutputSize","ToolMessage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toolOutputReferences.cjs","sources":["../../../src/tools/toolOutputReferences.ts"],"sourcesContent":["/**\n * Tool output reference registry.\n *\n * When enabled via `RunConfig.toolOutputReferences.enabled`, ToolNode\n * stores each successful tool output under a stable key\n * (`tool<idx>turn<turn>`) where `idx` is the tool's position within a\n * ToolNode batch and `turn` is the batch index within the run\n * (incremented once per ToolNode invocation).\n *\n * Subsequent tool calls can pipe a previous output into their args by\n * embedding `{{tool<idx>turn<turn>}}` inside any string argument;\n * {@link ToolOutputReferenceRegistry.resolve} walks the args and\n * substitutes the placeholders immediately before invocation.\n *\n * The registry stores the *raw, untruncated* tool output so a later\n * `{{…}}` substitution pipes the full payload into the next tool —\n * even when the LLM only saw a head+tail-truncated preview in\n * `ToolMessage.content`. Outputs are stored without any annotation\n * (the `_ref` key or the `[ref: ...]` prefix seen by the LLM is\n * strictly a UX signal attached to `ToolMessage.content`). Keeping the\n * registry pristine means downstream bash/jq piping receives the\n * complete, verbatim output with no injected fields.\n */\n\nimport { ToolMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport {\n calculateMaxTotalToolOutputSize,\n HARD_MAX_TOOL_RESULT_CHARS,\n HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE,\n} from '@/utils/truncation';\n\n/**\n * Non-global matcher for a single `{{tool<i>turn<n>}}` placeholder.\n * Exported for consumers that want to detect references (e.g., syntax\n * highlighting, docs). The stateful `g` variant lives inside the\n * registry so nobody trips on `lastIndex`.\n */\nexport const TOOL_OUTPUT_REF_PATTERN = /\\{\\{(tool\\d+turn\\d+)\\}\\}/;\n\n/** Object key used when a parsed-object output has `_ref` injected. */\nexport const TOOL_OUTPUT_REF_KEY = '_ref';\n\n/**\n * Object key used to carry unresolved reference warnings on a parsed-\n * object output. Using a dedicated field instead of a trailing text\n * line keeps the annotated `ToolMessage.content` parseable as JSON for\n * downstream consumers that rely on the object shape.\n */\nexport const TOOL_OUTPUT_UNRESOLVED_KEY = '_unresolved_refs';\n\n/** Single-line prefix prepended to non-object tool outputs so the LLM sees the reference key. */\nexport function buildReferencePrefix(key: string): string {\n return `[ref: ${key}]`;\n}\n\n/** Stable registry key for a tool output. */\nexport function buildReferenceKey(toolIndex: number, turn: number): string {\n return `tool${toolIndex}turn${turn}`;\n}\n\nexport type ToolOutputReferenceRegistryOptions = {\n /** Maximum characters stored per registered output. */\n maxOutputSize?: number;\n /** Maximum total characters retained across all registered outputs. */\n maxTotalSize?: number;\n /**\n * Upper bound on the number of concurrently-tracked runs. When\n * exceeded, the oldest run bucket is evicted (FIFO). Defaults to 32.\n */\n maxActiveRuns?: number;\n};\n\n/**\n * Result of resolving placeholders in tool args.\n */\nexport type ResolveResult<T> = {\n /** Arguments with placeholders replaced. Same shape as the input. */\n resolved: T;\n /** Reference keys that were referenced but had no stored value. */\n unresolved: string[];\n};\n\n/**\n * Read-only view over a frozen registry snapshot. Returned by\n * {@link ToolOutputReferenceRegistry.snapshot} for callers that need\n * to resolve placeholders against the registry state at a specific\n * point in time, ignoring any subsequent registrations.\n */\nexport interface ToolOutputResolveView {\n resolve<T>(args: T): ResolveResult<T>;\n}\n\n/**\n * Pre-resolved arg map keyed by `toolCallId`. Used by the mixed\n * direct+event dispatch path to feed event calls' resolved args\n * (captured pre-batch) into the dispatcher without re-resolving\n * against the now-stale live registry.\n */\nexport type PreResolvedArgsMap = Map<\n string,\n { resolved: Record<string, unknown>; unresolved: string[] }\n>;\n\n/**\n * Per-call sink for resolved args, keyed by `toolCallId`. Threaded\n * as a per-batch local map so concurrent `ToolNode.run()` calls do\n * not race on shared sink state.\n */\nexport type ResolvedArgsByCallId = Map<string, Record<string, unknown>>;\n\nconst EMPTY_ENTRIES: ReadonlyMap<string, string> = new Map<string, string>();\n\n/**\n * Per-run state bucket held inside the registry. Each distinct\n * `run_id` gets its own bucket so overlapping concurrent runs on a\n * shared registry cannot leak outputs, turn counters, or warn-memos\n * into one another.\n */\nclass RunStateBucket {\n entries: Map<string, string> = new Map();\n totalSize: number = 0;\n turnCounter: number = 0;\n warnedNonStringTools: Set<string> = new Set();\n}\n\n/**\n * Anonymous (`run_id` absent) bucket key. Anonymous batches are\n * treated as fresh runs on every invocation — see `nextTurn`.\n */\nconst ANON_RUN_KEY = '\\0anon';\n\n/**\n * Default upper bound on the number of concurrently-tracked runs per\n * registry. When exceeded, the oldest run's bucket (by insertion\n * order) is evicted. Keeps memory bounded when a ToolNode is reused\n * across many runs without explicit `releaseRun` calls.\n */\nconst DEFAULT_MAX_ACTIVE_RUNS = 32;\n\n/**\n * Ordered map of reference-key → stored output, partitioned by run so\n * concurrent / interleaved runs sharing one registry cannot leak\n * outputs between each other.\n *\n * Each public method takes a `runId` which selects the run's bucket.\n * Hosts typically get one registry per run via `Graph`, in which\n * case only a single bucket is ever populated; the partitioning\n * exists so the registry also behaves correctly when a single\n * instance is reused directly.\n */\nexport class ToolOutputReferenceRegistry {\n private runStates: Map<string, RunStateBucket> = new Map();\n private readonly maxOutputSize: number;\n private readonly maxTotalSize: number;\n private readonly maxActiveRuns: number;\n /**\n * Local stateful matcher used only by `replaceInString`. Kept\n * off-module so callers of the exported `TOOL_OUTPUT_REF_PATTERN`\n * never see a stale `lastIndex`.\n */\n private static readonly PLACEHOLDER_MATCHER = /\\{\\{(tool\\d+turn\\d+)\\}\\}/g;\n\n constructor(options: ToolOutputReferenceRegistryOptions = {}) {\n /**\n * Per-output default is the same ~400 KB budget as the standard\n * tool-result truncation (`HARD_MAX_TOOL_RESULT_CHARS`). This\n * keeps a single `{{…}}` substitution at a size that is safe to\n * pass through typical shell `ARG_MAX` limits and matches what\n * the LLM would otherwise have seen. Hosts that want larger per-\n * output payloads (API consumers, long JSON streams) can raise\n * the cap explicitly up to the 5 MB total budget.\n */\n const perOutput =\n options.maxOutputSize != null && options.maxOutputSize > 0\n ? options.maxOutputSize\n : HARD_MAX_TOOL_RESULT_CHARS;\n /**\n * Clamp a caller-supplied `maxTotalSize` to\n * `HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE` (5 MB) so the documented\n * absolute cap is enforced regardless of host config —\n * `calculateMaxTotalToolOutputSize` already applies the same\n * upper bound on its computed default, but the user-provided\n * branch was bypassing it.\n */\n const totalRaw =\n options.maxTotalSize != null && options.maxTotalSize > 0\n ? Math.min(options.maxTotalSize, HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE)\n : calculateMaxTotalToolOutputSize(perOutput);\n this.maxTotalSize = totalRaw;\n /**\n * The per-output cap can never exceed the per-run aggregate cap:\n * if a single entry were allowed to be larger than `maxTotalSize`,\n * the eviction loop would either blow the cap (to keep the entry)\n * or self-evict a just-stored value. Clamping here turns\n * `maxTotalSize` into a hard upper bound on *any* state the\n * registry retains per run.\n */\n this.maxOutputSize = Math.min(perOutput, totalRaw);\n this.maxActiveRuns =\n options.maxActiveRuns != null && options.maxActiveRuns > 0\n ? options.maxActiveRuns\n : DEFAULT_MAX_ACTIVE_RUNS;\n }\n\n private keyFor(runId: string | undefined): string {\n return runId ?? ANON_RUN_KEY;\n }\n\n private getOrCreate(runId: string | undefined): RunStateBucket {\n const key = this.keyFor(runId);\n let state = this.runStates.get(key);\n if (state == null) {\n state = new RunStateBucket();\n this.runStates.set(key, state);\n if (this.runStates.size > this.maxActiveRuns) {\n const oldest = this.runStates.keys().next().value;\n if (oldest != null && oldest !== key) {\n this.runStates.delete(oldest);\n }\n }\n }\n return state;\n }\n\n /** Registers (or replaces) the output stored under `key` for `runId`. */\n set(runId: string | undefined, key: string, value: string): void {\n const bucket = this.getOrCreate(runId);\n const clipped =\n value.length > this.maxOutputSize\n ? value.slice(0, this.maxOutputSize)\n : value;\n const existing = bucket.entries.get(key);\n if (existing != null) {\n bucket.totalSize -= existing.length;\n bucket.entries.delete(key);\n }\n bucket.entries.set(key, clipped);\n bucket.totalSize += clipped.length;\n this.evictWithinBucket(bucket);\n }\n\n /** Returns the stored value for `key` in `runId`'s bucket, or `undefined`. */\n get(runId: string | undefined, key: string): string | undefined {\n return this.runStates.get(this.keyFor(runId))?.entries.get(key);\n }\n\n /**\n * Returns `true` when `key` is currently stored in `runId`'s bucket.\n * Used by {@link annotateMessagesForLLM} to gate transient annotation\n * on whether the registry still owns the referenced output (a stale\n * `_refKey` from a prior run silently no-ops here).\n */\n has(runId: string | undefined, key: string): boolean {\n return this.runStates.get(this.keyFor(runId))?.entries.has(key) ?? false;\n }\n\n /** Total number of registered outputs across every run bucket. */\n get size(): number {\n let n = 0;\n for (const bucket of this.runStates.values()) {\n n += bucket.entries.size;\n }\n return n;\n }\n\n /** Maximum characters retained per output (post-clip). */\n get perOutputLimit(): number {\n return this.maxOutputSize;\n }\n\n /** Maximum total characters retained *per run*. */\n get totalLimit(): number {\n return this.maxTotalSize;\n }\n\n /** Drops every run's state. */\n clear(): void {\n this.runStates.clear();\n }\n\n /**\n * Explicitly release `runId`'s state. Safe to call when a run has\n * finished. Hosts sharing one registry across runs should call this\n * to reclaim memory deterministically; otherwise LRU eviction kicks\n * in when `maxActiveRuns` runs accumulate.\n */\n releaseRun(runId: string | undefined): void {\n this.runStates.delete(this.keyFor(runId));\n }\n\n /**\n * Claims the next batch turn synchronously from `runId`'s bucket.\n *\n * Must be called once at the start of each ToolNode batch before\n * any `await`, so concurrent invocations within the same run see\n * distinct turn values (reads are effectively atomic by JS's\n * single-threaded execution of the sync prefix).\n *\n * If `runId` is missing the anonymous bucket is dropped and a\n * fresh one created so each anonymous call behaves as its own run.\n */\n nextTurn(runId: string | undefined): number {\n if (runId == null) {\n this.runStates.delete(ANON_RUN_KEY);\n }\n const bucket = this.getOrCreate(runId);\n return bucket.turnCounter++;\n }\n\n /**\n * Records that `toolName` has been warned about in `runId` (returns\n * `true` on the first call per run, `false` after). Used by\n * ToolNode to emit one log line per offending tool per run when a\n * `ToolMessage.content` isn't a string.\n */\n claimWarnOnce(runId: string | undefined, toolName: string): boolean {\n const bucket = this.getOrCreate(runId);\n if (bucket.warnedNonStringTools.has(toolName)) {\n return false;\n }\n bucket.warnedNonStringTools.add(toolName);\n return true;\n }\n\n /**\n * Walks `args` and replaces every `{{tool<i>turn<n>}}` placeholder in\n * string values with the stored output *from `runId`'s bucket*. Non-\n * string values and object keys are left untouched. Unresolved\n * references are left in-place and reported so the caller can\n * surface them to the LLM. When no placeholder appears anywhere in\n * the serialized args, the original input is returned without\n * walking the tree.\n */\n resolve<T>(runId: string | undefined, args: T): ResolveResult<T> {\n if (!hasAnyPlaceholder(args)) {\n return { resolved: args, unresolved: [] };\n }\n const bucket = this.runStates.get(this.keyFor(runId));\n return this.resolveAgainst(bucket?.entries ?? EMPTY_ENTRIES, args);\n }\n\n /**\n * Captures a frozen snapshot of `runId`'s current entries and\n * returns a view that resolves placeholders against *only* that\n * snapshot. The snapshot is decoupled from the live registry, so\n * subsequent `set()` calls (for example, same-turn direct outputs\n * registering while an event branch is still in flight) are\n * invisible to the snapshot's `resolve`. Used by the mixed\n * direct+event dispatch path to preserve same-turn isolation when\n * a `PreToolUse` hook rewrites event args after directs have\n * completed.\n */\n snapshot(runId: string | undefined): ToolOutputResolveView {\n const bucket = this.runStates.get(this.keyFor(runId));\n const entries: ReadonlyMap<string, string> = bucket\n ? new Map(bucket.entries)\n : EMPTY_ENTRIES;\n return {\n resolve: <T>(args: T): ResolveResult<T> =>\n this.resolveAgainst(entries, args),\n };\n }\n\n private resolveAgainst<T>(\n entries: ReadonlyMap<string, string>,\n args: T\n ): ResolveResult<T> {\n if (!hasAnyPlaceholder(args)) {\n return { resolved: args, unresolved: [] };\n }\n const unresolved = new Set<string>();\n const resolved = this.transform(entries, args, unresolved) as T;\n return { resolved, unresolved: Array.from(unresolved) };\n }\n\n private transform(\n entries: ReadonlyMap<string, string>,\n value: unknown,\n unresolved: Set<string>\n ): unknown {\n if (typeof value === 'string') {\n return this.replaceInString(entries, value, unresolved);\n }\n if (Array.isArray(value)) {\n return value.map((item) => this.transform(entries, item, unresolved));\n }\n if (value !== null && typeof value === 'object') {\n const source = value as Record<string, unknown>;\n const next: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(source)) {\n next[key] = this.transform(entries, item, unresolved);\n }\n return next;\n }\n return value;\n }\n\n private replaceInString(\n entries: ReadonlyMap<string, string>,\n input: string,\n unresolved: Set<string>\n ): string {\n if (input.indexOf('{{tool') === -1) {\n return input;\n }\n return input.replace(\n ToolOutputReferenceRegistry.PLACEHOLDER_MATCHER,\n (match, key: string) => {\n const stored = entries.get(key);\n if (stored == null) {\n unresolved.add(key);\n return match;\n }\n return stored;\n }\n );\n }\n\n private evictWithinBucket(bucket: RunStateBucket): void {\n if (bucket.totalSize <= this.maxTotalSize) {\n return;\n }\n for (const key of bucket.entries.keys()) {\n if (bucket.totalSize <= this.maxTotalSize) {\n return;\n }\n const entry = bucket.entries.get(key);\n if (entry == null) {\n continue;\n }\n bucket.totalSize -= entry.length;\n bucket.entries.delete(key);\n }\n }\n}\n\n/**\n * Cheap pre-check: returns true if any string value in `args` contains\n * the `{{tool` substring. Lets `resolve()` skip the deep tree walk (and\n * its object allocations) for the common case of plain args.\n */\nfunction hasAnyPlaceholder(value: unknown): boolean {\n if (typeof value === 'string') {\n return value.indexOf('{{tool') !== -1;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n if (hasAnyPlaceholder(item)) {\n return true;\n }\n }\n return false;\n }\n if (value !== null && typeof value === 'object') {\n for (const item of Object.values(value as Record<string, unknown>)) {\n if (hasAnyPlaceholder(item)) {\n return true;\n }\n }\n return false;\n }\n return false;\n}\n\n/**\n * Annotates `content` with a reference key and/or unresolved-ref\n * warnings so the LLM sees both alongside the tool output.\n *\n * Behavior:\n * - If `content` parses as a plain (non-array, non-null) JSON object\n * and the object does not already have a conflicting `_ref` key,\n * the reference key and (when present) `_unresolved_refs` array\n * are injected as object fields, preserving JSON validity for\n * downstream consumers that parse the output.\n * - Otherwise (string output, JSON array/primitive, parse failure,\n * or `_ref` collision), a `[ref: <key>]\\n` prefix line is\n * prepended and unresolved refs are appended as a trailing\n * `[unresolved refs: …]` line.\n *\n * The annotated string is what the LLM sees as `ToolMessage.content`.\n * The *original* (un-annotated) value is what gets stored in the\n * registry, so downstream piping remains pristine.\n *\n * @param content Raw (post-truncation) tool output.\n * @param key Reference key for this output, or undefined when\n * there is nothing to register (errors etc.).\n * @param unresolved Reference keys that failed to resolve during\n * argument substitution. Surfaced so the LLM can\n * self-correct its next tool call.\n */\nexport function annotateToolOutputWithReference(\n content: string,\n key: string | undefined,\n unresolved: string[] = []\n): string {\n const hasRefKey = key != null;\n const hasUnresolved = unresolved.length > 0;\n if (!hasRefKey && !hasUnresolved) {\n return content;\n }\n const trimmed = content.trimStart();\n if (trimmed.startsWith('{')) {\n const annotated = tryInjectRefIntoJsonObject(content, key, unresolved);\n if (annotated != null) {\n return annotated;\n }\n }\n const prefix = hasRefKey ? `${buildReferencePrefix(key!)}\\n` : '';\n const trailer = hasUnresolved\n ? `\\n[unresolved refs: ${unresolved.join(', ')}]`\n : '';\n return `${prefix}${content}${trailer}`;\n}\n\nfunction tryInjectRefIntoJsonObject(\n content: string,\n key: string | undefined,\n unresolved: string[]\n): string | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n return null;\n }\n\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return null;\n }\n\n const obj = parsed as Record<string, unknown>;\n const injectingRef = key != null;\n const injectingUnresolved = unresolved.length > 0;\n\n /**\n * Reject the JSON-injection path (fall back to prefix form) when\n * either of our keys collides with real payload data:\n * - `_ref` collision: existing value is non-null and differs from\n * the key we're about to inject.\n * - `_unresolved_refs` collision: existing value is non-null and\n * is not a deep-equal match for the array we'd inject.\n * This keeps us from silently overwriting legitimate tool output.\n */\n if (\n injectingRef &&\n TOOL_OUTPUT_REF_KEY in obj &&\n obj[TOOL_OUTPUT_REF_KEY] !== key &&\n obj[TOOL_OUTPUT_REF_KEY] != null\n ) {\n return null;\n }\n if (\n injectingUnresolved &&\n TOOL_OUTPUT_UNRESOLVED_KEY in obj &&\n obj[TOOL_OUTPUT_UNRESOLVED_KEY] != null &&\n !arraysShallowEqual(obj[TOOL_OUTPUT_UNRESOLVED_KEY], unresolved)\n ) {\n return null;\n }\n\n /**\n * Only strip the framework-owned key we're actually injecting —\n * leave everything else (including a pre-existing `_ref` on the\n * unresolved-only path, or a pre-existing `_unresolved_refs` on a\n * plain-annotation path) untouched so we annotate rather than\n * mutate downstream payload data. Our injected keys land first in\n * the serialized JSON so the LLM sees them before the body.\n */\n const omitKeys = new Set<string>();\n if (injectingRef) omitKeys.add(TOOL_OUTPUT_REF_KEY);\n if (injectingUnresolved) omitKeys.add(TOOL_OUTPUT_UNRESOLVED_KEY);\n const rest: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (!omitKeys.has(k)) {\n rest[k] = v;\n }\n }\n const injected: Record<string, unknown> = {};\n if (injectingRef) {\n injected[TOOL_OUTPUT_REF_KEY] = key;\n }\n if (injectingUnresolved) {\n injected[TOOL_OUTPUT_UNRESOLVED_KEY] = unresolved;\n }\n Object.assign(injected, rest);\n\n const pretty = /^\\{\\s*\\n/.test(content);\n return pretty ? JSON.stringify(injected, null, 2) : JSON.stringify(injected);\n}\n\nfunction arraysShallowEqual(a: unknown, b: readonly string[]): boolean {\n if (!Array.isArray(a) || a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Lazy projection that, given a registry and a runId, returns a new\n * `messages` array where each `ToolMessage` carrying ref metadata is\n * projected into a transient copy with annotated content (when the ref\n * is live in the registry) and with the framework-owned `additional_\n * kwargs` keys (`_refKey`, `_refScope`, `_unresolvedRefs`) stripped\n * regardless of whether annotation applied. The original input array\n * and its messages are never mutated.\n *\n * Annotation is gated on registry presence: a stale `_refKey` from a\n * prior run (e.g. one that survived in persisted history) silently\n * no-ops on the *content* side. The strip-metadata side still runs so\n * stale framework keys never leak onto the wire under any custom or\n * future provider serializer that might transmit `additional_kwargs`.\n * `_unresolvedRefs` is always meaningful and is not gated.\n *\n * **Feature-disabled fast path:** when the host hasn't enabled the\n * tool-output-reference feature, the registry is `undefined` and this\n * function returns the input array reference-equal *without iterating\n * a single message*. The loop is exclusive to the feature-enabled\n * code path.\n */\nexport function annotateMessagesForLLM(\n messages: BaseMessage[],\n registry: ToolOutputReferenceRegistry | undefined,\n runId: string | undefined\n): BaseMessage[] {\n if (registry == null) return messages;\n\n /**\n * Lazy-allocate the output array so the common case (no ToolMessage\n * carries framework metadata) returns the input reference-equal with\n * zero allocations beyond the per-message predicate checks.\n */\n let out: BaseMessage[] | undefined;\n for (let i = 0; i < messages.length; i++) {\n const m = messages[i];\n if (m._getType() !== 'tool') continue;\n /**\n * `additional_kwargs` is untyped at the LangChain layer\n * (`Record<string, unknown>`), so persisted or client-supplied\n * ToolMessages can carry arbitrary shapes — including primitives\n * (a malformed serializer might write a string, or `null`).\n * Guard with a runtime object check before the `in` probes\n * because the `in` operator throws `TypeError` on primitives.\n * A single malformed message must never crash the provider call\n * path; skip its annotation/strip and continue.\n */\n const rawMeta = m.additional_kwargs as unknown;\n if (rawMeta == null || typeof rawMeta !== 'object') continue;\n const meta = rawMeta as Record<string, unknown>;\n const hasRefKey = '_refKey' in meta;\n const hasRefScope = '_refScope' in meta;\n const hasUnresolvedField = '_unresolvedRefs' in meta;\n if (!hasRefKey && !hasRefScope && !hasUnresolvedField) continue;\n\n const refKey = readRefKey(meta);\n const unresolved = readUnresolvedRefs(meta);\n\n /**\n * Prefer the message-stamped `_refScope` for the registry lookup.\n * For named runs it equals the current `runId`; for anonymous\n * invocations it carries the per-batch synthetic scope minted by\n * ToolNode (`\\0anon-<n>`), which `runId` from config cannot\n * recover. Falling back to `runId` keeps backward compatibility\n * with messages stamped before this field existed.\n */\n const lookupScope = readRefScope(meta) ?? runId;\n const liveRef =\n refKey != null && registry.has(lookupScope, refKey) ? refKey : undefined;\n const annotates = liveRef != null || unresolved.length > 0;\n\n const tm = m as ToolMessage;\n let nextContent: ToolMessage['content'] = tm.content;\n\n if (annotates && typeof tm.content === 'string') {\n nextContent = annotateToolOutputWithReference(\n tm.content,\n liveRef,\n unresolved\n );\n } else if (\n annotates &&\n Array.isArray(tm.content) &&\n unresolved.length > 0\n ) {\n const warningBlock = {\n type: 'text' as const,\n text: `[unresolved refs: ${unresolved.join(', ')}]`,\n };\n /**\n * `as unknown as ToolMessage['content']` is unavoidable here:\n * LangChain's content union (`MessageContentComplex[] |\n * DataContentBlock[] | string`) does not accept a freshly built\n * mixed array literal even though the structural shape is valid\n * at runtime. The double-cast is structurally safe — we\n * preserve every block from `tm.content` and prepend a single\n * `{ type: 'text', text }` block that all providers accept.\n */\n nextContent = [\n warningBlock,\n ...tm.content,\n ] as unknown as ToolMessage['content'];\n }\n\n /**\n * Project unconditionally: even when no annotation applies (stale\n * `_refKey` or non-annotatable content), `cloneToolMessageWithContent`\n * runs `stripFrameworkRefMetadata` on `additional_kwargs` so the\n * framework-owned keys never reach the wire.\n */\n out ??= messages.slice();\n out[i] = cloneToolMessageWithContent(tm, nextContent);\n }\n\n return out ?? messages;\n}\n\n/**\n * Reads `_refKey` defensively from untyped `additional_kwargs`. Returns\n * undefined for non-string values so a malformed field cannot poison\n * the registry lookup or downstream string operations.\n */\nfunction readRefKey(\n meta: Record<string, unknown> | undefined\n): string | undefined {\n const v = meta?._refKey;\n return typeof v === 'string' ? v : undefined;\n}\n\n/**\n * Reads `_refScope` defensively from untyped `additional_kwargs`.\n * Mirrors {@link readRefKey} — non-string scopes are dropped (the\n * caller falls back to the run-derived scope) rather than passed into\n * the registry as a malformed key.\n */\nfunction readRefScope(\n meta: Record<string, unknown> | undefined\n): string | undefined {\n const v = meta?._refScope;\n return typeof v === 'string' ? v : undefined;\n}\n\n/**\n * Reads `_unresolvedRefs` defensively from untyped `additional_kwargs`.\n * Returns an empty array for any non-array value, and filters out\n * non-string entries from a real array. Without this guard, a hydrated\n * ToolMessage carrying e.g. `_unresolvedRefs: 'tool0turn0'` would crash\n * `attemptInvoke` on the eventual `.length` / `.join(...)` call.\n */\nfunction readUnresolvedRefs(\n meta: Record<string, unknown> | undefined\n): string[] {\n const v = meta?._unresolvedRefs;\n if (!Array.isArray(v)) return [];\n const out: string[] = [];\n for (const item of v) {\n if (typeof item === 'string') out.push(item);\n }\n return out;\n}\n\n/**\n * Builds a fresh `ToolMessage` that mirrors `tm`'s identity fields with\n * the supplied `content`. Every `ToolMessage` field but `content` is\n * carried over so the projection is structurally identical to the\n * original from a LangChain serializer's perspective.\n *\n * `additional_kwargs` is rebuilt with the framework-owned ref keys\n * stripped. Defensive: LangChain's standard provider serializers do not\n * transmit `additional_kwargs` to provider HTTP APIs, but a custom\n * adapter or future LangChain change could. Stripping keeps the\n * implementation correct under any serializer behavior at the cost of a\n * shallow object spread per annotated message.\n */\nfunction cloneToolMessageWithContent(\n tm: ToolMessage,\n content: ToolMessage['content']\n): ToolMessage {\n return new ToolMessage({\n id: tm.id,\n name: tm.name,\n status: tm.status,\n artifact: tm.artifact,\n tool_call_id: tm.tool_call_id,\n response_metadata: tm.response_metadata,\n additional_kwargs: stripFrameworkRefMetadata(tm.additional_kwargs),\n content,\n });\n}\n\n/**\n * Returns a copy of `kwargs` with `_refKey`, `_refScope`, and\n * `_unresolvedRefs` removed. Returns the input reference-equal when\n * none of those keys are present so the no-strip path stays cheap;\n * returns `undefined` when stripping leaves the object empty so the\n * caller can drop the field entirely.\n */\nfunction stripFrameworkRefMetadata(\n kwargs: Record<string, unknown> | undefined\n): Record<string, unknown> | undefined {\n if (kwargs == null) return undefined;\n if (\n !('_refKey' in kwargs) &&\n !('_refScope' in kwargs) &&\n !('_unresolvedRefs' in kwargs)\n ) {\n return kwargs;\n }\n const { _refKey, _refScope, _unresolvedRefs, ...rest } = kwargs as Record<\n string,\n unknown\n > & {\n _refKey?: unknown;\n _refScope?: unknown;\n _unresolvedRefs?: unknown;\n };\n void _refKey;\n void _refScope;\n void _unresolvedRefs;\n return Object.keys(rest).length === 0 ? undefined : rest;\n}\n"],"names":["HARD_MAX_TOOL_RESULT_CHARS","HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE","calculateMaxTotalToolOutputSize","ToolMessage"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke.mjs","sources":["../../../src/llm/invoke.ts"],"sourcesContent":["import { concat } from '@langchain/core/utils/stream';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type * as t from '@/types';\nimport { manualToolStreamProviders } from '@/llm/providers';\nimport { annotateMessagesForLLM } from '@/tools/toolOutputReferences';\nimport { modifyDeltaProperties } from '@/messages';\nimport { ChatModelStreamHandler } from '@/stream';\nimport { GraphEvents, Providers } from '@/common';\nimport { initializeModel } from '@/llm/init';\n\n/**\n * Context passed to `attemptInvoke
|
|
1
|
+
{"version":3,"file":"invoke.mjs","sources":["../../../src/llm/invoke.ts"],"sourcesContent":["import { concat } from '@langchain/core/utils/stream';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';\nimport type * as t from '@/types';\nimport { manualToolStreamProviders } from '@/llm/providers';\nimport { annotateMessagesForLLM } from '@/tools/toolOutputReferences';\nimport { modifyDeltaProperties } from '@/messages';\nimport { ChatModelStreamHandler } from '@/stream';\nimport { GraphEvents, Providers } from '@/common';\nimport { initializeModel } from '@/llm/init';\n\n/**\n * Context passed to `attemptInvoke`. Matches the subset of Graph that\n * `ChatModelStreamHandler.handle` needs *plus* the explicit\n * `getOrCreateToolOutputRegistry()` accessor that `attemptInvoke`\n * itself calls to pull the run-scoped tool-output registry off the\n * graph and project each relevant ToolMessage into a transient\n * annotated copy before the provider call.\n *\n * The intersection is intentional: `Parameters<...>[3]` resolves\n * indirectly through the stream handler's signature (which returns\n * `StandardGraph` and already exposes the accessor since #117), but\n * stating it explicitly here surfaces the contract at the call site —\n * a developer reading `attemptInvoke` doesn't have to chase the\n * upstream handler's parameter list to discover that\n * `context?.getOrCreateToolOutputRegistry()` is a real thing. Single\n * optional chain only — the method itself is required on the\n * `StandardGraph` branch of the intersection, so the second `?.` is\n * unnecessary at the call site.\n *\n * `NonNullable<...>` strips `undefined` from the upstream parameter\n * type so the intersection doesn't collapse to `never` on the\n * undefined branch; callers express optionality via `context?:\n * InvokeContext` on the function signature instead.\n *\n * Callers without a registry (e.g. summarization) simply pass no\n * `context` and the transform safely no-ops.\n */\nexport type InvokeContext = NonNullable<\n Parameters<ChatModelStreamHandler['handle']>[3]\n> & {\n getOrCreateToolOutputRegistry?(): ToolOutputReferenceRegistry | undefined;\n};\n\n/**\n * Per-chunk callback for custom stream processing.\n * When provided, replaces the default `ChatModelStreamHandler`.\n */\nexport type OnChunk = (chunk: AIMessageChunk) => void | Promise<void>;\n\n/**\n * Invokes a chat model with the given messages, handling both streaming and\n * non-streaming paths.\n *\n * By default, stream chunks are processed through a `ChatModelStreamHandler`\n * that dispatches run steps (MESSAGE_CREATION, TOOL_CALLS) for the graph.\n * Pass an `onChunk` callback to override this with custom chunk processing\n * (e.g. summarization delta events).\n */\nexport async function attemptInvoke(\n {\n model,\n messages,\n provider,\n context,\n onChunk,\n }: {\n model: t.ChatModel;\n messages: BaseMessage[];\n provider: Providers;\n context?: InvokeContext;\n onChunk?: OnChunk;\n },\n config?: RunnableConfig\n): Promise<Partial<t.BaseGraphState>> {\n /**\n * Pull the run-scoped tool output registry off the graph (when one\n * exists) and project ToolMessages carrying ref metadata into a\n * transient annotated copy. The original `messages` array stays\n * untouched so the graph state never sees `[ref: …]` / `_ref`\n * payload.\n */\n const registry = context?.getOrCreateToolOutputRegistry();\n const runId = config?.configurable?.run_id as string | undefined;\n const messagesForProvider = annotateMessagesForLLM(messages, registry, runId);\n\n if (model.stream) {\n const stream = await model.stream(messagesForProvider, config);\n let finalChunk: AIMessageChunk | undefined;\n\n if (onChunk) {\n for await (const chunk of stream) {\n await onChunk(chunk);\n finalChunk = finalChunk ? concat(finalChunk, chunk) : chunk;\n }\n } else {\n const metadata = config?.metadata as Record<string, unknown> | undefined;\n const streamHandler = new ChatModelStreamHandler();\n for await (const chunk of stream) {\n await streamHandler.handle(\n GraphEvents.CHAT_MODEL_STREAM,\n { chunk },\n metadata,\n context\n );\n finalChunk = finalChunk ? concat(finalChunk, chunk) : chunk;\n }\n }\n\n if (manualToolStreamProviders.has(provider)) {\n finalChunk = modifyDeltaProperties(provider, finalChunk);\n }\n\n if ((finalChunk?.tool_calls?.length ?? 0) > 0) {\n finalChunk!.tool_calls = finalChunk!.tool_calls?.filter(\n (tool_call: ToolCall) => !!tool_call.name\n );\n }\n\n return { messages: [finalChunk as AIMessageChunk] };\n }\n\n const finalMessage = await model.invoke(messagesForProvider, config);\n if ((finalMessage.tool_calls?.length ?? 0) > 0) {\n finalMessage.tool_calls = finalMessage.tool_calls?.filter(\n (tool_call: ToolCall) => !!tool_call.name\n );\n }\n return { messages: [finalMessage] };\n}\n\n/**\n * Attempts each fallback provider in order until one succeeds.\n * Throws the last error if all fallbacks fail.\n */\nexport async function tryFallbackProviders({\n fallbacks,\n tools,\n messages,\n config,\n primaryError,\n context,\n onChunk,\n}: {\n fallbacks: Array<{ provider: Providers; clientOptions?: t.ClientOptions }>;\n tools?: t.GraphTools;\n messages: BaseMessage[];\n config?: RunnableConfig;\n primaryError: unknown;\n context?: InvokeContext;\n onChunk?: OnChunk;\n}): Promise<Partial<t.BaseGraphState> | undefined> {\n let lastError: unknown = primaryError;\n for (const fb of fallbacks) {\n try {\n const fbModel = initializeModel({\n provider: fb.provider,\n clientOptions: fb.clientOptions,\n tools,\n });\n const result = await attemptInvoke(\n {\n model: fbModel as t.ChatModel,\n messages,\n provider: fb.provider,\n context,\n onChunk,\n },\n config\n );\n return result;\n } catch (e) {\n lastError = e;\n continue;\n }\n }\n if (lastError !== undefined) {\n throw lastError;\n }\n return undefined;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -57,7 +57,9 @@ Referencing previous tool outputs:
|
|
|
57
57
|
- Every successful tool result is tagged with a reference key of the form \`tool<idx>turn<turn>\` (e.g., \`tool0turn0\`). The key appears either as a \`[ref: tool0turn0]\` prefix line or, when the output is a JSON object, as a \`_ref\` field on the object.
|
|
58
58
|
- To pipe a previous tool output into this tool, embed the placeholder \`{{tool<idx>turn<turn>}}\` literally anywhere in the \`command\` string (or any string arg). It will be substituted with the stored output verbatim before the command runs.
|
|
59
59
|
- The substituted value is the original output string (no \`[ref: …]\` prefix, no \`_ref\` key), so it is safe to pipe directly into \`jq\`, \`grep\`, \`awk\`, etc.
|
|
60
|
-
- Example: \`echo '{{tool0turn0}}' | jq '.foo'\` takes the full output of the first tool from the first turn and pipes it into jq.
|
|
60
|
+
- Example (simple ASCII output): \`echo '{{tool0turn0}}' | jq '.foo'\` takes the full output of the first tool from the first turn and pipes it into jq.
|
|
61
|
+
- For payloads that may contain quotes, parentheses, backticks, or arbitrary bytes (random/binary data, JSON with embedded quotes, multi-line strings), prefer a quoted-delimiter heredoc over \`echo '…'\`. The heredoc body is not interpreted by the shell, so substituted payloads pass through unchanged.
|
|
62
|
+
- Heredoc example: \`wc -c << 'EOF'\\n{{tool0turn0}}\\nEOF\` (the quotes around \`'EOF'\` disable interpolation inside the body).
|
|
61
63
|
- Unknown reference keys are left in place and surfaced as \`[unresolved refs: …]\` after the output.
|
|
62
64
|
`.trim();
|
|
63
65
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BashExecutor.mjs","sources":["../../../src/tools/BashExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { Constants } from '@/common';\n\nconfig();\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nconst baseEndpoint = getCodeBaseURL();\nconst EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\nexport const BashExecutionToolSchema = {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: `The bash command or script to execute.\n- The environment is stateless; variables and state don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- Use \\`echo\\`, \\`printf\\`, or \\`cat\\` for all outputs.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the command with. This should only be used if the input command requires additional arguments to run.',\n },\n },\n required: ['command'],\n} as const;\n\nexport const BashExecutionToolDescription = `\nRuns bash commands and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious commands.\n`.trim();\n\n/**\n * Supplemental prompt documenting the tool-output reference feature.\n *\n * Hosts should append this (separated by a blank line) to the base\n * {@link BashExecutionToolDescription} only when\n * `RunConfig.toolOutputReferences.enabled` is `true`. When the feature\n * is disabled, including this text would tell the LLM to emit\n * `{{tool0turn0}}` placeholders that pass through unsubstituted and\n * leak into the shell.\n */\nexport const BashToolOutputReferencesGuide = `\nReferencing previous tool outputs:\n- Every successful tool result is tagged with a reference key of the form \\`tool<idx>turn<turn>\\` (e.g., \\`tool0turn0\\`). The key appears either as a \\`[ref: tool0turn0]\\` prefix line or, when the output is a JSON object, as a \\`_ref\\` field on the object.\n- To pipe a previous tool output into this tool, embed the placeholder \\`{{tool<idx>turn<turn>}}\\` literally anywhere in the \\`command\\` string (or any string arg). It will be substituted with the stored output verbatim before the command runs.\n- The substituted value is the original output string (no \\`[ref: …]\\` prefix, no \\`_ref\\` key), so it is safe to pipe directly into \\`jq\\`, \\`grep\\`, \\`awk\\`, etc.\n- Example: \\`echo '{{tool0turn0}}' | jq '.foo'\\` takes the full output of the first tool from the first turn and pipes it into jq.\n- Unknown reference keys are left in place and surfaced as \\`[unresolved refs: …]\\` after the output.\n`.trim();\n\n/**\n * Composes the bash tool description, optionally appending the\n * tool-output references guide. Hosts that enable\n * `RunConfig.toolOutputReferences` should pass `enableToolOutputReferences: true`\n * when registering the tool so the LLM learns the `{{…}}` syntax it\n * will actually be able to use.\n */\nexport function buildBashExecutionToolDescription(options?: {\n enableToolOutputReferences?: boolean;\n}): string {\n if (options?.enableToolOutputReferences === true) {\n return `${BashExecutionToolDescription}\\n\\n${BashToolOutputReferencesGuide}`;\n }\n return BashExecutionToolDescription;\n}\n\nexport const BashExecutionToolName = Constants.BASH_TOOL;\n\n/**\n * Default bash tool definition using the base description.\n *\n * When `RunConfig.toolOutputReferences.enabled` is `true`, build a\n * reference-aware description with\n * {@link buildBashExecutionToolDescription}\n * (`{ enableToolOutputReferences: true }`) and construct a custom\n * definition using it — using this constant as-is leaves the LLM\n * unaware of the `{{tool<i>turn<n>}}` syntax.\n */\nexport const BashExecutionToolDefinition = {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n} as const;\n\nfunction createBashExecutionTool(\n params: t.BashExecutionToolParams = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n const { command, ...rest } = rawInput as {\n command: string;\n args?: string[];\n };\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang: 'bash',\n code: command,\n ...rest,\n ...params,\n };\n\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'LibreChat/1.0',\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error:\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createBashExecutionTool };\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BashExecutor.mjs","sources":["../../../src/tools/BashExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { Constants } from '@/common';\n\nconfig();\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nconst baseEndpoint = getCodeBaseURL();\nconst EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\nexport const BashExecutionToolSchema = {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: `The bash command or script to execute.\n- The environment is stateless; variables and state don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- Use \\`echo\\`, \\`printf\\`, or \\`cat\\` for all outputs.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the command with. This should only be used if the input command requires additional arguments to run.',\n },\n },\n required: ['command'],\n} as const;\n\nexport const BashExecutionToolDescription = `\nRuns bash commands and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious commands.\n`.trim();\n\n/**\n * Supplemental prompt documenting the tool-output reference feature.\n *\n * Hosts should append this (separated by a blank line) to the base\n * {@link BashExecutionToolDescription} only when\n * `RunConfig.toolOutputReferences.enabled` is `true`. When the feature\n * is disabled, including this text would tell the LLM to emit\n * `{{tool0turn0}}` placeholders that pass through unsubstituted and\n * leak into the shell.\n */\nexport const BashToolOutputReferencesGuide = `\nReferencing previous tool outputs:\n- Every successful tool result is tagged with a reference key of the form \\`tool<idx>turn<turn>\\` (e.g., \\`tool0turn0\\`). The key appears either as a \\`[ref: tool0turn0]\\` prefix line or, when the output is a JSON object, as a \\`_ref\\` field on the object.\n- To pipe a previous tool output into this tool, embed the placeholder \\`{{tool<idx>turn<turn>}}\\` literally anywhere in the \\`command\\` string (or any string arg). It will be substituted with the stored output verbatim before the command runs.\n- The substituted value is the original output string (no \\`[ref: …]\\` prefix, no \\`_ref\\` key), so it is safe to pipe directly into \\`jq\\`, \\`grep\\`, \\`awk\\`, etc.\n- Example (simple ASCII output): \\`echo '{{tool0turn0}}' | jq '.foo'\\` takes the full output of the first tool from the first turn and pipes it into jq.\n- For payloads that may contain quotes, parentheses, backticks, or arbitrary bytes (random/binary data, JSON with embedded quotes, multi-line strings), prefer a quoted-delimiter heredoc over \\`echo '…'\\`. The heredoc body is not interpreted by the shell, so substituted payloads pass through unchanged.\n- Heredoc example: \\`wc -c << 'EOF'\\\\n{{tool0turn0}}\\\\nEOF\\` (the quotes around \\`'EOF'\\` disable interpolation inside the body).\n- Unknown reference keys are left in place and surfaced as \\`[unresolved refs: …]\\` after the output.\n`.trim();\n\n/**\n * Composes the bash tool description, optionally appending the\n * tool-output references guide. Hosts that enable\n * `RunConfig.toolOutputReferences` should pass `enableToolOutputReferences: true`\n * when registering the tool so the LLM learns the `{{…}}` syntax it\n * will actually be able to use.\n */\nexport function buildBashExecutionToolDescription(options?: {\n enableToolOutputReferences?: boolean;\n}): string {\n if (options?.enableToolOutputReferences === true) {\n return `${BashExecutionToolDescription}\\n\\n${BashToolOutputReferencesGuide}`;\n }\n return BashExecutionToolDescription;\n}\n\nexport const BashExecutionToolName = Constants.BASH_TOOL;\n\n/**\n * Default bash tool definition using the base description.\n *\n * When `RunConfig.toolOutputReferences.enabled` is `true`, build a\n * reference-aware description with\n * {@link buildBashExecutionToolDescription}\n * (`{ enableToolOutputReferences: true }`) and construct a custom\n * definition using it — using this constant as-is leaves the LLM\n * unaware of the `{{tool<i>turn<n>}}` syntax.\n */\nexport const BashExecutionToolDefinition = {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n} as const;\n\nfunction createBashExecutionTool(\n params: t.BashExecutionToolParams = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n const { command, ...rest } = rawInput as {\n command: string;\n args?: string[];\n };\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang: 'bash',\n code: command,\n ...rest,\n ...params,\n };\n\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'LibreChat/1.0',\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error:\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createBashExecutionTool };\n"],"names":[],"mappings":"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"}
|
|
@@ -511,16 +511,20 @@ function annotateMessagesForLLM(messages, registry, runId) {
|
|
|
511
511
|
/**
|
|
512
512
|
* `additional_kwargs` is untyped at the LangChain layer
|
|
513
513
|
* (`Record<string, unknown>`), so persisted or client-supplied
|
|
514
|
-
* ToolMessages can carry arbitrary shapes
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
* provider call
|
|
514
|
+
* ToolMessages can carry arbitrary shapes — including primitives
|
|
515
|
+
* (a malformed serializer might write a string, or `null`).
|
|
516
|
+
* Guard with a runtime object check before the `in` probes
|
|
517
|
+
* because the `in` operator throws `TypeError` on primitives.
|
|
518
|
+
* A single malformed message must never crash the provider call
|
|
519
|
+
* path; skip its annotation/strip and continue.
|
|
519
520
|
*/
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const
|
|
521
|
+
const rawMeta = m.additional_kwargs;
|
|
522
|
+
if (rawMeta == null || typeof rawMeta !== 'object')
|
|
523
|
+
continue;
|
|
524
|
+
const meta = rawMeta;
|
|
525
|
+
const hasRefKey = '_refKey' in meta;
|
|
526
|
+
const hasRefScope = '_refScope' in meta;
|
|
527
|
+
const hasUnresolvedField = '_unresolvedRefs' in meta;
|
|
524
528
|
if (!hasRefKey && !hasRefScope && !hasUnresolvedField)
|
|
525
529
|
continue;
|
|
526
530
|
const refKey = readRefKey(meta);
|
|
@@ -548,6 +552,15 @@ function annotateMessagesForLLM(messages, registry, runId) {
|
|
|
548
552
|
type: 'text',
|
|
549
553
|
text: `[unresolved refs: ${unresolved.join(', ')}]`,
|
|
550
554
|
};
|
|
555
|
+
/**
|
|
556
|
+
* `as unknown as ToolMessage['content']` is unavoidable here:
|
|
557
|
+
* LangChain's content union (`MessageContentComplex[] |
|
|
558
|
+
* DataContentBlock[] | string`) does not accept a freshly built
|
|
559
|
+
* mixed array literal even though the structural shape is valid
|
|
560
|
+
* at runtime. The double-cast is structurally safe — we
|
|
561
|
+
* preserve every block from `tm.content` and prepend a single
|
|
562
|
+
* `{ type: 'text', text }` block that all providers accept.
|
|
563
|
+
*/
|
|
551
564
|
nextContent = [
|
|
552
565
|
warningBlock,
|
|
553
566
|
...tm.content,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolOutputReferences.mjs","sources":["../../../src/tools/toolOutputReferences.ts"],"sourcesContent":["/**\n * Tool output reference registry.\n *\n * When enabled via `RunConfig.toolOutputReferences.enabled`, ToolNode\n * stores each successful tool output under a stable key\n * (`tool<idx>turn<turn>`) where `idx` is the tool's position within a\n * ToolNode batch and `turn` is the batch index within the run\n * (incremented once per ToolNode invocation).\n *\n * Subsequent tool calls can pipe a previous output into their args by\n * embedding `{{tool<idx>turn<turn>}}` inside any string argument;\n * {@link ToolOutputReferenceRegistry.resolve} walks the args and\n * substitutes the placeholders immediately before invocation.\n *\n * The registry stores the *raw, untruncated* tool output so a later\n * `{{…}}` substitution pipes the full payload into the next tool —\n * even when the LLM only saw a head+tail-truncated preview in\n * `ToolMessage.content`. Outputs are stored without any annotation\n * (the `_ref` key or the `[ref: ...]` prefix seen by the LLM is\n * strictly a UX signal attached to `ToolMessage.content`). Keeping the\n * registry pristine means downstream bash/jq piping receives the\n * complete, verbatim output with no injected fields.\n */\n\nimport { ToolMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport {\n calculateMaxTotalToolOutputSize,\n HARD_MAX_TOOL_RESULT_CHARS,\n HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE,\n} from '@/utils/truncation';\n\n/**\n * Non-global matcher for a single `{{tool<i>turn<n>}}` placeholder.\n * Exported for consumers that want to detect references (e.g., syntax\n * highlighting, docs). The stateful `g` variant lives inside the\n * registry so nobody trips on `lastIndex`.\n */\nexport const TOOL_OUTPUT_REF_PATTERN = /\\{\\{(tool\\d+turn\\d+)\\}\\}/;\n\n/** Object key used when a parsed-object output has `_ref` injected. */\nexport const TOOL_OUTPUT_REF_KEY = '_ref';\n\n/**\n * Object key used to carry unresolved reference warnings on a parsed-\n * object output. Using a dedicated field instead of a trailing text\n * line keeps the annotated `ToolMessage.content` parseable as JSON for\n * downstream consumers that rely on the object shape.\n */\nexport const TOOL_OUTPUT_UNRESOLVED_KEY = '_unresolved_refs';\n\n/** Single-line prefix prepended to non-object tool outputs so the LLM sees the reference key. */\nexport function buildReferencePrefix(key: string): string {\n return `[ref: ${key}]`;\n}\n\n/** Stable registry key for a tool output. */\nexport function buildReferenceKey(toolIndex: number, turn: number): string {\n return `tool${toolIndex}turn${turn}`;\n}\n\nexport type ToolOutputReferenceRegistryOptions = {\n /** Maximum characters stored per registered output. */\n maxOutputSize?: number;\n /** Maximum total characters retained across all registered outputs. */\n maxTotalSize?: number;\n /**\n * Upper bound on the number of concurrently-tracked runs. When\n * exceeded, the oldest run bucket is evicted (FIFO). Defaults to 32.\n */\n maxActiveRuns?: number;\n};\n\n/**\n * Result of resolving placeholders in tool args.\n */\nexport type ResolveResult<T> = {\n /** Arguments with placeholders replaced. Same shape as the input. */\n resolved: T;\n /** Reference keys that were referenced but had no stored value. */\n unresolved: string[];\n};\n\n/**\n * Read-only view over a frozen registry snapshot. Returned by\n * {@link ToolOutputReferenceRegistry.snapshot} for callers that need\n * to resolve placeholders against the registry state at a specific\n * point in time, ignoring any subsequent registrations.\n */\nexport interface ToolOutputResolveView {\n resolve<T>(args: T): ResolveResult<T>;\n}\n\n/**\n * Pre-resolved arg map keyed by `toolCallId`. Used by the mixed\n * direct+event dispatch path to feed event calls' resolved args\n * (captured pre-batch) into the dispatcher without re-resolving\n * against the now-stale live registry.\n */\nexport type PreResolvedArgsMap = Map<\n string,\n { resolved: Record<string, unknown>; unresolved: string[] }\n>;\n\n/**\n * Per-call sink for resolved args, keyed by `toolCallId`. Threaded\n * as a per-batch local map so concurrent `ToolNode.run()` calls do\n * not race on shared sink state.\n */\nexport type ResolvedArgsByCallId = Map<string, Record<string, unknown>>;\n\nconst EMPTY_ENTRIES: ReadonlyMap<string, string> = new Map<string, string>();\n\n/**\n * Per-run state bucket held inside the registry. Each distinct\n * `run_id` gets its own bucket so overlapping concurrent runs on a\n * shared registry cannot leak outputs, turn counters, or warn-memos\n * into one another.\n */\nclass RunStateBucket {\n entries: Map<string, string> = new Map();\n totalSize: number = 0;\n turnCounter: number = 0;\n warnedNonStringTools: Set<string> = new Set();\n}\n\n/**\n * Anonymous (`run_id` absent) bucket key. Anonymous batches are\n * treated as fresh runs on every invocation — see `nextTurn`.\n */\nconst ANON_RUN_KEY = '\\0anon';\n\n/**\n * Default upper bound on the number of concurrently-tracked runs per\n * registry. When exceeded, the oldest run's bucket (by insertion\n * order) is evicted. Keeps memory bounded when a ToolNode is reused\n * across many runs without explicit `releaseRun` calls.\n */\nconst DEFAULT_MAX_ACTIVE_RUNS = 32;\n\n/**\n * Ordered map of reference-key → stored output, partitioned by run so\n * concurrent / interleaved runs sharing one registry cannot leak\n * outputs between each other.\n *\n * Each public method takes a `runId` which selects the run's bucket.\n * Hosts typically get one registry per run via `Graph`, in which\n * case only a single bucket is ever populated; the partitioning\n * exists so the registry also behaves correctly when a single\n * instance is reused directly.\n */\nexport class ToolOutputReferenceRegistry {\n private runStates: Map<string, RunStateBucket> = new Map();\n private readonly maxOutputSize: number;\n private readonly maxTotalSize: number;\n private readonly maxActiveRuns: number;\n /**\n * Local stateful matcher used only by `replaceInString`. Kept\n * off-module so callers of the exported `TOOL_OUTPUT_REF_PATTERN`\n * never see a stale `lastIndex`.\n */\n private static readonly PLACEHOLDER_MATCHER = /\\{\\{(tool\\d+turn\\d+)\\}\\}/g;\n\n constructor(options: ToolOutputReferenceRegistryOptions = {}) {\n /**\n * Per-output default is the same ~400 KB budget as the standard\n * tool-result truncation (`HARD_MAX_TOOL_RESULT_CHARS`). This\n * keeps a single `{{…}}` substitution at a size that is safe to\n * pass through typical shell `ARG_MAX` limits and matches what\n * the LLM would otherwise have seen. Hosts that want larger per-\n * output payloads (API consumers, long JSON streams) can raise\n * the cap explicitly up to the 5 MB total budget.\n */\n const perOutput =\n options.maxOutputSize != null && options.maxOutputSize > 0\n ? options.maxOutputSize\n : HARD_MAX_TOOL_RESULT_CHARS;\n /**\n * Clamp a caller-supplied `maxTotalSize` to\n * `HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE` (5 MB) so the documented\n * absolute cap is enforced regardless of host config —\n * `calculateMaxTotalToolOutputSize` already applies the same\n * upper bound on its computed default, but the user-provided\n * branch was bypassing it.\n */\n const totalRaw =\n options.maxTotalSize != null && options.maxTotalSize > 0\n ? Math.min(options.maxTotalSize, HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE)\n : calculateMaxTotalToolOutputSize(perOutput);\n this.maxTotalSize = totalRaw;\n /**\n * The per-output cap can never exceed the per-run aggregate cap:\n * if a single entry were allowed to be larger than `maxTotalSize`,\n * the eviction loop would either blow the cap (to keep the entry)\n * or self-evict a just-stored value. Clamping here turns\n * `maxTotalSize` into a hard upper bound on *any* state the\n * registry retains per run.\n */\n this.maxOutputSize = Math.min(perOutput, totalRaw);\n this.maxActiveRuns =\n options.maxActiveRuns != null && options.maxActiveRuns > 0\n ? options.maxActiveRuns\n : DEFAULT_MAX_ACTIVE_RUNS;\n }\n\n private keyFor(runId: string | undefined): string {\n return runId ?? ANON_RUN_KEY;\n }\n\n private getOrCreate(runId: string | undefined): RunStateBucket {\n const key = this.keyFor(runId);\n let state = this.runStates.get(key);\n if (state == null) {\n state = new RunStateBucket();\n this.runStates.set(key, state);\n if (this.runStates.size > this.maxActiveRuns) {\n const oldest = this.runStates.keys().next().value;\n if (oldest != null && oldest !== key) {\n this.runStates.delete(oldest);\n }\n }\n }\n return state;\n }\n\n /** Registers (or replaces) the output stored under `key` for `runId`. */\n set(runId: string | undefined, key: string, value: string): void {\n const bucket = this.getOrCreate(runId);\n const clipped =\n value.length > this.maxOutputSize\n ? value.slice(0, this.maxOutputSize)\n : value;\n const existing = bucket.entries.get(key);\n if (existing != null) {\n bucket.totalSize -= existing.length;\n bucket.entries.delete(key);\n }\n bucket.entries.set(key, clipped);\n bucket.totalSize += clipped.length;\n this.evictWithinBucket(bucket);\n }\n\n /** Returns the stored value for `key` in `runId`'s bucket, or `undefined`. */\n get(runId: string | undefined, key: string): string | undefined {\n return this.runStates.get(this.keyFor(runId))?.entries.get(key);\n }\n\n /**\n * Returns `true` when `key` is currently stored in `runId`'s bucket.\n * Used by {@link annotateMessagesForLLM} to gate transient annotation\n * on whether the registry still owns the referenced output (a stale\n * `_refKey` from a prior run silently no-ops here).\n */\n has(runId: string | undefined, key: string): boolean {\n return this.runStates.get(this.keyFor(runId))?.entries.has(key) ?? false;\n }\n\n /** Total number of registered outputs across every run bucket. */\n get size(): number {\n let n = 0;\n for (const bucket of this.runStates.values()) {\n n += bucket.entries.size;\n }\n return n;\n }\n\n /** Maximum characters retained per output (post-clip). */\n get perOutputLimit(): number {\n return this.maxOutputSize;\n }\n\n /** Maximum total characters retained *per run*. */\n get totalLimit(): number {\n return this.maxTotalSize;\n }\n\n /** Drops every run's state. */\n clear(): void {\n this.runStates.clear();\n }\n\n /**\n * Explicitly release `runId`'s state. Safe to call when a run has\n * finished. Hosts sharing one registry across runs should call this\n * to reclaim memory deterministically; otherwise LRU eviction kicks\n * in when `maxActiveRuns` runs accumulate.\n */\n releaseRun(runId: string | undefined): void {\n this.runStates.delete(this.keyFor(runId));\n }\n\n /**\n * Claims the next batch turn synchronously from `runId`'s bucket.\n *\n * Must be called once at the start of each ToolNode batch before\n * any `await`, so concurrent invocations within the same run see\n * distinct turn values (reads are effectively atomic by JS's\n * single-threaded execution of the sync prefix).\n *\n * If `runId` is missing the anonymous bucket is dropped and a\n * fresh one created so each anonymous call behaves as its own run.\n */\n nextTurn(runId: string | undefined): number {\n if (runId == null) {\n this.runStates.delete(ANON_RUN_KEY);\n }\n const bucket = this.getOrCreate(runId);\n return bucket.turnCounter++;\n }\n\n /**\n * Records that `toolName` has been warned about in `runId` (returns\n * `true` on the first call per run, `false` after). Used by\n * ToolNode to emit one log line per offending tool per run when a\n * `ToolMessage.content` isn't a string.\n */\n claimWarnOnce(runId: string | undefined, toolName: string): boolean {\n const bucket = this.getOrCreate(runId);\n if (bucket.warnedNonStringTools.has(toolName)) {\n return false;\n }\n bucket.warnedNonStringTools.add(toolName);\n return true;\n }\n\n /**\n * Walks `args` and replaces every `{{tool<i>turn<n>}}` placeholder in\n * string values with the stored output *from `runId`'s bucket*. Non-\n * string values and object keys are left untouched. Unresolved\n * references are left in-place and reported so the caller can\n * surface them to the LLM. When no placeholder appears anywhere in\n * the serialized args, the original input is returned without\n * walking the tree.\n */\n resolve<T>(runId: string | undefined, args: T): ResolveResult<T> {\n if (!hasAnyPlaceholder(args)) {\n return { resolved: args, unresolved: [] };\n }\n const bucket = this.runStates.get(this.keyFor(runId));\n return this.resolveAgainst(bucket?.entries ?? EMPTY_ENTRIES, args);\n }\n\n /**\n * Captures a frozen snapshot of `runId`'s current entries and\n * returns a view that resolves placeholders against *only* that\n * snapshot. The snapshot is decoupled from the live registry, so\n * subsequent `set()` calls (for example, same-turn direct outputs\n * registering while an event branch is still in flight) are\n * invisible to the snapshot's `resolve`. Used by the mixed\n * direct+event dispatch path to preserve same-turn isolation when\n * a `PreToolUse` hook rewrites event args after directs have\n * completed.\n */\n snapshot(runId: string | undefined): ToolOutputResolveView {\n const bucket = this.runStates.get(this.keyFor(runId));\n const entries: ReadonlyMap<string, string> = bucket\n ? new Map(bucket.entries)\n : EMPTY_ENTRIES;\n return {\n resolve: <T>(args: T): ResolveResult<T> =>\n this.resolveAgainst(entries, args),\n };\n }\n\n private resolveAgainst<T>(\n entries: ReadonlyMap<string, string>,\n args: T\n ): ResolveResult<T> {\n if (!hasAnyPlaceholder(args)) {\n return { resolved: args, unresolved: [] };\n }\n const unresolved = new Set<string>();\n const resolved = this.transform(entries, args, unresolved) as T;\n return { resolved, unresolved: Array.from(unresolved) };\n }\n\n private transform(\n entries: ReadonlyMap<string, string>,\n value: unknown,\n unresolved: Set<string>\n ): unknown {\n if (typeof value === 'string') {\n return this.replaceInString(entries, value, unresolved);\n }\n if (Array.isArray(value)) {\n return value.map((item) => this.transform(entries, item, unresolved));\n }\n if (value !== null && typeof value === 'object') {\n const source = value as Record<string, unknown>;\n const next: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(source)) {\n next[key] = this.transform(entries, item, unresolved);\n }\n return next;\n }\n return value;\n }\n\n private replaceInString(\n entries: ReadonlyMap<string, string>,\n input: string,\n unresolved: Set<string>\n ): string {\n if (input.indexOf('{{tool') === -1) {\n return input;\n }\n return input.replace(\n ToolOutputReferenceRegistry.PLACEHOLDER_MATCHER,\n (match, key: string) => {\n const stored = entries.get(key);\n if (stored == null) {\n unresolved.add(key);\n return match;\n }\n return stored;\n }\n );\n }\n\n private evictWithinBucket(bucket: RunStateBucket): void {\n if (bucket.totalSize <= this.maxTotalSize) {\n return;\n }\n for (const key of bucket.entries.keys()) {\n if (bucket.totalSize <= this.maxTotalSize) {\n return;\n }\n const entry = bucket.entries.get(key);\n if (entry == null) {\n continue;\n }\n bucket.totalSize -= entry.length;\n bucket.entries.delete(key);\n }\n }\n}\n\n/**\n * Cheap pre-check: returns true if any string value in `args` contains\n * the `{{tool` substring. Lets `resolve()` skip the deep tree walk (and\n * its object allocations) for the common case of plain args.\n */\nfunction hasAnyPlaceholder(value: unknown): boolean {\n if (typeof value === 'string') {\n return value.indexOf('{{tool') !== -1;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n if (hasAnyPlaceholder(item)) {\n return true;\n }\n }\n return false;\n }\n if (value !== null && typeof value === 'object') {\n for (const item of Object.values(value as Record<string, unknown>)) {\n if (hasAnyPlaceholder(item)) {\n return true;\n }\n }\n return false;\n }\n return false;\n}\n\n/**\n * Annotates `content` with a reference key and/or unresolved-ref\n * warnings so the LLM sees both alongside the tool output.\n *\n * Behavior:\n * - If `content` parses as a plain (non-array, non-null) JSON object\n * and the object does not already have a conflicting `_ref` key,\n * the reference key and (when present) `_unresolved_refs` array\n * are injected as object fields, preserving JSON validity for\n * downstream consumers that parse the output.\n * - Otherwise (string output, JSON array/primitive, parse failure,\n * or `_ref` collision), a `[ref: <key>]\\n` prefix line is\n * prepended and unresolved refs are appended as a trailing\n * `[unresolved refs: …]` line.\n *\n * The annotated string is what the LLM sees as `ToolMessage.content`.\n * The *original* (un-annotated) value is what gets stored in the\n * registry, so downstream piping remains pristine.\n *\n * @param content Raw (post-truncation) tool output.\n * @param key Reference key for this output, or undefined when\n * there is nothing to register (errors etc.).\n * @param unresolved Reference keys that failed to resolve during\n * argument substitution. Surfaced so the LLM can\n * self-correct its next tool call.\n */\nexport function annotateToolOutputWithReference(\n content: string,\n key: string | undefined,\n unresolved: string[] = []\n): string {\n const hasRefKey = key != null;\n const hasUnresolved = unresolved.length > 0;\n if (!hasRefKey && !hasUnresolved) {\n return content;\n }\n const trimmed = content.trimStart();\n if (trimmed.startsWith('{')) {\n const annotated = tryInjectRefIntoJsonObject(content, key, unresolved);\n if (annotated != null) {\n return annotated;\n }\n }\n const prefix = hasRefKey ? `${buildReferencePrefix(key!)}\\n` : '';\n const trailer = hasUnresolved\n ? `\\n[unresolved refs: ${unresolved.join(', ')}]`\n : '';\n return `${prefix}${content}${trailer}`;\n}\n\nfunction tryInjectRefIntoJsonObject(\n content: string,\n key: string | undefined,\n unresolved: string[]\n): string | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n return null;\n }\n\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return null;\n }\n\n const obj = parsed as Record<string, unknown>;\n const injectingRef = key != null;\n const injectingUnresolved = unresolved.length > 0;\n\n /**\n * Reject the JSON-injection path (fall back to prefix form) when\n * either of our keys collides with real payload data:\n * - `_ref` collision: existing value is non-null and differs from\n * the key we're about to inject.\n * - `_unresolved_refs` collision: existing value is non-null and\n * is not a deep-equal match for the array we'd inject.\n * This keeps us from silently overwriting legitimate tool output.\n */\n if (\n injectingRef &&\n TOOL_OUTPUT_REF_KEY in obj &&\n obj[TOOL_OUTPUT_REF_KEY] !== key &&\n obj[TOOL_OUTPUT_REF_KEY] != null\n ) {\n return null;\n }\n if (\n injectingUnresolved &&\n TOOL_OUTPUT_UNRESOLVED_KEY in obj &&\n obj[TOOL_OUTPUT_UNRESOLVED_KEY] != null &&\n !arraysShallowEqual(obj[TOOL_OUTPUT_UNRESOLVED_KEY], unresolved)\n ) {\n return null;\n }\n\n /**\n * Only strip the framework-owned key we're actually injecting —\n * leave everything else (including a pre-existing `_ref` on the\n * unresolved-only path, or a pre-existing `_unresolved_refs` on a\n * plain-annotation path) untouched so we annotate rather than\n * mutate downstream payload data. Our injected keys land first in\n * the serialized JSON so the LLM sees them before the body.\n */\n const omitKeys = new Set<string>();\n if (injectingRef) omitKeys.add(TOOL_OUTPUT_REF_KEY);\n if (injectingUnresolved) omitKeys.add(TOOL_OUTPUT_UNRESOLVED_KEY);\n const rest: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (!omitKeys.has(k)) {\n rest[k] = v;\n }\n }\n const injected: Record<string, unknown> = {};\n if (injectingRef) {\n injected[TOOL_OUTPUT_REF_KEY] = key;\n }\n if (injectingUnresolved) {\n injected[TOOL_OUTPUT_UNRESOLVED_KEY] = unresolved;\n }\n Object.assign(injected, rest);\n\n const pretty = /^\\{\\s*\\n/.test(content);\n return pretty ? JSON.stringify(injected, null, 2) : JSON.stringify(injected);\n}\n\nfunction arraysShallowEqual(a: unknown, b: readonly string[]): boolean {\n if (!Array.isArray(a) || a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Lazy projection that, given a registry and a runId, returns a new\n * `messages` array where each `ToolMessage` carrying ref metadata is\n * projected into a transient copy with annotated content (when the ref\n * is live in the registry) and with the framework-owned `additional_\n * kwargs` keys (`_refKey`, `_refScope`, `_unresolvedRefs`) stripped\n * regardless of whether annotation applied. The original input array\n * and its messages are never mutated.\n *\n * Annotation is gated on registry presence: a stale `_refKey` from a\n * prior run (e.g. one that survived in persisted history) silently\n * no-ops on the *content* side. The strip-metadata side still runs so\n * stale framework keys never leak onto the wire under any custom or\n * future provider serializer that might transmit `additional_kwargs`.\n * `_unresolvedRefs` is always meaningful and is not gated.\n *\n * **Feature-disabled fast path:** when the host hasn't enabled the\n * tool-output-reference feature, the registry is `undefined` and this\n * function returns the input array reference-equal *without iterating\n * a single message*. The loop is exclusive to the feature-enabled\n * code path.\n */\nexport function annotateMessagesForLLM(\n messages: BaseMessage[],\n registry: ToolOutputReferenceRegistry | undefined,\n runId: string | undefined\n): BaseMessage[] {\n if (registry == null) return messages;\n\n /**\n * Lazy-allocate the output array so the common case (no ToolMessage\n * carries framework metadata) returns the input reference-equal with\n * zero allocations beyond the per-message predicate checks.\n */\n let out: BaseMessage[] | undefined;\n for (let i = 0; i < messages.length; i++) {\n const m = messages[i];\n if (m._getType() !== 'tool') continue;\n /**\n * `additional_kwargs` is untyped at the LangChain layer\n * (`Record<string, unknown>`), so persisted or client-supplied\n * ToolMessages can carry arbitrary shapes under our framework\n * keys. Treat them as untrusted input and coerce defensively\n * before any array operation — a malformed field on a single\n * hydrated message must not crash `attemptInvoke` before the\n * provider call.\n */\n const meta = m.additional_kwargs as Record<string, unknown> | undefined;\n const hasRefKey = meta != null && '_refKey' in meta;\n const hasRefScope = meta != null && '_refScope' in meta;\n const hasUnresolvedField = meta != null && '_unresolvedRefs' in meta;\n if (!hasRefKey && !hasRefScope && !hasUnresolvedField) continue;\n\n const refKey = readRefKey(meta);\n const unresolved = readUnresolvedRefs(meta);\n\n /**\n * Prefer the message-stamped `_refScope` for the registry lookup.\n * For named runs it equals the current `runId`; for anonymous\n * invocations it carries the per-batch synthetic scope minted by\n * ToolNode (`\\0anon-<n>`), which `runId` from config cannot\n * recover. Falling back to `runId` keeps backward compatibility\n * with messages stamped before this field existed.\n */\n const lookupScope = readRefScope(meta) ?? runId;\n const liveRef =\n refKey != null && registry.has(lookupScope, refKey) ? refKey : undefined;\n const annotates = liveRef != null || unresolved.length > 0;\n\n const tm = m as ToolMessage;\n let nextContent: ToolMessage['content'] = tm.content;\n\n if (annotates && typeof tm.content === 'string') {\n nextContent = annotateToolOutputWithReference(\n tm.content,\n liveRef,\n unresolved\n );\n } else if (\n annotates &&\n Array.isArray(tm.content) &&\n unresolved.length > 0\n ) {\n const warningBlock = {\n type: 'text' as const,\n text: `[unresolved refs: ${unresolved.join(', ')}]`,\n };\n nextContent = [\n warningBlock,\n ...tm.content,\n ] as unknown as ToolMessage['content'];\n }\n\n /**\n * Project unconditionally: even when no annotation applies (stale\n * `_refKey` or non-annotatable content), `cloneToolMessageWithContent`\n * runs `stripFrameworkRefMetadata` on `additional_kwargs` so the\n * framework-owned keys never reach the wire.\n */\n out ??= messages.slice();\n out[i] = cloneToolMessageWithContent(tm, nextContent);\n }\n\n return out ?? messages;\n}\n\n/**\n * Reads `_refKey` defensively from untyped `additional_kwargs`. Returns\n * undefined for non-string values so a malformed field cannot poison\n * the registry lookup or downstream string operations.\n */\nfunction readRefKey(\n meta: Record<string, unknown> | undefined\n): string | undefined {\n const v = meta?._refKey;\n return typeof v === 'string' ? v : undefined;\n}\n\n/**\n * Reads `_refScope` defensively from untyped `additional_kwargs`.\n * Mirrors {@link readRefKey} — non-string scopes are dropped (the\n * caller falls back to the run-derived scope) rather than passed into\n * the registry as a malformed key.\n */\nfunction readRefScope(\n meta: Record<string, unknown> | undefined\n): string | undefined {\n const v = meta?._refScope;\n return typeof v === 'string' ? v : undefined;\n}\n\n/**\n * Reads `_unresolvedRefs` defensively from untyped `additional_kwargs`.\n * Returns an empty array for any non-array value, and filters out\n * non-string entries from a real array. Without this guard, a hydrated\n * ToolMessage carrying e.g. `_unresolvedRefs: 'tool0turn0'` would crash\n * `attemptInvoke` on the eventual `.length` / `.join(...)` call.\n */\nfunction readUnresolvedRefs(\n meta: Record<string, unknown> | undefined\n): string[] {\n const v = meta?._unresolvedRefs;\n if (!Array.isArray(v)) return [];\n const out: string[] = [];\n for (const item of v) {\n if (typeof item === 'string') out.push(item);\n }\n return out;\n}\n\n/**\n * Builds a fresh `ToolMessage` that mirrors `tm`'s identity fields with\n * the supplied `content`. Every `ToolMessage` field but `content` is\n * carried over so the projection is structurally identical to the\n * original from a LangChain serializer's perspective.\n *\n * `additional_kwargs` is rebuilt with the framework-owned ref keys\n * stripped. Defensive: LangChain's standard provider serializers do not\n * transmit `additional_kwargs` to provider HTTP APIs, but a custom\n * adapter or future LangChain change could. Stripping keeps the\n * implementation correct under any serializer behavior at the cost of a\n * shallow object spread per annotated message.\n */\nfunction cloneToolMessageWithContent(\n tm: ToolMessage,\n content: ToolMessage['content']\n): ToolMessage {\n return new ToolMessage({\n id: tm.id,\n name: tm.name,\n status: tm.status,\n artifact: tm.artifact,\n tool_call_id: tm.tool_call_id,\n response_metadata: tm.response_metadata,\n additional_kwargs: stripFrameworkRefMetadata(tm.additional_kwargs),\n content,\n });\n}\n\n/**\n * Returns a copy of `kwargs` with `_refKey`, `_refScope`, and\n * `_unresolvedRefs` removed. Returns the input reference-equal when\n * none of those keys are present so the no-strip path stays cheap;\n * returns `undefined` when stripping leaves the object empty so the\n * caller can drop the field entirely.\n */\nfunction stripFrameworkRefMetadata(\n kwargs: Record<string, unknown> | undefined\n): Record<string, unknown> | undefined {\n if (kwargs == null) return undefined;\n if (\n !('_refKey' in kwargs) &&\n !('_refScope' in kwargs) &&\n !('_unresolvedRefs' in kwargs)\n ) {\n return kwargs;\n }\n const { _refKey, _refScope, _unresolvedRefs, ...rest } = kwargs as Record<\n string,\n unknown\n > & {\n _refKey?: unknown;\n _refScope?: unknown;\n _unresolvedRefs?: unknown;\n };\n void _refKey;\n void _refScope;\n void _unresolvedRefs;\n return Object.keys(rest).length === 0 ? undefined : rest;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toolOutputReferences.mjs","sources":["../../../src/tools/toolOutputReferences.ts"],"sourcesContent":["/**\n * Tool output reference registry.\n *\n * When enabled via `RunConfig.toolOutputReferences.enabled`, ToolNode\n * stores each successful tool output under a stable key\n * (`tool<idx>turn<turn>`) where `idx` is the tool's position within a\n * ToolNode batch and `turn` is the batch index within the run\n * (incremented once per ToolNode invocation).\n *\n * Subsequent tool calls can pipe a previous output into their args by\n * embedding `{{tool<idx>turn<turn>}}` inside any string argument;\n * {@link ToolOutputReferenceRegistry.resolve} walks the args and\n * substitutes the placeholders immediately before invocation.\n *\n * The registry stores the *raw, untruncated* tool output so a later\n * `{{…}}` substitution pipes the full payload into the next tool —\n * even when the LLM only saw a head+tail-truncated preview in\n * `ToolMessage.content`. Outputs are stored without any annotation\n * (the `_ref` key or the `[ref: ...]` prefix seen by the LLM is\n * strictly a UX signal attached to `ToolMessage.content`). Keeping the\n * registry pristine means downstream bash/jq piping receives the\n * complete, verbatim output with no injected fields.\n */\n\nimport { ToolMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport {\n calculateMaxTotalToolOutputSize,\n HARD_MAX_TOOL_RESULT_CHARS,\n HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE,\n} from '@/utils/truncation';\n\n/**\n * Non-global matcher for a single `{{tool<i>turn<n>}}` placeholder.\n * Exported for consumers that want to detect references (e.g., syntax\n * highlighting, docs). The stateful `g` variant lives inside the\n * registry so nobody trips on `lastIndex`.\n */\nexport const TOOL_OUTPUT_REF_PATTERN = /\\{\\{(tool\\d+turn\\d+)\\}\\}/;\n\n/** Object key used when a parsed-object output has `_ref` injected. */\nexport const TOOL_OUTPUT_REF_KEY = '_ref';\n\n/**\n * Object key used to carry unresolved reference warnings on a parsed-\n * object output. Using a dedicated field instead of a trailing text\n * line keeps the annotated `ToolMessage.content` parseable as JSON for\n * downstream consumers that rely on the object shape.\n */\nexport const TOOL_OUTPUT_UNRESOLVED_KEY = '_unresolved_refs';\n\n/** Single-line prefix prepended to non-object tool outputs so the LLM sees the reference key. */\nexport function buildReferencePrefix(key: string): string {\n return `[ref: ${key}]`;\n}\n\n/** Stable registry key for a tool output. */\nexport function buildReferenceKey(toolIndex: number, turn: number): string {\n return `tool${toolIndex}turn${turn}`;\n}\n\nexport type ToolOutputReferenceRegistryOptions = {\n /** Maximum characters stored per registered output. */\n maxOutputSize?: number;\n /** Maximum total characters retained across all registered outputs. */\n maxTotalSize?: number;\n /**\n * Upper bound on the number of concurrently-tracked runs. When\n * exceeded, the oldest run bucket is evicted (FIFO). Defaults to 32.\n */\n maxActiveRuns?: number;\n};\n\n/**\n * Result of resolving placeholders in tool args.\n */\nexport type ResolveResult<T> = {\n /** Arguments with placeholders replaced. Same shape as the input. */\n resolved: T;\n /** Reference keys that were referenced but had no stored value. */\n unresolved: string[];\n};\n\n/**\n * Read-only view over a frozen registry snapshot. Returned by\n * {@link ToolOutputReferenceRegistry.snapshot} for callers that need\n * to resolve placeholders against the registry state at a specific\n * point in time, ignoring any subsequent registrations.\n */\nexport interface ToolOutputResolveView {\n resolve<T>(args: T): ResolveResult<T>;\n}\n\n/**\n * Pre-resolved arg map keyed by `toolCallId`. Used by the mixed\n * direct+event dispatch path to feed event calls' resolved args\n * (captured pre-batch) into the dispatcher without re-resolving\n * against the now-stale live registry.\n */\nexport type PreResolvedArgsMap = Map<\n string,\n { resolved: Record<string, unknown>; unresolved: string[] }\n>;\n\n/**\n * Per-call sink for resolved args, keyed by `toolCallId`. Threaded\n * as a per-batch local map so concurrent `ToolNode.run()` calls do\n * not race on shared sink state.\n */\nexport type ResolvedArgsByCallId = Map<string, Record<string, unknown>>;\n\nconst EMPTY_ENTRIES: ReadonlyMap<string, string> = new Map<string, string>();\n\n/**\n * Per-run state bucket held inside the registry. Each distinct\n * `run_id` gets its own bucket so overlapping concurrent runs on a\n * shared registry cannot leak outputs, turn counters, or warn-memos\n * into one another.\n */\nclass RunStateBucket {\n entries: Map<string, string> = new Map();\n totalSize: number = 0;\n turnCounter: number = 0;\n warnedNonStringTools: Set<string> = new Set();\n}\n\n/**\n * Anonymous (`run_id` absent) bucket key. Anonymous batches are\n * treated as fresh runs on every invocation — see `nextTurn`.\n */\nconst ANON_RUN_KEY = '\\0anon';\n\n/**\n * Default upper bound on the number of concurrently-tracked runs per\n * registry. When exceeded, the oldest run's bucket (by insertion\n * order) is evicted. Keeps memory bounded when a ToolNode is reused\n * across many runs without explicit `releaseRun` calls.\n */\nconst DEFAULT_MAX_ACTIVE_RUNS = 32;\n\n/**\n * Ordered map of reference-key → stored output, partitioned by run so\n * concurrent / interleaved runs sharing one registry cannot leak\n * outputs between each other.\n *\n * Each public method takes a `runId` which selects the run's bucket.\n * Hosts typically get one registry per run via `Graph`, in which\n * case only a single bucket is ever populated; the partitioning\n * exists so the registry also behaves correctly when a single\n * instance is reused directly.\n */\nexport class ToolOutputReferenceRegistry {\n private runStates: Map<string, RunStateBucket> = new Map();\n private readonly maxOutputSize: number;\n private readonly maxTotalSize: number;\n private readonly maxActiveRuns: number;\n /**\n * Local stateful matcher used only by `replaceInString`. Kept\n * off-module so callers of the exported `TOOL_OUTPUT_REF_PATTERN`\n * never see a stale `lastIndex`.\n */\n private static readonly PLACEHOLDER_MATCHER = /\\{\\{(tool\\d+turn\\d+)\\}\\}/g;\n\n constructor(options: ToolOutputReferenceRegistryOptions = {}) {\n /**\n * Per-output default is the same ~400 KB budget as the standard\n * tool-result truncation (`HARD_MAX_TOOL_RESULT_CHARS`). This\n * keeps a single `{{…}}` substitution at a size that is safe to\n * pass through typical shell `ARG_MAX` limits and matches what\n * the LLM would otherwise have seen. Hosts that want larger per-\n * output payloads (API consumers, long JSON streams) can raise\n * the cap explicitly up to the 5 MB total budget.\n */\n const perOutput =\n options.maxOutputSize != null && options.maxOutputSize > 0\n ? options.maxOutputSize\n : HARD_MAX_TOOL_RESULT_CHARS;\n /**\n * Clamp a caller-supplied `maxTotalSize` to\n * `HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE` (5 MB) so the documented\n * absolute cap is enforced regardless of host config —\n * `calculateMaxTotalToolOutputSize` already applies the same\n * upper bound on its computed default, but the user-provided\n * branch was bypassing it.\n */\n const totalRaw =\n options.maxTotalSize != null && options.maxTotalSize > 0\n ? Math.min(options.maxTotalSize, HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE)\n : calculateMaxTotalToolOutputSize(perOutput);\n this.maxTotalSize = totalRaw;\n /**\n * The per-output cap can never exceed the per-run aggregate cap:\n * if a single entry were allowed to be larger than `maxTotalSize`,\n * the eviction loop would either blow the cap (to keep the entry)\n * or self-evict a just-stored value. Clamping here turns\n * `maxTotalSize` into a hard upper bound on *any* state the\n * registry retains per run.\n */\n this.maxOutputSize = Math.min(perOutput, totalRaw);\n this.maxActiveRuns =\n options.maxActiveRuns != null && options.maxActiveRuns > 0\n ? options.maxActiveRuns\n : DEFAULT_MAX_ACTIVE_RUNS;\n }\n\n private keyFor(runId: string | undefined): string {\n return runId ?? ANON_RUN_KEY;\n }\n\n private getOrCreate(runId: string | undefined): RunStateBucket {\n const key = this.keyFor(runId);\n let state = this.runStates.get(key);\n if (state == null) {\n state = new RunStateBucket();\n this.runStates.set(key, state);\n if (this.runStates.size > this.maxActiveRuns) {\n const oldest = this.runStates.keys().next().value;\n if (oldest != null && oldest !== key) {\n this.runStates.delete(oldest);\n }\n }\n }\n return state;\n }\n\n /** Registers (or replaces) the output stored under `key` for `runId`. */\n set(runId: string | undefined, key: string, value: string): void {\n const bucket = this.getOrCreate(runId);\n const clipped =\n value.length > this.maxOutputSize\n ? value.slice(0, this.maxOutputSize)\n : value;\n const existing = bucket.entries.get(key);\n if (existing != null) {\n bucket.totalSize -= existing.length;\n bucket.entries.delete(key);\n }\n bucket.entries.set(key, clipped);\n bucket.totalSize += clipped.length;\n this.evictWithinBucket(bucket);\n }\n\n /** Returns the stored value for `key` in `runId`'s bucket, or `undefined`. */\n get(runId: string | undefined, key: string): string | undefined {\n return this.runStates.get(this.keyFor(runId))?.entries.get(key);\n }\n\n /**\n * Returns `true` when `key` is currently stored in `runId`'s bucket.\n * Used by {@link annotateMessagesForLLM} to gate transient annotation\n * on whether the registry still owns the referenced output (a stale\n * `_refKey` from a prior run silently no-ops here).\n */\n has(runId: string | undefined, key: string): boolean {\n return this.runStates.get(this.keyFor(runId))?.entries.has(key) ?? false;\n }\n\n /** Total number of registered outputs across every run bucket. */\n get size(): number {\n let n = 0;\n for (const bucket of this.runStates.values()) {\n n += bucket.entries.size;\n }\n return n;\n }\n\n /** Maximum characters retained per output (post-clip). */\n get perOutputLimit(): number {\n return this.maxOutputSize;\n }\n\n /** Maximum total characters retained *per run*. */\n get totalLimit(): number {\n return this.maxTotalSize;\n }\n\n /** Drops every run's state. */\n clear(): void {\n this.runStates.clear();\n }\n\n /**\n * Explicitly release `runId`'s state. Safe to call when a run has\n * finished. Hosts sharing one registry across runs should call this\n * to reclaim memory deterministically; otherwise LRU eviction kicks\n * in when `maxActiveRuns` runs accumulate.\n */\n releaseRun(runId: string | undefined): void {\n this.runStates.delete(this.keyFor(runId));\n }\n\n /**\n * Claims the next batch turn synchronously from `runId`'s bucket.\n *\n * Must be called once at the start of each ToolNode batch before\n * any `await`, so concurrent invocations within the same run see\n * distinct turn values (reads are effectively atomic by JS's\n * single-threaded execution of the sync prefix).\n *\n * If `runId` is missing the anonymous bucket is dropped and a\n * fresh one created so each anonymous call behaves as its own run.\n */\n nextTurn(runId: string | undefined): number {\n if (runId == null) {\n this.runStates.delete(ANON_RUN_KEY);\n }\n const bucket = this.getOrCreate(runId);\n return bucket.turnCounter++;\n }\n\n /**\n * Records that `toolName` has been warned about in `runId` (returns\n * `true` on the first call per run, `false` after). Used by\n * ToolNode to emit one log line per offending tool per run when a\n * `ToolMessage.content` isn't a string.\n */\n claimWarnOnce(runId: string | undefined, toolName: string): boolean {\n const bucket = this.getOrCreate(runId);\n if (bucket.warnedNonStringTools.has(toolName)) {\n return false;\n }\n bucket.warnedNonStringTools.add(toolName);\n return true;\n }\n\n /**\n * Walks `args` and replaces every `{{tool<i>turn<n>}}` placeholder in\n * string values with the stored output *from `runId`'s bucket*. Non-\n * string values and object keys are left untouched. Unresolved\n * references are left in-place and reported so the caller can\n * surface them to the LLM. When no placeholder appears anywhere in\n * the serialized args, the original input is returned without\n * walking the tree.\n */\n resolve<T>(runId: string | undefined, args: T): ResolveResult<T> {\n if (!hasAnyPlaceholder(args)) {\n return { resolved: args, unresolved: [] };\n }\n const bucket = this.runStates.get(this.keyFor(runId));\n return this.resolveAgainst(bucket?.entries ?? EMPTY_ENTRIES, args);\n }\n\n /**\n * Captures a frozen snapshot of `runId`'s current entries and\n * returns a view that resolves placeholders against *only* that\n * snapshot. The snapshot is decoupled from the live registry, so\n * subsequent `set()` calls (for example, same-turn direct outputs\n * registering while an event branch is still in flight) are\n * invisible to the snapshot's `resolve`. Used by the mixed\n * direct+event dispatch path to preserve same-turn isolation when\n * a `PreToolUse` hook rewrites event args after directs have\n * completed.\n */\n snapshot(runId: string | undefined): ToolOutputResolveView {\n const bucket = this.runStates.get(this.keyFor(runId));\n const entries: ReadonlyMap<string, string> = bucket\n ? new Map(bucket.entries)\n : EMPTY_ENTRIES;\n return {\n resolve: <T>(args: T): ResolveResult<T> =>\n this.resolveAgainst(entries, args),\n };\n }\n\n private resolveAgainst<T>(\n entries: ReadonlyMap<string, string>,\n args: T\n ): ResolveResult<T> {\n if (!hasAnyPlaceholder(args)) {\n return { resolved: args, unresolved: [] };\n }\n const unresolved = new Set<string>();\n const resolved = this.transform(entries, args, unresolved) as T;\n return { resolved, unresolved: Array.from(unresolved) };\n }\n\n private transform(\n entries: ReadonlyMap<string, string>,\n value: unknown,\n unresolved: Set<string>\n ): unknown {\n if (typeof value === 'string') {\n return this.replaceInString(entries, value, unresolved);\n }\n if (Array.isArray(value)) {\n return value.map((item) => this.transform(entries, item, unresolved));\n }\n if (value !== null && typeof value === 'object') {\n const source = value as Record<string, unknown>;\n const next: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(source)) {\n next[key] = this.transform(entries, item, unresolved);\n }\n return next;\n }\n return value;\n }\n\n private replaceInString(\n entries: ReadonlyMap<string, string>,\n input: string,\n unresolved: Set<string>\n ): string {\n if (input.indexOf('{{tool') === -1) {\n return input;\n }\n return input.replace(\n ToolOutputReferenceRegistry.PLACEHOLDER_MATCHER,\n (match, key: string) => {\n const stored = entries.get(key);\n if (stored == null) {\n unresolved.add(key);\n return match;\n }\n return stored;\n }\n );\n }\n\n private evictWithinBucket(bucket: RunStateBucket): void {\n if (bucket.totalSize <= this.maxTotalSize) {\n return;\n }\n for (const key of bucket.entries.keys()) {\n if (bucket.totalSize <= this.maxTotalSize) {\n return;\n }\n const entry = bucket.entries.get(key);\n if (entry == null) {\n continue;\n }\n bucket.totalSize -= entry.length;\n bucket.entries.delete(key);\n }\n }\n}\n\n/**\n * Cheap pre-check: returns true if any string value in `args` contains\n * the `{{tool` substring. Lets `resolve()` skip the deep tree walk (and\n * its object allocations) for the common case of plain args.\n */\nfunction hasAnyPlaceholder(value: unknown): boolean {\n if (typeof value === 'string') {\n return value.indexOf('{{tool') !== -1;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n if (hasAnyPlaceholder(item)) {\n return true;\n }\n }\n return false;\n }\n if (value !== null && typeof value === 'object') {\n for (const item of Object.values(value as Record<string, unknown>)) {\n if (hasAnyPlaceholder(item)) {\n return true;\n }\n }\n return false;\n }\n return false;\n}\n\n/**\n * Annotates `content` with a reference key and/or unresolved-ref\n * warnings so the LLM sees both alongside the tool output.\n *\n * Behavior:\n * - If `content` parses as a plain (non-array, non-null) JSON object\n * and the object does not already have a conflicting `_ref` key,\n * the reference key and (when present) `_unresolved_refs` array\n * are injected as object fields, preserving JSON validity for\n * downstream consumers that parse the output.\n * - Otherwise (string output, JSON array/primitive, parse failure,\n * or `_ref` collision), a `[ref: <key>]\\n` prefix line is\n * prepended and unresolved refs are appended as a trailing\n * `[unresolved refs: …]` line.\n *\n * The annotated string is what the LLM sees as `ToolMessage.content`.\n * The *original* (un-annotated) value is what gets stored in the\n * registry, so downstream piping remains pristine.\n *\n * @param content Raw (post-truncation) tool output.\n * @param key Reference key for this output, or undefined when\n * there is nothing to register (errors etc.).\n * @param unresolved Reference keys that failed to resolve during\n * argument substitution. Surfaced so the LLM can\n * self-correct its next tool call.\n */\nexport function annotateToolOutputWithReference(\n content: string,\n key: string | undefined,\n unresolved: string[] = []\n): string {\n const hasRefKey = key != null;\n const hasUnresolved = unresolved.length > 0;\n if (!hasRefKey && !hasUnresolved) {\n return content;\n }\n const trimmed = content.trimStart();\n if (trimmed.startsWith('{')) {\n const annotated = tryInjectRefIntoJsonObject(content, key, unresolved);\n if (annotated != null) {\n return annotated;\n }\n }\n const prefix = hasRefKey ? `${buildReferencePrefix(key!)}\\n` : '';\n const trailer = hasUnresolved\n ? `\\n[unresolved refs: ${unresolved.join(', ')}]`\n : '';\n return `${prefix}${content}${trailer}`;\n}\n\nfunction tryInjectRefIntoJsonObject(\n content: string,\n key: string | undefined,\n unresolved: string[]\n): string | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n return null;\n }\n\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return null;\n }\n\n const obj = parsed as Record<string, unknown>;\n const injectingRef = key != null;\n const injectingUnresolved = unresolved.length > 0;\n\n /**\n * Reject the JSON-injection path (fall back to prefix form) when\n * either of our keys collides with real payload data:\n * - `_ref` collision: existing value is non-null and differs from\n * the key we're about to inject.\n * - `_unresolved_refs` collision: existing value is non-null and\n * is not a deep-equal match for the array we'd inject.\n * This keeps us from silently overwriting legitimate tool output.\n */\n if (\n injectingRef &&\n TOOL_OUTPUT_REF_KEY in obj &&\n obj[TOOL_OUTPUT_REF_KEY] !== key &&\n obj[TOOL_OUTPUT_REF_KEY] != null\n ) {\n return null;\n }\n if (\n injectingUnresolved &&\n TOOL_OUTPUT_UNRESOLVED_KEY in obj &&\n obj[TOOL_OUTPUT_UNRESOLVED_KEY] != null &&\n !arraysShallowEqual(obj[TOOL_OUTPUT_UNRESOLVED_KEY], unresolved)\n ) {\n return null;\n }\n\n /**\n * Only strip the framework-owned key we're actually injecting —\n * leave everything else (including a pre-existing `_ref` on the\n * unresolved-only path, or a pre-existing `_unresolved_refs` on a\n * plain-annotation path) untouched so we annotate rather than\n * mutate downstream payload data. Our injected keys land first in\n * the serialized JSON so the LLM sees them before the body.\n */\n const omitKeys = new Set<string>();\n if (injectingRef) omitKeys.add(TOOL_OUTPUT_REF_KEY);\n if (injectingUnresolved) omitKeys.add(TOOL_OUTPUT_UNRESOLVED_KEY);\n const rest: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (!omitKeys.has(k)) {\n rest[k] = v;\n }\n }\n const injected: Record<string, unknown> = {};\n if (injectingRef) {\n injected[TOOL_OUTPUT_REF_KEY] = key;\n }\n if (injectingUnresolved) {\n injected[TOOL_OUTPUT_UNRESOLVED_KEY] = unresolved;\n }\n Object.assign(injected, rest);\n\n const pretty = /^\\{\\s*\\n/.test(content);\n return pretty ? JSON.stringify(injected, null, 2) : JSON.stringify(injected);\n}\n\nfunction arraysShallowEqual(a: unknown, b: readonly string[]): boolean {\n if (!Array.isArray(a) || a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Lazy projection that, given a registry and a runId, returns a new\n * `messages` array where each `ToolMessage` carrying ref metadata is\n * projected into a transient copy with annotated content (when the ref\n * is live in the registry) and with the framework-owned `additional_\n * kwargs` keys (`_refKey`, `_refScope`, `_unresolvedRefs`) stripped\n * regardless of whether annotation applied. The original input array\n * and its messages are never mutated.\n *\n * Annotation is gated on registry presence: a stale `_refKey` from a\n * prior run (e.g. one that survived in persisted history) silently\n * no-ops on the *content* side. The strip-metadata side still runs so\n * stale framework keys never leak onto the wire under any custom or\n * future provider serializer that might transmit `additional_kwargs`.\n * `_unresolvedRefs` is always meaningful and is not gated.\n *\n * **Feature-disabled fast path:** when the host hasn't enabled the\n * tool-output-reference feature, the registry is `undefined` and this\n * function returns the input array reference-equal *without iterating\n * a single message*. The loop is exclusive to the feature-enabled\n * code path.\n */\nexport function annotateMessagesForLLM(\n messages: BaseMessage[],\n registry: ToolOutputReferenceRegistry | undefined,\n runId: string | undefined\n): BaseMessage[] {\n if (registry == null) return messages;\n\n /**\n * Lazy-allocate the output array so the common case (no ToolMessage\n * carries framework metadata) returns the input reference-equal with\n * zero allocations beyond the per-message predicate checks.\n */\n let out: BaseMessage[] | undefined;\n for (let i = 0; i < messages.length; i++) {\n const m = messages[i];\n if (m._getType() !== 'tool') continue;\n /**\n * `additional_kwargs` is untyped at the LangChain layer\n * (`Record<string, unknown>`), so persisted or client-supplied\n * ToolMessages can carry arbitrary shapes — including primitives\n * (a malformed serializer might write a string, or `null`).\n * Guard with a runtime object check before the `in` probes\n * because the `in` operator throws `TypeError` on primitives.\n * A single malformed message must never crash the provider call\n * path; skip its annotation/strip and continue.\n */\n const rawMeta = m.additional_kwargs as unknown;\n if (rawMeta == null || typeof rawMeta !== 'object') continue;\n const meta = rawMeta as Record<string, unknown>;\n const hasRefKey = '_refKey' in meta;\n const hasRefScope = '_refScope' in meta;\n const hasUnresolvedField = '_unresolvedRefs' in meta;\n if (!hasRefKey && !hasRefScope && !hasUnresolvedField) continue;\n\n const refKey = readRefKey(meta);\n const unresolved = readUnresolvedRefs(meta);\n\n /**\n * Prefer the message-stamped `_refScope` for the registry lookup.\n * For named runs it equals the current `runId`; for anonymous\n * invocations it carries the per-batch synthetic scope minted by\n * ToolNode (`\\0anon-<n>`), which `runId` from config cannot\n * recover. Falling back to `runId` keeps backward compatibility\n * with messages stamped before this field existed.\n */\n const lookupScope = readRefScope(meta) ?? runId;\n const liveRef =\n refKey != null && registry.has(lookupScope, refKey) ? refKey : undefined;\n const annotates = liveRef != null || unresolved.length > 0;\n\n const tm = m as ToolMessage;\n let nextContent: ToolMessage['content'] = tm.content;\n\n if (annotates && typeof tm.content === 'string') {\n nextContent = annotateToolOutputWithReference(\n tm.content,\n liveRef,\n unresolved\n );\n } else if (\n annotates &&\n Array.isArray(tm.content) &&\n unresolved.length > 0\n ) {\n const warningBlock = {\n type: 'text' as const,\n text: `[unresolved refs: ${unresolved.join(', ')}]`,\n };\n /**\n * `as unknown as ToolMessage['content']` is unavoidable here:\n * LangChain's content union (`MessageContentComplex[] |\n * DataContentBlock[] | string`) does not accept a freshly built\n * mixed array literal even though the structural shape is valid\n * at runtime. The double-cast is structurally safe — we\n * preserve every block from `tm.content` and prepend a single\n * `{ type: 'text', text }` block that all providers accept.\n */\n nextContent = [\n warningBlock,\n ...tm.content,\n ] as unknown as ToolMessage['content'];\n }\n\n /**\n * Project unconditionally: even when no annotation applies (stale\n * `_refKey` or non-annotatable content), `cloneToolMessageWithContent`\n * runs `stripFrameworkRefMetadata` on `additional_kwargs` so the\n * framework-owned keys never reach the wire.\n */\n out ??= messages.slice();\n out[i] = cloneToolMessageWithContent(tm, nextContent);\n }\n\n return out ?? messages;\n}\n\n/**\n * Reads `_refKey` defensively from untyped `additional_kwargs`. Returns\n * undefined for non-string values so a malformed field cannot poison\n * the registry lookup or downstream string operations.\n */\nfunction readRefKey(\n meta: Record<string, unknown> | undefined\n): string | undefined {\n const v = meta?._refKey;\n return typeof v === 'string' ? v : undefined;\n}\n\n/**\n * Reads `_refScope` defensively from untyped `additional_kwargs`.\n * Mirrors {@link readRefKey} — non-string scopes are dropped (the\n * caller falls back to the run-derived scope) rather than passed into\n * the registry as a malformed key.\n */\nfunction readRefScope(\n meta: Record<string, unknown> | undefined\n): string | undefined {\n const v = meta?._refScope;\n return typeof v === 'string' ? v : undefined;\n}\n\n/**\n * Reads `_unresolvedRefs` defensively from untyped `additional_kwargs`.\n * Returns an empty array for any non-array value, and filters out\n * non-string entries from a real array. Without this guard, a hydrated\n * ToolMessage carrying e.g. `_unresolvedRefs: 'tool0turn0'` would crash\n * `attemptInvoke` on the eventual `.length` / `.join(...)` call.\n */\nfunction readUnresolvedRefs(\n meta: Record<string, unknown> | undefined\n): string[] {\n const v = meta?._unresolvedRefs;\n if (!Array.isArray(v)) return [];\n const out: string[] = [];\n for (const item of v) {\n if (typeof item === 'string') out.push(item);\n }\n return out;\n}\n\n/**\n * Builds a fresh `ToolMessage` that mirrors `tm`'s identity fields with\n * the supplied `content`. Every `ToolMessage` field but `content` is\n * carried over so the projection is structurally identical to the\n * original from a LangChain serializer's perspective.\n *\n * `additional_kwargs` is rebuilt with the framework-owned ref keys\n * stripped. Defensive: LangChain's standard provider serializers do not\n * transmit `additional_kwargs` to provider HTTP APIs, but a custom\n * adapter or future LangChain change could. Stripping keeps the\n * implementation correct under any serializer behavior at the cost of a\n * shallow object spread per annotated message.\n */\nfunction cloneToolMessageWithContent(\n tm: ToolMessage,\n content: ToolMessage['content']\n): ToolMessage {\n return new ToolMessage({\n id: tm.id,\n name: tm.name,\n status: tm.status,\n artifact: tm.artifact,\n tool_call_id: tm.tool_call_id,\n response_metadata: tm.response_metadata,\n additional_kwargs: stripFrameworkRefMetadata(tm.additional_kwargs),\n content,\n });\n}\n\n/**\n * Returns a copy of `kwargs` with `_refKey`, `_refScope`, and\n * `_unresolvedRefs` removed. Returns the input reference-equal when\n * none of those keys are present so the no-strip path stays cheap;\n * returns `undefined` when stripping leaves the object empty so the\n * caller can drop the field entirely.\n */\nfunction stripFrameworkRefMetadata(\n kwargs: Record<string, unknown> | undefined\n): Record<string, unknown> | undefined {\n if (kwargs == null) return undefined;\n if (\n !('_refKey' in kwargs) &&\n !('_refScope' in kwargs) &&\n !('_unresolvedRefs' in kwargs)\n ) {\n return kwargs;\n }\n const { _refKey, _refScope, _unresolvedRefs, ...rest } = kwargs as Record<\n string,\n unknown\n > & {\n _refKey?: unknown;\n _refScope?: unknown;\n _unresolvedRefs?: unknown;\n };\n void _refKey;\n void _refScope;\n void _unresolvedRefs;\n return Object.keys(rest).length === 0 ? undefined : rest;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,40 @@
|
|
|
1
1
|
import { AIMessageChunk } from '@langchain/core/messages';
|
|
2
2
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
3
3
|
import type { BaseMessage } from '@langchain/core/messages';
|
|
4
|
+
import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
|
|
4
5
|
import type * as t from '@/types';
|
|
5
6
|
import { ChatModelStreamHandler } from '@/stream';
|
|
6
7
|
import { Providers } from '@/common';
|
|
7
8
|
/**
|
|
8
|
-
* Context passed to `attemptInvoke
|
|
9
|
-
*
|
|
9
|
+
* Context passed to `attemptInvoke`. Matches the subset of Graph that
|
|
10
|
+
* `ChatModelStreamHandler.handle` needs *plus* the explicit
|
|
11
|
+
* `getOrCreateToolOutputRegistry()` accessor that `attemptInvoke`
|
|
12
|
+
* itself calls to pull the run-scoped tool-output registry off the
|
|
13
|
+
* graph and project each relevant ToolMessage into a transient
|
|
14
|
+
* annotated copy before the provider call.
|
|
10
15
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
+
* The intersection is intentional: `Parameters<...>[3]` resolves
|
|
17
|
+
* indirectly through the stream handler's signature (which returns
|
|
18
|
+
* `StandardGraph` and already exposes the accessor since #117), but
|
|
19
|
+
* stating it explicitly here surfaces the contract at the call site —
|
|
20
|
+
* a developer reading `attemptInvoke` doesn't have to chase the
|
|
21
|
+
* upstream handler's parameter list to discover that
|
|
22
|
+
* `context?.getOrCreateToolOutputRegistry()` is a real thing. Single
|
|
23
|
+
* optional chain only — the method itself is required on the
|
|
24
|
+
* `StandardGraph` branch of the intersection, so the second `?.` is
|
|
25
|
+
* unnecessary at the call site.
|
|
26
|
+
*
|
|
27
|
+
* `NonNullable<...>` strips `undefined` from the upstream parameter
|
|
28
|
+
* type so the intersection doesn't collapse to `never` on the
|
|
29
|
+
* undefined branch; callers express optionality via `context?:
|
|
30
|
+
* InvokeContext` on the function signature instead.
|
|
16
31
|
*
|
|
17
32
|
* Callers without a registry (e.g. summarization) simply pass no
|
|
18
33
|
* `context` and the transform safely no-ops.
|
|
19
34
|
*/
|
|
20
|
-
export type InvokeContext = Parameters<ChatModelStreamHandler['handle']>[3]
|
|
35
|
+
export type InvokeContext = NonNullable<Parameters<ChatModelStreamHandler['handle']>[3]> & {
|
|
36
|
+
getOrCreateToolOutputRegistry?(): ToolOutputReferenceRegistry | undefined;
|
|
37
|
+
};
|
|
21
38
|
/**
|
|
22
39
|
* Per-chunk callback for custom stream processing.
|
|
23
40
|
* When provided, replaces the default `ChatModelStreamHandler`.
|
package/package.json
CHANGED
package/src/llm/invoke.test.ts
CHANGED
|
@@ -28,10 +28,17 @@ type StubModel = {
|
|
|
28
28
|
) => AsyncGenerator<AIMessageChunk>;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
type CapturingModel = {
|
|
32
32
|
invokeMessages: BaseMessage[][];
|
|
33
33
|
model: StubModel;
|
|
34
|
-
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type StreamingCapturingModel = {
|
|
37
|
+
streamMessages: BaseMessage[][];
|
|
38
|
+
model: StubModel;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function buildCapturingModel(): CapturingModel {
|
|
35
42
|
const invokeMessages: BaseMessage[][] = [];
|
|
36
43
|
const responseMsg = new AIMessage({ content: 'ok' });
|
|
37
44
|
const model: StubModel = {
|
|
@@ -43,10 +50,7 @@ function buildCapturingModel(): {
|
|
|
43
50
|
return { invokeMessages, model };
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
function buildStreamingCapturingModel(): {
|
|
47
|
-
streamMessages: BaseMessage[][];
|
|
48
|
-
model: StubModel;
|
|
49
|
-
} {
|
|
53
|
+
function buildStreamingCapturingModel(): StreamingCapturingModel {
|
|
50
54
|
const streamMessages: BaseMessage[][] = [];
|
|
51
55
|
const model: StubModel = {
|
|
52
56
|
stream: jest.fn(async function* (
|
package/src/llm/invoke.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { AIMessageChunk } from '@langchain/core/messages';
|
|
|
3
3
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
4
4
|
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
5
5
|
import type { BaseMessage } from '@langchain/core/messages';
|
|
6
|
+
import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
|
|
6
7
|
import type * as t from '@/types';
|
|
7
8
|
import { manualToolStreamProviders } from '@/llm/providers';
|
|
8
9
|
import { annotateMessagesForLLM } from '@/tools/toolOutputReferences';
|
|
@@ -12,19 +13,37 @@ import { GraphEvents, Providers } from '@/common';
|
|
|
12
13
|
import { initializeModel } from '@/llm/init';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
|
-
* Context passed to `attemptInvoke
|
|
16
|
-
*
|
|
16
|
+
* Context passed to `attemptInvoke`. Matches the subset of Graph that
|
|
17
|
+
* `ChatModelStreamHandler.handle` needs *plus* the explicit
|
|
18
|
+
* `getOrCreateToolOutputRegistry()` accessor that `attemptInvoke`
|
|
19
|
+
* itself calls to pull the run-scoped tool-output registry off the
|
|
20
|
+
* graph and project each relevant ToolMessage into a transient
|
|
21
|
+
* annotated copy before the provider call.
|
|
17
22
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
+
* The intersection is intentional: `Parameters<...>[3]` resolves
|
|
24
|
+
* indirectly through the stream handler's signature (which returns
|
|
25
|
+
* `StandardGraph` and already exposes the accessor since #117), but
|
|
26
|
+
* stating it explicitly here surfaces the contract at the call site —
|
|
27
|
+
* a developer reading `attemptInvoke` doesn't have to chase the
|
|
28
|
+
* upstream handler's parameter list to discover that
|
|
29
|
+
* `context?.getOrCreateToolOutputRegistry()` is a real thing. Single
|
|
30
|
+
* optional chain only — the method itself is required on the
|
|
31
|
+
* `StandardGraph` branch of the intersection, so the second `?.` is
|
|
32
|
+
* unnecessary at the call site.
|
|
33
|
+
*
|
|
34
|
+
* `NonNullable<...>` strips `undefined` from the upstream parameter
|
|
35
|
+
* type so the intersection doesn't collapse to `never` on the
|
|
36
|
+
* undefined branch; callers express optionality via `context?:
|
|
37
|
+
* InvokeContext` on the function signature instead.
|
|
23
38
|
*
|
|
24
39
|
* Callers without a registry (e.g. summarization) simply pass no
|
|
25
40
|
* `context` and the transform safely no-ops.
|
|
26
41
|
*/
|
|
27
|
-
export type InvokeContext =
|
|
42
|
+
export type InvokeContext = NonNullable<
|
|
43
|
+
Parameters<ChatModelStreamHandler['handle']>[3]
|
|
44
|
+
> & {
|
|
45
|
+
getOrCreateToolOutputRegistry?(): ToolOutputReferenceRegistry | undefined;
|
|
46
|
+
};
|
|
28
47
|
|
|
29
48
|
/**
|
|
30
49
|
* Per-chunk callback for custom stream processing.
|
|
@@ -66,7 +66,9 @@ Referencing previous tool outputs:
|
|
|
66
66
|
- Every successful tool result is tagged with a reference key of the form \`tool<idx>turn<turn>\` (e.g., \`tool0turn0\`). The key appears either as a \`[ref: tool0turn0]\` prefix line or, when the output is a JSON object, as a \`_ref\` field on the object.
|
|
67
67
|
- To pipe a previous tool output into this tool, embed the placeholder \`{{tool<idx>turn<turn>}}\` literally anywhere in the \`command\` string (or any string arg). It will be substituted with the stored output verbatim before the command runs.
|
|
68
68
|
- The substituted value is the original output string (no \`[ref: …]\` prefix, no \`_ref\` key), so it is safe to pipe directly into \`jq\`, \`grep\`, \`awk\`, etc.
|
|
69
|
-
- Example: \`echo '{{tool0turn0}}' | jq '.foo'\` takes the full output of the first tool from the first turn and pipes it into jq.
|
|
69
|
+
- Example (simple ASCII output): \`echo '{{tool0turn0}}' | jq '.foo'\` takes the full output of the first tool from the first turn and pipes it into jq.
|
|
70
|
+
- For payloads that may contain quotes, parentheses, backticks, or arbitrary bytes (random/binary data, JSON with embedded quotes, multi-line strings), prefer a quoted-delimiter heredoc over \`echo '…'\`. The heredoc body is not interpreted by the shell, so substituted payloads pass through unchanged.
|
|
71
|
+
- Heredoc example: \`wc -c << 'EOF'\\n{{tool0turn0}}\\nEOF\` (the quotes around \`'EOF'\` disable interpolation inside the body).
|
|
70
72
|
- Unknown reference keys are left in place and surfaced as \`[unresolved refs: …]\` after the output.
|
|
71
73
|
`.trim();
|
|
72
74
|
|
|
@@ -27,6 +27,19 @@ describe('buildBashExecutionToolDescription', () => {
|
|
|
27
27
|
expect(composed).toContain('{{tool<idx>turn<turn>}}');
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
it('nudges the model toward heredoc when payloads may contain shell metacharacters', () => {
|
|
31
|
+
/**
|
|
32
|
+
* Real-world failure observed against ClickHouse + bash piping:
|
|
33
|
+
* the model emitted `echo '{{ref}}' | wc -c` and the substituted
|
|
34
|
+
* binary payload contained literal single quotes, breaking the
|
|
35
|
+
* shell. The model self-corrected to a heredoc on retry. Surface
|
|
36
|
+
* the heredoc pattern upfront so the round-trip isn't burned to
|
|
37
|
+
* rediscover it.
|
|
38
|
+
*/
|
|
39
|
+
expect(BashToolOutputReferencesGuide).toContain('heredoc');
|
|
40
|
+
expect(BashToolOutputReferencesGuide).toContain('<< \'EOF\'');
|
|
41
|
+
});
|
|
42
|
+
|
|
30
43
|
it('separates base and guide with a blank line', () => {
|
|
31
44
|
const composed = buildBashExecutionToolDescription({
|
|
32
45
|
enableToolOutputReferences: true,
|
|
@@ -12,12 +12,14 @@ function makeToolMessage(fields: {
|
|
|
12
12
|
name?: string;
|
|
13
13
|
tool_call_id?: string;
|
|
14
14
|
status?: 'success' | 'error';
|
|
15
|
+
artifact?: unknown;
|
|
15
16
|
additional_kwargs?: Record<string, unknown>;
|
|
16
17
|
}): ToolMessage {
|
|
17
18
|
return new ToolMessage({
|
|
18
19
|
name: fields.name ?? 'echo',
|
|
19
20
|
tool_call_id: fields.tool_call_id ?? 'tc1',
|
|
20
21
|
status: fields.status ?? 'success',
|
|
22
|
+
artifact: fields.artifact,
|
|
21
23
|
additional_kwargs: fields.additional_kwargs,
|
|
22
24
|
content: fields.content,
|
|
23
25
|
});
|
|
@@ -190,6 +192,31 @@ describe('annotateMessagesForLLM', () => {
|
|
|
190
192
|
expect(blocks[2].type).toBe('image_url');
|
|
191
193
|
});
|
|
192
194
|
|
|
195
|
+
it('preserves artifact on the projected ToolMessage', () => {
|
|
196
|
+
/**
|
|
197
|
+
* Hosts attach `artifact` to ToolMessages via the
|
|
198
|
+
* `content_and_artifact` response format (e.g. code execution
|
|
199
|
+
* sessions, MCP tools that return structured side-data). The
|
|
200
|
+
* projection must round-trip the artifact untouched so downstream
|
|
201
|
+
* consumers (audit logs, code-session tracking) keep working.
|
|
202
|
+
*/
|
|
203
|
+
const registry = new ToolOutputReferenceRegistry();
|
|
204
|
+
registry.set('r1', 'tool0turn0', 'raw');
|
|
205
|
+
const artifact = {
|
|
206
|
+
session_id: 'abc',
|
|
207
|
+
files: [{ id: 'f1', name: 'a.txt' }],
|
|
208
|
+
};
|
|
209
|
+
const tm = makeToolMessage({
|
|
210
|
+
content: 'output',
|
|
211
|
+
artifact,
|
|
212
|
+
additional_kwargs: { _refKey: 'tool0turn0' },
|
|
213
|
+
});
|
|
214
|
+
const out = annotateMessagesForLLM([tm], registry, 'r1');
|
|
215
|
+
const projected = out[0] as ToolMessage;
|
|
216
|
+
expect(projected.artifact).toBe(artifact);
|
|
217
|
+
expect(projected.content).toBe('[ref: tool0turn0]\noutput');
|
|
218
|
+
});
|
|
219
|
+
|
|
193
220
|
it('does not mutate the original ToolMessage instance or its content', () => {
|
|
194
221
|
const registry = new ToolOutputReferenceRegistry();
|
|
195
222
|
registry.set('r1', 'tool0turn0', 'raw');
|
|
@@ -404,6 +431,39 @@ describe('annotateMessagesForLLM', () => {
|
|
|
404
431
|
expect((out[0] as ToolMessage).additional_kwargs._refKey).toBeUndefined();
|
|
405
432
|
});
|
|
406
433
|
|
|
434
|
+
it('skips a ToolMessage whose additional_kwargs is a primitive without throwing', () => {
|
|
435
|
+
/**
|
|
436
|
+
* The `in` operator throws `TypeError` on primitives, so without
|
|
437
|
+
* a runtime object guard, a hydrated ToolMessage carrying e.g.
|
|
438
|
+
* `additional_kwargs: 'not-an-object'` (from a buggy serializer)
|
|
439
|
+
* would crash `attemptInvoke` before the provider call. Verify
|
|
440
|
+
* we skip that message and process subsequent live-ref messages
|
|
441
|
+
* normally.
|
|
442
|
+
*/
|
|
443
|
+
const registry = new ToolOutputReferenceRegistry();
|
|
444
|
+
registry.set('r1', 'tool0turn0', 'raw');
|
|
445
|
+
|
|
446
|
+
const malformed = new ToolMessage({
|
|
447
|
+
name: 'echo',
|
|
448
|
+
tool_call_id: 'mal',
|
|
449
|
+
status: 'success',
|
|
450
|
+
content: 'malformed-output',
|
|
451
|
+
});
|
|
452
|
+
/* Force a primitive past LangChain's typed setter via a cast. */
|
|
453
|
+
(malformed as unknown as { additional_kwargs: unknown }).additional_kwargs =
|
|
454
|
+
'not-an-object' as unknown;
|
|
455
|
+
|
|
456
|
+
const live = makeToolMessage({
|
|
457
|
+
content: 'live-output',
|
|
458
|
+
additional_kwargs: { _refKey: 'tool0turn0' },
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const out = annotateMessagesForLLM([malformed, live], registry, 'r1');
|
|
462
|
+
/* Malformed message passes through unchanged; live ref still annotates. */
|
|
463
|
+
expect(out[0]).toBe(malformed);
|
|
464
|
+
expect(out[1].content).toBe('[ref: tool0turn0]\nlive-output');
|
|
465
|
+
});
|
|
466
|
+
|
|
407
467
|
it('treats stale _refKey but live unresolved as unresolved-only', () => {
|
|
408
468
|
const registry = new ToolOutputReferenceRegistry();
|
|
409
469
|
const tm = makeToolMessage({
|
|
@@ -642,16 +642,19 @@ export function annotateMessagesForLLM(
|
|
|
642
642
|
/**
|
|
643
643
|
* `additional_kwargs` is untyped at the LangChain layer
|
|
644
644
|
* (`Record<string, unknown>`), so persisted or client-supplied
|
|
645
|
-
* ToolMessages can carry arbitrary shapes
|
|
646
|
-
*
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
* provider call
|
|
645
|
+
* ToolMessages can carry arbitrary shapes — including primitives
|
|
646
|
+
* (a malformed serializer might write a string, or `null`).
|
|
647
|
+
* Guard with a runtime object check before the `in` probes
|
|
648
|
+
* because the `in` operator throws `TypeError` on primitives.
|
|
649
|
+
* A single malformed message must never crash the provider call
|
|
650
|
+
* path; skip its annotation/strip and continue.
|
|
650
651
|
*/
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
const
|
|
654
|
-
const
|
|
652
|
+
const rawMeta = m.additional_kwargs as unknown;
|
|
653
|
+
if (rawMeta == null || typeof rawMeta !== 'object') continue;
|
|
654
|
+
const meta = rawMeta as Record<string, unknown>;
|
|
655
|
+
const hasRefKey = '_refKey' in meta;
|
|
656
|
+
const hasRefScope = '_refScope' in meta;
|
|
657
|
+
const hasUnresolvedField = '_unresolvedRefs' in meta;
|
|
655
658
|
if (!hasRefKey && !hasRefScope && !hasUnresolvedField) continue;
|
|
656
659
|
|
|
657
660
|
const refKey = readRefKey(meta);
|
|
@@ -688,6 +691,15 @@ export function annotateMessagesForLLM(
|
|
|
688
691
|
type: 'text' as const,
|
|
689
692
|
text: `[unresolved refs: ${unresolved.join(', ')}]`,
|
|
690
693
|
};
|
|
694
|
+
/**
|
|
695
|
+
* `as unknown as ToolMessage['content']` is unavoidable here:
|
|
696
|
+
* LangChain's content union (`MessageContentComplex[] |
|
|
697
|
+
* DataContentBlock[] | string`) does not accept a freshly built
|
|
698
|
+
* mixed array literal even though the structural shape is valid
|
|
699
|
+
* at runtime. The double-cast is structurally safe — we
|
|
700
|
+
* preserve every block from `tm.content` and prepend a single
|
|
701
|
+
* `{ type: 'text', text }` block that all providers accept.
|
|
702
|
+
*/
|
|
691
703
|
nextContent = [
|
|
692
704
|
warningBlock,
|
|
693
705
|
...tm.content,
|