@librechat/agents 3.2.65 → 3.2.66

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.
Files changed (56) hide show
  1. package/dist/cjs/graphs/Graph.cjs +15 -2
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/instrumentation.cjs +15 -3
  4. package/dist/cjs/instrumentation.cjs.map +1 -1
  5. package/dist/cjs/langfuseToolOutputTracing.cjs +1 -2
  6. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  7. package/dist/cjs/langfuseTraceShaping.cjs +51 -24
  8. package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
  9. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +8 -0
  10. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  11. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +8 -0
  12. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  13. package/dist/cjs/tools/BashExecutor.cjs +9 -8
  14. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  15. package/dist/cjs/tools/CodeExecutor.cjs +9 -7
  16. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  17. package/dist/esm/graphs/Graph.mjs +15 -2
  18. package/dist/esm/graphs/Graph.mjs.map +1 -1
  19. package/dist/esm/instrumentation.mjs +15 -3
  20. package/dist/esm/instrumentation.mjs.map +1 -1
  21. package/dist/esm/langfuseToolOutputTracing.mjs +1 -2
  22. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  23. package/dist/esm/langfuseTraceShaping.mjs +51 -24
  24. package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
  25. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +8 -0
  26. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  27. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +8 -0
  28. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  29. package/dist/esm/tools/BashExecutor.mjs +9 -8
  30. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  31. package/dist/esm/tools/CodeExecutor.mjs +9 -7
  32. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  33. package/dist/types/graphs/Graph.d.ts +2 -0
  34. package/dist/types/langfuseTraceShaping.d.ts +12 -6
  35. package/dist/types/tools/BashExecutor.d.ts +7 -6
  36. package/dist/types/tools/CodeExecutor.d.ts +7 -5
  37. package/dist/types/types/graph.d.ts +10 -3
  38. package/package.json +1 -1
  39. package/src/graphs/Graph.ts +21 -3
  40. package/src/instrumentation.ts +20 -0
  41. package/src/langfuseToolOutputTracing.ts +2 -4
  42. package/src/langfuseTraceShaping.ts +73 -20
  43. package/src/llm/anthropic/utils/cross-provider-server-tools.test.ts +110 -0
  44. package/src/llm/anthropic/utils/message_inputs.ts +15 -0
  45. package/src/llm/bedrock/utils/cross-provider-server-tools.test.ts +122 -0
  46. package/src/llm/bedrock/utils/message_inputs.ts +13 -0
  47. package/src/specs/langfuse-instrumentation.test.ts +64 -0
  48. package/src/specs/langfuse-routing.integration.test.ts +49 -4
  49. package/src/specs/langfuse-tool-output-tracing.test.ts +5 -4
  50. package/src/specs/langfuse-trace-shaping.test.ts +80 -9
  51. package/src/specs/subagent.test.ts +180 -0
  52. package/src/tools/BashExecutor.ts +9 -8
  53. package/src/tools/CodeExecutor.ts +9 -7
  54. package/src/tools/__tests__/BashExecutor.test.ts +16 -5
  55. package/src/tools/__tests__/CodeExecutor.stateful.test.ts +17 -6
  56. package/src/types/graph.ts +10 -3
@@ -1 +1 @@
1
- {"version":3,"file":"BashExecutor.cjs","names":["getCodeBaseURL","CODE_ARTIFACT_PATH_GUIDANCE","BASH_SHELL_GUIDANCE","resolveCodeApiAuthHeaders","HttpsProxyAgent","buildCodeApiHttpErrorMessage","emptyOutputMessage","appendTmpScratchReminder","appendCodeSessionFileSummary","appendFailedExecutionFileReminder"],"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 {\n BASH_SHELL_GUIDANCE,\n CODE_ARTIFACT_PATH_GUIDANCE,\n appendFailedExecutionFileReminder,\n appendTmpScratchReminder,\n appendCodeSessionFileSummary,\n emptyOutputMessage,\n buildCodeApiHttpErrorMessage,\n getCodeBaseURL,\n resolveCodeApiAuthHeaders,\n} from './CodeExecutor';\nimport { Constants } from '@/common';\n\nconfig();\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- Prior /mnt/data files are available and can be modified in place.\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\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- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\n- NEVER use this tool to execute malicious commands.\n`.trim();\n\n/**\n * Bash statefulness is filesystem-tier: on a warm session the machine (files\n * including /tmp, installed packages, background processes) persists between\n * calls, but each call may start a fresh shell — so shell variables and cwd\n * are NOT reliable, and the machine can be reset at any time. Only /mnt/data\n * is durable.\n */\nexport const STATEFUL_BASH_NOTE =\n 'Session state (best-effort): commands in this conversation usually run on the same machine, so files (including /tmp), installed packages, and running background processes from earlier calls typically persist. Each call may still start a fresh shell — do not rely on shell variables or the working directory carrying over — and the machine may be reset at any time. Only /mnt/data is durable.';\n\nexport const StatefulBashExecutionToolDescription = `\nRuns bash commands and returns stdout/stderr output from a session-based execution environment, similar to a long-running machine.\n\n${STATEFUL_BASH_NOTE}\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\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 statefulSessions?: boolean;\n}): string {\n const base =\n options?.statefulSessions === true\n ? StatefulBashExecutionToolDescription\n : BashExecutionToolDescription;\n if (options?.enableToolOutputReferences === true) {\n return `${base}\\n\\n${BashToolOutputReferencesGuide}`;\n }\n return base;\n}\n\nconst STATELESS_BASH_PARAM_NOTE =\n 'The environment is stateless; variables and state don\\'t persist between executions.';\nconst STATEFUL_BASH_PARAM_NOTE =\n 'Files, installed packages, and background processes usually persist between calls, but each call may start a fresh shell (do not rely on shell variables or cwd) and the machine may reset. Only /mnt/data is durable.';\n\nexport function buildBashExecutionToolSchema(opts?: {\n statefulSessions?: boolean;\n}): typeof BashExecutionToolSchema {\n const note =\n opts?.statefulSessions === true\n ? STATEFUL_BASH_PARAM_NOTE\n : STATELESS_BASH_PARAM_NOTE;\n const commandDescription =\n BashExecutionToolSchema.properties.command.description.replace(\n STATELESS_BASH_PARAM_NOTE,\n note\n );\n return {\n ...BashExecutionToolSchema,\n properties: {\n ...BashExecutionToolSchema.properties,\n command: {\n ...BashExecutionToolSchema.properties.command,\n description: commandDescription,\n },\n },\n } as typeof BashExecutionToolSchema;\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 | null = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n /* `statefulSessions` is prompt-only — keep it out of the wire body. */\n const {\n authHeaders,\n statefulSessions: _statefulSessions,\n ...executionParams\n } = params ?? {};\n void _statefulSessions;\n /* Drop any model-supplied `runtime_session_hint` from the raw args: the\n * hint must only come from ToolNode's injected `_runtime_session_hint`\n * (below), never from the tool call itself. */\n const {\n command,\n runtime_session_hint: _ignoredModelHint,\n ...rest\n } = rawInput as {\n command: string;\n runtime_session_hint?: unknown;\n args?: string[];\n };\n void _ignoredModelHint;\n const { session_id, _injected_files, _runtime_session_hint } =\n (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n _runtime_session_hint?: string;\n };\n\n const postData: Record<string, unknown> = {\n lang: 'bash',\n code: command,\n ...rest,\n ...executionParams,\n };\n\n if (\n typeof _runtime_session_hint === 'string' &&\n _runtime_session_hint !== ''\n ) {\n postData.runtime_session_hint = _runtime_session_hint;\n }\n\n /* See `CodeExecutor.ts` for the rationale — `/files/<session_id>`\n * HTTP fallback was removed because codeapi's sessionAuth requires\n * kind/id query params unavailable at this point. */\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (\n session_id != null &&\n session_id.length > 0 &&\n !Array.isArray(postData.files)\n ) {\n // eslint-disable-next-line no-console\n console.debug(\n `[BashExecutor] No injected files for session_id=${session_id} — exec will run without input files`\n );\n }\n\n try {\n const resolvedAuthHeaders =\n await resolveCodeApiAuthHeaders(authHeaders);\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n ...resolvedAuthHeaders,\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(\n await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)\n );\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\n const outputWithReminder = appendTmpScratchReminder(\n formattedOutput,\n command\n );\n const hasFiles = result.files != null && result.files.length > 0;\n const runtimeEcho =\n result.runtime_session_id != null\n ? {\n runtime_session_id: result.runtime_session_id,\n runtime_status: result.runtime_status,\n }\n : {};\n return [\n appendCodeSessionFileSummary(outputWithReminder, result.files),\n (hasFiles\n ? {\n session_id: result.session_id,\n files: result.files,\n ...runtimeEcho,\n }\n : {\n session_id: result.session_id,\n ...runtimeEcho,\n }) satisfies t.CodeExecutionArtifact,\n ];\n } catch (error) {\n const messageWithReminder = appendFailedExecutionFileReminder(\n (error as Error | undefined)?.message ?? '',\n command\n );\n throw new Error(`Execution error:\\n\\n${messageWithReminder}`);\n }\n },\n {\n name: BashExecutionToolName,\n description: buildBashExecutionToolDescription({\n statefulSessions: params?.statefulSessions,\n }),\n schema: buildBashExecutionToolSchema(params ?? undefined),\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createBashExecutionTool };\n"],"mappings":"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"}
1
+ {"version":3,"file":"BashExecutor.cjs","names":["getCodeBaseURL","CODE_ARTIFACT_PATH_GUIDANCE","BASH_SHELL_GUIDANCE","resolveCodeApiAuthHeaders","HttpsProxyAgent","buildCodeApiHttpErrorMessage","emptyOutputMessage","appendTmpScratchReminder","appendCodeSessionFileSummary","appendFailedExecutionFileReminder"],"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 {\n BASH_SHELL_GUIDANCE,\n CODE_ARTIFACT_PATH_GUIDANCE,\n appendFailedExecutionFileReminder,\n appendTmpScratchReminder,\n appendCodeSessionFileSummary,\n emptyOutputMessage,\n buildCodeApiHttpErrorMessage,\n getCodeBaseURL,\n resolveCodeApiAuthHeaders,\n} from './CodeExecutor';\nimport { Constants } from '@/common';\n\nconfig();\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- Prior /mnt/data files are available and can be modified in place.\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\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- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\n- NEVER use this tool to execute malicious commands.\n`.trim();\n\n/**\n * Bash statefulness is filesystem-tier and scoped to `/mnt/data`. The machine\n * is warm across calls, but each call runs in a fresh sandbox (new process\n * tree + private /tmp), so background processes are reaped when the call ends\n * and anything written outside /mnt/data is discarded. The note must not\n * promise otherwise: a model told background processes survive will start a\n * server in one call and assume it is listening in the next.\n */\nexport const STATEFUL_BASH_NOTE =\n 'Session state: commands in this conversation run on the same warm machine, so files written to /mnt/data persist between calls. Each call runs in a fresh, isolated sandbox: shell variables, the working directory, /tmp, and background processes do NOT survive after the call returns — a process started in one call is terminated when that call ends. Only /mnt/data is durable (the machine itself may also be reset at any time).';\n\nexport const StatefulBashExecutionToolDescription = `\nRuns bash commands and returns stdout/stderr output. Commands in this conversation share one warm machine with a persistent /mnt/data, but each command runs in its own isolated sandbox (not a persistent shell session).\n\n${STATEFUL_BASH_NOTE}\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\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 statefulSessions?: boolean;\n}): string {\n const base =\n options?.statefulSessions === true\n ? StatefulBashExecutionToolDescription\n : BashExecutionToolDescription;\n if (options?.enableToolOutputReferences === true) {\n return `${base}\\n\\n${BashToolOutputReferencesGuide}`;\n }\n return base;\n}\n\nconst STATELESS_BASH_PARAM_NOTE =\n 'The environment is stateless; variables and state don\\'t persist between executions.';\nconst STATEFUL_BASH_PARAM_NOTE =\n 'Files written to /mnt/data persist between calls on the same warm machine. Each call runs in a fresh sandbox: shell variables, cwd, /tmp, and background processes do NOT survive the call. Only /mnt/data is durable.';\n\nexport function buildBashExecutionToolSchema(opts?: {\n statefulSessions?: boolean;\n}): typeof BashExecutionToolSchema {\n const note =\n opts?.statefulSessions === true\n ? STATEFUL_BASH_PARAM_NOTE\n : STATELESS_BASH_PARAM_NOTE;\n const commandDescription =\n BashExecutionToolSchema.properties.command.description.replace(\n STATELESS_BASH_PARAM_NOTE,\n note\n );\n return {\n ...BashExecutionToolSchema,\n properties: {\n ...BashExecutionToolSchema.properties,\n command: {\n ...BashExecutionToolSchema.properties.command,\n description: commandDescription,\n },\n },\n } as typeof BashExecutionToolSchema;\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 | null = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n /* `statefulSessions` is prompt-only — keep it out of the wire body. */\n const {\n authHeaders,\n statefulSessions: _statefulSessions,\n ...executionParams\n } = params ?? {};\n void _statefulSessions;\n /* Drop any model-supplied `runtime_session_hint` from the raw args: the\n * hint must only come from ToolNode's injected `_runtime_session_hint`\n * (below), never from the tool call itself. */\n const {\n command,\n runtime_session_hint: _ignoredModelHint,\n ...rest\n } = rawInput as {\n command: string;\n runtime_session_hint?: unknown;\n args?: string[];\n };\n void _ignoredModelHint;\n const { session_id, _injected_files, _runtime_session_hint } =\n (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n _runtime_session_hint?: string;\n };\n\n const postData: Record<string, unknown> = {\n lang: 'bash',\n code: command,\n ...rest,\n ...executionParams,\n };\n\n if (\n typeof _runtime_session_hint === 'string' &&\n _runtime_session_hint !== ''\n ) {\n postData.runtime_session_hint = _runtime_session_hint;\n }\n\n /* See `CodeExecutor.ts` for the rationale — `/files/<session_id>`\n * HTTP fallback was removed because codeapi's sessionAuth requires\n * kind/id query params unavailable at this point. */\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (\n session_id != null &&\n session_id.length > 0 &&\n !Array.isArray(postData.files)\n ) {\n // eslint-disable-next-line no-console\n console.debug(\n `[BashExecutor] No injected files for session_id=${session_id} — exec will run without input files`\n );\n }\n\n try {\n const resolvedAuthHeaders =\n await resolveCodeApiAuthHeaders(authHeaders);\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n ...resolvedAuthHeaders,\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(\n await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)\n );\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\n const outputWithReminder = appendTmpScratchReminder(\n formattedOutput,\n command\n );\n const hasFiles = result.files != null && result.files.length > 0;\n const runtimeEcho =\n result.runtime_session_id != null\n ? {\n runtime_session_id: result.runtime_session_id,\n runtime_status: result.runtime_status,\n }\n : {};\n return [\n appendCodeSessionFileSummary(outputWithReminder, result.files),\n (hasFiles\n ? {\n session_id: result.session_id,\n files: result.files,\n ...runtimeEcho,\n }\n : {\n session_id: result.session_id,\n ...runtimeEcho,\n }) satisfies t.CodeExecutionArtifact,\n ];\n } catch (error) {\n const messageWithReminder = appendFailedExecutionFileReminder(\n (error as Error | undefined)?.message ?? '',\n command\n );\n throw new Error(`Execution error:\\n\\n${messageWithReminder}`);\n }\n },\n {\n name: BashExecutionToolName,\n description: buildBashExecutionToolDescription({\n statefulSessions: params?.statefulSessions,\n }),\n schema: buildBashExecutionToolSchema(params ?? undefined),\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createBashExecutionTool };\n"],"mappings":";;;;;;;;;;;mBAkBO;AAGP,MAAM,gBAAgB,GADDA,qBAAAA,eACe,EAAE;AAEtC,MAAa,0BAA0B;CACrC,MAAM;CACN,YAAY;EACV,SAAS;GACP,MAAM;GACN,aAAa;;;IAGfC,qBAAAA,4BAA4B;IAC5BC,qBAAAA,oBAAoB;;;;;EAKpB;EACA,MAAM;GACJ,MAAM;GACN,OAAO,EAAE,MAAM,SAAS;GACxB,aACE;EACJ;CACF;CACA,UAAU,CAAC,SAAS;AACtB;AAEA,MAAa,+BAA+B;;;;;;IAMxCD,qBAAAA,4BAA4B;IAC5BC,qBAAAA,oBAAoB;;EAEtB,KAAK;;;;;;;;;AAUP,MAAa,qBACX;AAEF,MAAa,uCAAuC;;;EAGlD,mBAAmB;;;;;IAKjBD,qBAAAA,4BAA4B;IAC5BC,qBAAAA,oBAAoB;;EAEtB,KAAK;;;;;;;;;;;AAYP,MAAa,gCAAgC;;;;;;;;;EAS3C,KAAK;;;;;;;;AASP,SAAgB,kCAAkC,SAGvC;CACT,MAAM,OACJ,SAAS,qBAAqB,OAC1B,uCACA;CACN,IAAI,SAAS,+BAA+B,MAC1C,OAAO,GAAG,KAAK,MAAM;CAEvB,OAAO;AACT;AAEA,MAAM,4BACJ;AACF,MAAM,2BACJ;AAEF,SAAgB,6BAA6B,MAEV;CACjC,MAAM,OACJ,MAAM,qBAAqB,OACvB,2BACA;CACN,MAAM,qBACJ,wBAAwB,WAAW,QAAQ,YAAY,QACrD,2BACA,IACF;CACF,OAAO;EACL,GAAG;EACH,YAAY;GACV,GAAG,wBAAwB;GAC3B,SAAS;IACP,GAAG,wBAAwB,WAAW;IACtC,aAAa;GACf;EACF;CACF;AACF;AAEA,MAAa,wBAAA;;;;;;;;;;;AAYb,MAAa,8BAA8B;CACzC,MAAM;CACN,aAAa;CACb,QAAQ;AACV;AAEA,SAAS,wBACP,SAA2C,CAAC,GACrB;CACvB,QAAA,GAAA,sBAAA,KAAA,CACE,OAAO,UAAU,WAAW;EAE1B,MAAM,EACJ,aACA,kBAAkB,mBAClB,GAAG,oBACD,UAAU,CAAC;EAKf,MAAM,EACJ,SACA,sBAAsB,mBACtB,GAAG,SACD;EAMJ,MAAM,EAAE,YAAY,iBAAiB,0BAClC,OAAO,YAAY,CAAC;EAMvB,MAAM,WAAoC;GACxC,MAAM;GACN,MAAM;GACN,GAAG;GACH,GAAG;EACL;EAEA,IACE,OAAO,0BAA0B,YACjC,0BAA0B,IAE1B,SAAS,uBAAuB;EAMlC,IAAI,mBAAmB,gBAAgB,SAAS,GAC9C,SAAS,QAAQ;OACZ,IACL,cAAc,QACd,WAAW,SAAS,KACpB,CAAC,MAAM,QAAQ,SAAS,KAAK,GAG7B,QAAQ,MACN,mDAAmD,WAAW,qCAChE;EAGF,IAAI;GAGF,MAAM,eAA4B;IAChC,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,cAAc;KACd,GAAG,MANCC,qBAAAA,0BAA0B,WAAW;IAO3C;IACA,MAAM,KAAK,UAAU,QAAQ;GAC/B;GAEA,IAAI,QAAQ,IAAI,SAAS,QAAQ,QAAQ,IAAI,UAAU,IACrD,aAAa,QAAQ,IAAIC,kBAAAA,gBAAgB,QAAQ,IAAI,KAAK;GAE5D,MAAM,WAAW,OAAA,GAAA,WAAA,QAAA,CAAY,eAAe,YAAY;GACxD,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MACR,MAAMC,qBAAAA,6BAA6B,QAAQ,eAAe,QAAQ,CACpE;GAGF,MAAM,SAA0B,MAAM,SAAS,KAAK;GACpD,IAAI,kBAAkB;GACtB,IAAI,OAAO,QACT,mBAAmB,YAAY,OAAO,OAAO;QAE7C,mBAAmBC,qBAAAA;GAErB,IAAI,OAAO,QAAQ,mBAAmB,YAAY,OAAO,OAAO;GAEhE,MAAM,qBAAqBC,qBAAAA,yBACzB,iBACA,OACF;GACA,MAAM,WAAW,OAAO,SAAS,QAAQ,OAAO,MAAM,SAAS;GAC/D,MAAM,cACJ,OAAO,sBAAsB,OACzB;IACA,oBAAoB,OAAO;IAC3B,gBAAgB,OAAO;GACzB,IACE,CAAC;GACP,OAAO,CACLC,+BAAAA,6BAA6B,oBAAoB,OAAO,KAAK,GAC5D,WACG;IACA,YAAY,OAAO;IACnB,OAAO,OAAO;IACd,GAAG;GACL,IACE;IACA,YAAY,OAAO;IACnB,GAAG;GACL,CACJ;EACF,SAAS,OAAO;GACd,MAAM,sBAAsBC,qBAAAA,kCACzB,OAA6B,WAAW,IACzC,OACF;GACA,MAAM,IAAI,MAAM,uBAAuB,qBAAqB;EAC9D;CACF,GACA;EACE,MAAM;EACN,aAAa,kCAAkC,EAC7C,kBAAkB,QAAQ,iBAC5B,CAAC;EACD,QAAQ,6BAA6B,UAAU,KAAA,CAAS;EACxD,gBAAA;CACF,CACF;AACF"}
@@ -98,14 +98,16 @@ Usage:
98
98
  - NEVER use this tool to execute malicious code.
99
99
  `.trim();
100
100
  /**
101
- * Best-effort statefulness note. Deliberately hedged: warm reuse is an
102
- * optimization, not a guarantee (the runtime may be reset on idle timeout,
103
- * eviction, or the 8h VM lifetime), so the model must never depend on carried
104
- * state for correctness and must persist anything durable to /mnt/data.
101
+ * Statefulness here is FILESYSTEM-tier, not runtime-tier. Executions in a
102
+ * session reuse one warm machine, so `/mnt/data` carries across calls but
103
+ * every execution is a brand-new interpreter process in a fresh sandbox, so
104
+ * variables and imports never survive. The note must not imply otherwise: a
105
+ * model told its in-memory state persists writes `df = ...` in one call and
106
+ * `df.head()` in the next, then hits a NameError it was told to treat as rare.
105
107
  */
106
- const STATEFUL_ENV_NOTE = "Session state (best-effort): consecutive executions in this conversation usually share one runtime, so variables, imports, and in-memory data from earlier successful calls are typically still available. The runtime may be reset at any time, so treat carried-over state as an optimization, never a guarantee. Anything that must survive MUST be written to /mnt/data. If a NameError/ImportError signals lost state, re-run the needed setup and continue.";
108
+ const STATEFUL_ENV_NOTE = "Session state: executions in this conversation run on the same warm machine, so files persist between calls but each execution is a NEW process. Variables, imports, and in-memory data NEVER carry over: every call must re-import and rebuild the state it needs. Only /mnt/data is durable (the machine itself may also be reset at any time), so write anything that must survive there and read it back next call.";
107
109
  const StatefulCodeExecutionToolDescription = `
108
- Runs code and returns stdout/stderr output from a session-based execution environment, similar to a long-running command-line session.
110
+ Runs code and returns stdout/stderr output. Executions in this conversation share one warm machine with a persistent /mnt/data, but each execution runs as a separate process (not a notebook-style kernel).
109
111
 
110
112
  ${STATEFUL_ENV_NOTE}
111
113
 
@@ -119,7 +121,7 @@ function buildCodeExecutionToolDescription(opts) {
119
121
  return opts?.statefulSessions === true ? StatefulCodeExecutionToolDescription : CodeExecutionToolDescription;
120
122
  }
121
123
  const STATELESS_CODE_PARAM_NOTE = "The environment is stateless; variables and imports don't persist between executions.";
122
- const STATEFUL_CODE_PARAM_NOTE = "Executions in this conversation usually share one runtime: variables and imports from prior successful calls are typically still defined, but the runtime may reset between calls. Rebuild state on NameError/ImportError; persist anything important to /mnt/data.";
124
+ const STATEFUL_CODE_PARAM_NOTE = "Executions in this conversation share one warm machine, so files written to /mnt/data persist between calls. Each execution is a new process: variables and imports do NOT carry over re-import and reload from /mnt/data every call.";
123
125
  function buildCodeExecutionToolSchema(opts) {
124
126
  const note = opts?.statefulSessions === true ? STATEFUL_CODE_PARAM_NOTE : STATELESS_CODE_PARAM_NOTE;
125
127
  const codeDescription = CodeExecutionToolSchema.properties.code.description.replace(STATELESS_CODE_PARAM_NOTE, note);
@@ -1 +1 @@
1
- {"version":3,"file":"CodeExecutor.cjs","names":["HttpsProxyAgent","appendCodeSessionFileSummary"],"sources":["../../../src/tools/CodeExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { appendCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';\nimport { EnvVar, Constants } from '@/common';\n\nexport {\n appendCodeSessionFileSummary,\n stripCodeSessionFileSummary,\n} from '@/tools/CodeSessionFileSummary';\n\nconfig();\n\nexport const getCodeBaseURL = (): string =>\n getEnvironmentVariable(EnvVar.CODE_BASEURL) ??\n Constants.OFFICIAL_CODE_BASEURL;\n\nexport const emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nexport const CODE_ARTIFACT_PATH_GUIDANCE =\n 'Persist handoff artifacts in `/mnt/data` with standard extensions (.json/.txt/.csv/.tsv/.log/.parquet/.png/.jpg/.pdf/.xlsx); failed executions do not register new files; `/tmp` and odd extensions are same-call scratch only, not later-call storage.';\n\nexport const BASH_SHELL_GUIDANCE =\n 'Bash: multi-line files use heredoc/printf; run Python via python3 -c/heredoc, not bare Python.';\n\nconst TMP_PATH_PATTERN = /(^|[^A-Za-z0-9_])\\/tmp(?:\\/|\\b)/;\nconst MNT_DATA_PATH_PATTERN = /(^|[^A-Za-z0-9_])\\/mnt\\/data(?:\\/|\\b)/;\n\nexport const TMP_SCRATCH_OUTPUT_REMINDER =\n 'Note: /tmp files are same-call scratch only and were not persisted; use /mnt/data for files needed later.';\n\nexport const FAILED_EXECUTION_FILE_REMINDER =\n 'Note: any files written during this failed call were not registered for later calls; fix the error and rerun before relying on them.';\n\nexport function appendTmpScratchReminder(output: string, code: string): string {\n if (!TMP_PATH_PATTERN.test(code)) {\n return output;\n }\n return `${output.trimEnd()}\\n${TMP_SCRATCH_OUTPUT_REMINDER}\\n`;\n}\n\nexport function appendFailedExecutionFileReminder(\n output: string,\n code: string\n): string {\n if (\n !MNT_DATA_PATH_PATTERN.test(code) ||\n output.includes(FAILED_EXECUTION_FILE_REMINDER)\n ) {\n return output;\n }\n return `${output.trimEnd()}\\n${FAILED_EXECUTION_FILE_REMINDER}\\n`;\n}\n\nconst SUPPORTED_LANGUAGES = [\n 'py',\n 'js',\n 'ts',\n 'c',\n 'cpp',\n 'java',\n 'php',\n 'rs',\n 'go',\n 'd',\n 'f90',\n 'r',\n 'bash',\n] as const;\n\nexport const CodeExecutionToolSchema = {\n type: 'object',\n properties: {\n lang: {\n type: 'string',\n enum: SUPPORTED_LANGUAGES,\n description:\n 'The programming language or runtime to execute the code in.',\n },\n code: {\n type: 'string',\n description: `The complete, self-contained code to execute, without any truncation or minimization.\n- The environment is stateless; variables and imports don't persist between executions.\n- Prior /mnt/data files are available and can be modified in place.\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\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- py: This is not a Jupyter notebook environment. Use \\`print()\\` for all outputs.\n- py: Matplotlib: Use \\`plt.savefig()\\` to save plots as files.\n- js: use the \\`console\\` or \\`process\\` methods for all outputs.\n- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).\n- Other languages: use appropriate output functions.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',\n },\n },\n required: ['lang', 'code'],\n} as const;\n\nconst baseEndpoint = getCodeBaseURL();\nconst EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\ntype SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];\n\nexport async function resolveCodeApiAuthHeaders(\n authHeaders?: t.CodeApiAuthHeaders\n): Promise<t.CodeApiAuthHeaderMap> {\n if (authHeaders == null) {\n return {};\n }\n if (typeof authHeaders === 'function') {\n return authHeaders();\n }\n return authHeaders;\n}\n\nexport async function buildCodeApiHttpErrorMessage(\n method: string,\n endpoint: string,\n response: { status: number; text: () => Promise<string> }\n): Promise<string> {\n let responseBody = '';\n try {\n responseBody = await response.text();\n } catch {\n responseBody = '';\n }\n const body = responseBody.trim();\n const bodySuffix = body === '' ? '' : `, body: ${body.slice(0, 1000)}`;\n return `CodeAPI request failed: ${method} ${endpoint} returned ${response.status}${bodySuffix}`;\n}\n\nexport const CodeExecutionToolDescription = `\nRuns code 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- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- NEVER use this tool to execute malicious code.\n`.trim();\n\n/**\n * Best-effort statefulness note. Deliberately hedged: warm reuse is an\n * optimization, not a guarantee (the runtime may be reset on idle timeout,\n * eviction, or the 8h VM lifetime), so the model must never depend on carried\n * state for correctness and must persist anything durable to /mnt/data.\n */\nexport const STATEFUL_ENV_NOTE =\n 'Session state (best-effort): consecutive executions in this conversation usually share one runtime, so variables, imports, and in-memory data from earlier successful calls are typically still available. The runtime may be reset at any time, so treat carried-over state as an optimization, never a guarantee. Anything that must survive MUST be written to /mnt/data. If a NameError/ImportError signals lost state, re-run the needed setup and continue.';\n\nexport const StatefulCodeExecutionToolDescription = `\nRuns code and returns stdout/stderr output from a session-based execution environment, similar to a long-running command-line session.\n\n${STATEFUL_ENV_NOTE}\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- NEVER use this tool to execute malicious code.\n`.trim();\n\nexport function buildCodeExecutionToolDescription(opts?: {\n statefulSessions?: boolean;\n}): string {\n return opts?.statefulSessions === true\n ? StatefulCodeExecutionToolDescription\n : CodeExecutionToolDescription;\n}\n\nconst STATELESS_CODE_PARAM_NOTE =\n 'The environment is stateless; variables and imports don\\'t persist between executions.';\nconst STATEFUL_CODE_PARAM_NOTE =\n 'Executions in this conversation usually share one runtime: variables and imports from prior successful calls are typically still defined, but the runtime may reset between calls. Rebuild state on NameError/ImportError; persist anything important to /mnt/data.';\n\nexport function buildCodeExecutionToolSchema(opts?: {\n statefulSessions?: boolean;\n}): typeof CodeExecutionToolSchema {\n const note =\n opts?.statefulSessions === true\n ? STATEFUL_CODE_PARAM_NOTE\n : STATELESS_CODE_PARAM_NOTE;\n const codeDescription =\n CodeExecutionToolSchema.properties.code.description.replace(\n STATELESS_CODE_PARAM_NOTE,\n note\n );\n return {\n ...CodeExecutionToolSchema,\n properties: {\n ...CodeExecutionToolSchema.properties,\n code: {\n ...CodeExecutionToolSchema.properties.code,\n description: codeDescription,\n },\n },\n } as typeof CodeExecutionToolSchema;\n}\n\nexport const CodeExecutionToolName = Constants.EXECUTE_CODE;\n\nexport const CodeExecutionToolDefinition = {\n name: CodeExecutionToolName,\n description: CodeExecutionToolDescription,\n schema: CodeExecutionToolSchema,\n} as const;\n\nfunction createCodeExecutionTool(\n params: t.CodeExecutionToolParams | null = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n /* `statefulSessions` is a prompt-only flag (drives the description);\n * keep it out of the wire body. */\n const {\n authHeaders,\n statefulSessions: _statefulSessions,\n ...executionParams\n } = params ?? {};\n void _statefulSessions;\n /* Drop any model-supplied `runtime_session_hint` from the raw args: the\n * hint is host-controlled and must only ever come from ToolNode's\n * injected `_runtime_session_hint` (below). Spreading `...rest` into\n * postData would otherwise let a tool call opt itself into / pick a\n * stateful runtime even when statefulSessions is off. */\n const {\n lang,\n code,\n runtime_session_hint: _ignoredModelHint,\n ...rest\n } = rawInput as {\n lang: SupportedLanguage;\n code: string;\n runtime_session_hint?: unknown;\n args?: string[];\n };\n void _ignoredModelHint;\n /**\n * Extract session context from config.toolCall (injected by ToolNode).\n * - session_id: associates with the previous run.\n * - _injected_files: File refs to pass directly (avoids /files endpoint race condition).\n */\n const { session_id, _injected_files, _runtime_session_hint } =\n (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n _runtime_session_hint?: string;\n };\n\n const postData: Record<string, unknown> = {\n lang,\n code,\n ...rest,\n ...executionParams,\n };\n\n /* Stateful sessions: forward the hint so the Code API can route this\n * execution to a warm per-session runtime. Additive — stateless\n * servers ignore the unknown field. */\n if (\n typeof _runtime_session_hint === 'string' &&\n _runtime_session_hint !== ''\n ) {\n postData.runtime_session_hint = _runtime_session_hint;\n }\n\n /* File injection: `_injected_files` from ToolNode (set when host\n * primes a CodeSessionContext) or `params.files` from tool\n * factory (set by hosts that pre-resolve at construction time).\n * The legacy `/files/<session_id>` HTTP fallback was removed —\n * codeapi's `sessionAuth` middleware now requires kind/id query\n * params the tool can't supply at this point, so the fetch 400'd\n * silently and the catch swallowed the failure. */\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (\n session_id != null &&\n session_id.length > 0 &&\n !Array.isArray(postData.files)\n ) {\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] No injected files for session_id=${session_id} — exec will run without input files`\n );\n }\n\n try {\n const resolvedAuthHeaders =\n await resolveCodeApiAuthHeaders(authHeaders);\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n ...resolvedAuthHeaders,\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(\n await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)\n );\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\n const outputWithReminder = appendTmpScratchReminder(\n formattedOutput,\n code\n );\n const hasFiles = result.files != null && result.files.length > 0;\n /* Echo the durable runtime session (stateful backends only) so hosts\n * can surface a \"session active / was reset\" signal later. Additive:\n * absent on stateless servers. */\n const runtimeEcho =\n result.runtime_session_id != null\n ? {\n runtime_session_id: result.runtime_session_id,\n runtime_status: result.runtime_status,\n }\n : {};\n return [\n appendCodeSessionFileSummary(outputWithReminder, result.files),\n (hasFiles\n ? {\n session_id: result.session_id,\n files: result.files,\n ...runtimeEcho,\n }\n : {\n session_id: result.session_id,\n ...runtimeEcho,\n }) satisfies t.CodeExecutionArtifact,\n ];\n } catch (error) {\n const messageWithReminder = appendFailedExecutionFileReminder(\n (error as Error | undefined)?.message ?? '',\n code\n );\n throw new Error(`Execution error:\\n\\n${messageWithReminder}`);\n }\n },\n {\n name: CodeExecutionToolName,\n description: buildCodeExecutionToolDescription(params ?? undefined),\n schema: buildCodeExecutionToolSchema(params ?? undefined),\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createCodeExecutionTool };\n"],"mappings":"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"}
1
+ {"version":3,"file":"CodeExecutor.cjs","names":["HttpsProxyAgent","appendCodeSessionFileSummary"],"sources":["../../../src/tools/CodeExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { appendCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';\nimport { EnvVar, Constants } from '@/common';\n\nexport {\n appendCodeSessionFileSummary,\n stripCodeSessionFileSummary,\n} from '@/tools/CodeSessionFileSummary';\n\nconfig();\n\nexport const getCodeBaseURL = (): string =>\n getEnvironmentVariable(EnvVar.CODE_BASEURL) ??\n Constants.OFFICIAL_CODE_BASEURL;\n\nexport const emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nexport const CODE_ARTIFACT_PATH_GUIDANCE =\n 'Persist handoff artifacts in `/mnt/data` with standard extensions (.json/.txt/.csv/.tsv/.log/.parquet/.png/.jpg/.pdf/.xlsx); failed executions do not register new files; `/tmp` and odd extensions are same-call scratch only, not later-call storage.';\n\nexport const BASH_SHELL_GUIDANCE =\n 'Bash: multi-line files use heredoc/printf; run Python via python3 -c/heredoc, not bare Python.';\n\nconst TMP_PATH_PATTERN = /(^|[^A-Za-z0-9_])\\/tmp(?:\\/|\\b)/;\nconst MNT_DATA_PATH_PATTERN = /(^|[^A-Za-z0-9_])\\/mnt\\/data(?:\\/|\\b)/;\n\nexport const TMP_SCRATCH_OUTPUT_REMINDER =\n 'Note: /tmp files are same-call scratch only and were not persisted; use /mnt/data for files needed later.';\n\nexport const FAILED_EXECUTION_FILE_REMINDER =\n 'Note: any files written during this failed call were not registered for later calls; fix the error and rerun before relying on them.';\n\nexport function appendTmpScratchReminder(output: string, code: string): string {\n if (!TMP_PATH_PATTERN.test(code)) {\n return output;\n }\n return `${output.trimEnd()}\\n${TMP_SCRATCH_OUTPUT_REMINDER}\\n`;\n}\n\nexport function appendFailedExecutionFileReminder(\n output: string,\n code: string\n): string {\n if (\n !MNT_DATA_PATH_PATTERN.test(code) ||\n output.includes(FAILED_EXECUTION_FILE_REMINDER)\n ) {\n return output;\n }\n return `${output.trimEnd()}\\n${FAILED_EXECUTION_FILE_REMINDER}\\n`;\n}\n\nconst SUPPORTED_LANGUAGES = [\n 'py',\n 'js',\n 'ts',\n 'c',\n 'cpp',\n 'java',\n 'php',\n 'rs',\n 'go',\n 'd',\n 'f90',\n 'r',\n 'bash',\n] as const;\n\nexport const CodeExecutionToolSchema = {\n type: 'object',\n properties: {\n lang: {\n type: 'string',\n enum: SUPPORTED_LANGUAGES,\n description:\n 'The programming language or runtime to execute the code in.',\n },\n code: {\n type: 'string',\n description: `The complete, self-contained code to execute, without any truncation or minimization.\n- The environment is stateless; variables and imports don't persist between executions.\n- Prior /mnt/data files are available and can be modified in place.\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\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- py: This is not a Jupyter notebook environment. Use \\`print()\\` for all outputs.\n- py: Matplotlib: Use \\`plt.savefig()\\` to save plots as files.\n- js: use the \\`console\\` or \\`process\\` methods for all outputs.\n- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).\n- Other languages: use appropriate output functions.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',\n },\n },\n required: ['lang', 'code'],\n} as const;\n\nconst baseEndpoint = getCodeBaseURL();\nconst EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\ntype SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];\n\nexport async function resolveCodeApiAuthHeaders(\n authHeaders?: t.CodeApiAuthHeaders\n): Promise<t.CodeApiAuthHeaderMap> {\n if (authHeaders == null) {\n return {};\n }\n if (typeof authHeaders === 'function') {\n return authHeaders();\n }\n return authHeaders;\n}\n\nexport async function buildCodeApiHttpErrorMessage(\n method: string,\n endpoint: string,\n response: { status: number; text: () => Promise<string> }\n): Promise<string> {\n let responseBody = '';\n try {\n responseBody = await response.text();\n } catch {\n responseBody = '';\n }\n const body = responseBody.trim();\n const bodySuffix = body === '' ? '' : `, body: ${body.slice(0, 1000)}`;\n return `CodeAPI request failed: ${method} ${endpoint} returned ${response.status}${bodySuffix}`;\n}\n\nexport const CodeExecutionToolDescription = `\nRuns code 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- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- NEVER use this tool to execute malicious code.\n`.trim();\n\n/**\n * Statefulness here is FILESYSTEM-tier, not runtime-tier. Executions in a\n * session reuse one warm machine, so `/mnt/data` carries across calls — but\n * every execution is a brand-new interpreter process in a fresh sandbox, so\n * variables and imports never survive. The note must not imply otherwise: a\n * model told its in-memory state persists writes `df = ...` in one call and\n * `df.head()` in the next, then hits a NameError it was told to treat as rare.\n */\nexport const STATEFUL_ENV_NOTE =\n 'Session state: executions in this conversation run on the same warm machine, so files persist between calls — but each execution is a NEW process. Variables, imports, and in-memory data NEVER carry over: every call must re-import and rebuild the state it needs. Only /mnt/data is durable (the machine itself may also be reset at any time), so write anything that must survive there and read it back next call.';\n\nexport const StatefulCodeExecutionToolDescription = `\nRuns code and returns stdout/stderr output. Executions in this conversation share one warm machine with a persistent /mnt/data, but each execution runs as a separate process (not a notebook-style kernel).\n\n${STATEFUL_ENV_NOTE}\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- NEVER use this tool to execute malicious code.\n`.trim();\n\nexport function buildCodeExecutionToolDescription(opts?: {\n statefulSessions?: boolean;\n}): string {\n return opts?.statefulSessions === true\n ? StatefulCodeExecutionToolDescription\n : CodeExecutionToolDescription;\n}\n\nconst STATELESS_CODE_PARAM_NOTE =\n 'The environment is stateless; variables and imports don\\'t persist between executions.';\nconst STATEFUL_CODE_PARAM_NOTE =\n 'Executions in this conversation share one warm machine, so files written to /mnt/data persist between calls. Each execution is a new process: variables and imports do NOT carry over — re-import and reload from /mnt/data every call.';\n\nexport function buildCodeExecutionToolSchema(opts?: {\n statefulSessions?: boolean;\n}): typeof CodeExecutionToolSchema {\n const note =\n opts?.statefulSessions === true\n ? STATEFUL_CODE_PARAM_NOTE\n : STATELESS_CODE_PARAM_NOTE;\n const codeDescription =\n CodeExecutionToolSchema.properties.code.description.replace(\n STATELESS_CODE_PARAM_NOTE,\n note\n );\n return {\n ...CodeExecutionToolSchema,\n properties: {\n ...CodeExecutionToolSchema.properties,\n code: {\n ...CodeExecutionToolSchema.properties.code,\n description: codeDescription,\n },\n },\n } as typeof CodeExecutionToolSchema;\n}\n\nexport const CodeExecutionToolName = Constants.EXECUTE_CODE;\n\nexport const CodeExecutionToolDefinition = {\n name: CodeExecutionToolName,\n description: CodeExecutionToolDescription,\n schema: CodeExecutionToolSchema,\n} as const;\n\nfunction createCodeExecutionTool(\n params: t.CodeExecutionToolParams | null = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n /* `statefulSessions` is a prompt-only flag (drives the description);\n * keep it out of the wire body. */\n const {\n authHeaders,\n statefulSessions: _statefulSessions,\n ...executionParams\n } = params ?? {};\n void _statefulSessions;\n /* Drop any model-supplied `runtime_session_hint` from the raw args: the\n * hint is host-controlled and must only ever come from ToolNode's\n * injected `_runtime_session_hint` (below). Spreading `...rest` into\n * postData would otherwise let a tool call opt itself into / pick a\n * stateful runtime even when statefulSessions is off. */\n const {\n lang,\n code,\n runtime_session_hint: _ignoredModelHint,\n ...rest\n } = rawInput as {\n lang: SupportedLanguage;\n code: string;\n runtime_session_hint?: unknown;\n args?: string[];\n };\n void _ignoredModelHint;\n /**\n * Extract session context from config.toolCall (injected by ToolNode).\n * - session_id: associates with the previous run.\n * - _injected_files: File refs to pass directly (avoids /files endpoint race condition).\n */\n const { session_id, _injected_files, _runtime_session_hint } =\n (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n _runtime_session_hint?: string;\n };\n\n const postData: Record<string, unknown> = {\n lang,\n code,\n ...rest,\n ...executionParams,\n };\n\n /* Stateful sessions: forward the hint so the Code API can route this\n * execution to a warm per-session runtime. Additive — stateless\n * servers ignore the unknown field. */\n if (\n typeof _runtime_session_hint === 'string' &&\n _runtime_session_hint !== ''\n ) {\n postData.runtime_session_hint = _runtime_session_hint;\n }\n\n /* File injection: `_injected_files` from ToolNode (set when host\n * primes a CodeSessionContext) or `params.files` from tool\n * factory (set by hosts that pre-resolve at construction time).\n * The legacy `/files/<session_id>` HTTP fallback was removed —\n * codeapi's `sessionAuth` middleware now requires kind/id query\n * params the tool can't supply at this point, so the fetch 400'd\n * silently and the catch swallowed the failure. */\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (\n session_id != null &&\n session_id.length > 0 &&\n !Array.isArray(postData.files)\n ) {\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] No injected files for session_id=${session_id} — exec will run without input files`\n );\n }\n\n try {\n const resolvedAuthHeaders =\n await resolveCodeApiAuthHeaders(authHeaders);\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n ...resolvedAuthHeaders,\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(\n await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)\n );\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\n const outputWithReminder = appendTmpScratchReminder(\n formattedOutput,\n code\n );\n const hasFiles = result.files != null && result.files.length > 0;\n /* Echo the durable runtime session (stateful backends only) so hosts\n * can surface a \"session active / was reset\" signal later. Additive:\n * absent on stateless servers. */\n const runtimeEcho =\n result.runtime_session_id != null\n ? {\n runtime_session_id: result.runtime_session_id,\n runtime_status: result.runtime_status,\n }\n : {};\n return [\n appendCodeSessionFileSummary(outputWithReminder, result.files),\n (hasFiles\n ? {\n session_id: result.session_id,\n files: result.files,\n ...runtimeEcho,\n }\n : {\n session_id: result.session_id,\n ...runtimeEcho,\n }) satisfies t.CodeExecutionArtifact,\n ];\n } catch (error) {\n const messageWithReminder = appendFailedExecutionFileReminder(\n (error as Error | undefined)?.message ?? '',\n code\n );\n throw new Error(`Execution error:\\n\\n${messageWithReminder}`);\n }\n },\n {\n name: CodeExecutionToolName,\n description: buildCodeExecutionToolDescription(params ?? undefined),\n schema: buildCodeExecutionToolSchema(params ?? undefined),\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createCodeExecutionTool };\n"],"mappings":"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"}
@@ -23,6 +23,7 @@ import { joinKeys, resetIfNotEmpty } from "../utils/graph.mjs";
23
23
  import { isAnthropicLike, isGoogleLike, isOpenAILike } from "../utils/llm.mjs";
24
24
  import { handleToolCalls } from "../tools/handlers.mjs";
25
25
  import { ToolOutputReferenceRegistry } from "../tools/toolOutputReferences.mjs";
26
+ import { HandlerRegistry } from "../events.mjs";
26
27
  import { sleep } from "../utils/run.mjs";
27
28
  import "../utils/index.mjs";
28
29
  import { partitionAndMarkOpenRouterToolCache } from "../llm/openrouter/toolCache.mjs";
@@ -52,6 +53,13 @@ import { nanoid } from "nanoid";
52
53
  const { AGENT, TOOLS, SUMMARIZE } = GraphNodeKeys;
53
54
  /** Minimum relative variance before calibrated toolSchemaTokens overrides current value. */
54
55
  const CALIBRATION_VARIANCE_THRESHOLD = .15;
56
+ function createToolHandlerRegistry(source) {
57
+ const toolHandler = source?.getHandler("on_tool_execute");
58
+ if (toolHandler == null) return;
59
+ const registry = new HandlerRegistry();
60
+ registry.register("on_tool_execute", toolHandler);
61
+ return registry;
62
+ }
55
63
  /**
56
64
  * Start index of the span post-prune formatters can mutate in place: the
57
65
  * trailing tool batch plus its owning AI message (artifact formatting touches
@@ -242,6 +250,8 @@ var Graph = class {
242
250
  /** Set of invoked tool call IDs from non-message run steps completed mid-run, if any */
243
251
  invokedToolIds;
244
252
  handlerRegistry;
253
+ /** Host registry retained only for forwarding tools from nested child graphs. */
254
+ parentToolHandlerRegistry;
245
255
  /**
246
256
  * True when event-driven tool execution can be routed through callbacks even
247
257
  * though this graph intentionally does not own the full handler registry.
@@ -324,6 +334,7 @@ var Graph = class {
324
334
  this.prelimMessageIdsByStepKey = /* @__PURE__ */ new Map();
325
335
  this.invokedToolIds = void 0;
326
336
  this.handlerRegistry = void 0;
337
+ this.parentToolHandlerRegistry = void 0;
327
338
  this.hookRegistry = void 0;
328
339
  this.humanInTheLoop = void 0;
329
340
  this.toolOutputReferences = void 0;
@@ -1243,7 +1254,7 @@ var StandardGraph = class StandardGraph extends Graph {
1243
1254
  if (agentContext.subagentConfigs != null && agentContext.subagentConfigs.length > 0 && effectiveSubagentDepth > 0) {
1244
1255
  const resolvedConfigs = resolveSubagentConfigs(agentContext.subagentConfigs, agentContext);
1245
1256
  if (resolvedConfigs.length > 0) {
1246
- const getParentHandlerRegistry = () => this.handlerRegistry;
1257
+ const getParentHandlerRegistry = () => this.handlerRegistry ?? this.parentToolHandlerRegistry;
1247
1258
  const executor = new SubagentExecutor({
1248
1259
  configs: new Map(resolvedConfigs.map((c) => [c.type, c])),
1249
1260
  parentSignal: this.signal,
@@ -1260,6 +1271,7 @@ var StandardGraph = class StandardGraph extends Graph {
1260
1271
  maxDepth: effectiveSubagentDepth,
1261
1272
  createChildGraph: (input) => {
1262
1273
  const childGraph = new StandardGraph(input);
1274
+ const toolHandlerRegistry = createToolHandlerRegistry(getParentHandlerRegistry());
1263
1275
  childGraph.hookRegistry = this.hookRegistry;
1264
1276
  /**
1265
1277
  * Do not propagate `humanInTheLoop` into the child graph yet:
@@ -1272,7 +1284,8 @@ var StandardGraph = class StandardGraph extends Graph {
1272
1284
  childGraph.codeSessionToolNames = this.codeSessionToolNames;
1273
1285
  childGraph.interruptingToolNames = this.interruptingToolNames;
1274
1286
  childGraph.toolExecution = this.toolExecution;
1275
- childGraph.eventToolExecutionAvailable = this.handlerRegistry?.getHandler("on_tool_execute") != null;
1287
+ childGraph.parentToolHandlerRegistry = toolHandlerRegistry;
1288
+ childGraph.eventToolExecutionAvailable = toolHandlerRegistry != null;
1276
1289
  return childGraph;
1277
1290
  }
1278
1291
  });