@librechat/agents 3.1.94 → 3.1.96

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/main.cjs CHANGED
@@ -202,6 +202,7 @@ exports.createBashProgrammaticToolCallingSchema = BashProgrammaticToolCalling.cr
202
202
  exports.createBashProgrammaticToolCallingTool = BashProgrammaticToolCalling.createBashProgrammaticToolCallingTool;
203
203
  exports.extractUsedBashToolNames = BashProgrammaticToolCalling.extractUsedBashToolNames;
204
204
  exports.filterBashToolsByUsage = BashProgrammaticToolCalling.filterBashToolsByUsage;
205
+ exports.normalizeBashToolResultsForReplay = BashProgrammaticToolCalling.normalizeBashToolResultsForReplay;
205
206
  exports.normalizeToBashIdentifier = BashProgrammaticToolCalling.normalizeToBashIdentifier;
206
207
  exports.SkillToolDefinition = SkillTool.SkillToolDefinition;
207
208
  exports.SkillToolDescription = SkillTool.SkillToolDescription;
@@ -1 +1 @@
1
- {"version":3,"file":"main.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"main.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -51,7 +51,7 @@ const CORE_RULES = `Rules:
51
51
  - Tools are pre-defined as bash functions—DO NOT redefine them
52
52
  - Each tool function accepts a JSON string argument
53
53
  - Save tool output with raw=$(tool '{}'); printf '%s\n' "$raw" > /mnt/data/file.json; direct tool > file may be empty
54
- - jq: use fromjson? // . on saved tool stdout and again on JSON-string fields; check types since arrays may contain strings
54
+ - Tool stdout is normalized to one compact JSON value when possible; parse saved stdout once, then use fromjson? // . only for JSON-string fields
55
55
  - Only echo/printf output returns to the model
56
56
  - ${CodeExecutor.CODE_ARTIFACT_PATH_GUIDANCE}
57
57
  - ${CodeExecutor.BASH_SHELL_GUIDANCE}
@@ -114,6 +114,32 @@ const BashProgrammaticToolCallingDefinition = {
114
114
  description: BashProgrammaticToolCallingDescription,
115
115
  schema: BashProgrammaticToolCallingSchema,
116
116
  };
117
+ function maybeParseJsonResultString(result) {
118
+ if (typeof result !== 'string') {
119
+ return result;
120
+ }
121
+ const trimmed = result.trim();
122
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
123
+ return result;
124
+ }
125
+ try {
126
+ return JSON.parse(trimmed);
127
+ }
128
+ catch {
129
+ return result;
130
+ }
131
+ }
132
+ function normalizeBashToolResultsForReplay(toolResults) {
133
+ return toolResults.map((toolResult) => {
134
+ if (toolResult.is_error) {
135
+ return toolResult;
136
+ }
137
+ return {
138
+ ...toolResult,
139
+ result: maybeParseJsonResultString(toolResult.result),
140
+ };
141
+ });
142
+ }
117
143
  // ============================================================================
118
144
  // Helper Functions
119
145
  // ============================================================================
@@ -254,7 +280,7 @@ function createBashProgrammaticToolCallingTool(initParams = {}) {
254
280
  // eslint-disable-next-line no-console
255
281
  console.log(`[BashPTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`);
256
282
  }
257
- const toolResults = await ProgrammaticToolCalling.executeTools(response.tool_calls ?? [], toolMap, _enum.Constants.BASH_PROGRAMMATIC_TOOL_CALLING);
283
+ const toolResults = normalizeBashToolResultsForReplay(await ProgrammaticToolCalling.executeTools(response.tool_calls ?? [], toolMap, _enum.Constants.BASH_PROGRAMMATIC_TOOL_CALLING));
258
284
  response = await ProgrammaticToolCalling.makeRequest(EXEC_ENDPOINT, {
259
285
  continuation_token: response.continuation_token,
260
286
  tool_results: toolResults,
@@ -294,5 +320,6 @@ exports.createBashProgrammaticToolCallingSchema = createBashProgrammaticToolCall
294
320
  exports.createBashProgrammaticToolCallingTool = createBashProgrammaticToolCallingTool;
295
321
  exports.extractUsedBashToolNames = extractUsedBashToolNames;
296
322
  exports.filterBashToolsByUsage = filterBashToolsByUsage;
323
+ exports.normalizeBashToolResultsForReplay = normalizeBashToolResultsForReplay;
297
324
  exports.normalizeToBashIdentifier = normalizeToBashIdentifier;
298
325
  //# sourceMappingURL=BashProgrammaticToolCalling.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"BashProgrammaticToolCalling.cjs","sources":["../../../src/tools/BashProgrammaticToolCalling.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { ProgrammaticToolCallingJsonSchema } from './ptcTimeout';\nimport type * as t from '@/types';\nimport {\n makeRequest,\n executeTools,\n formatCompletedResponse,\n} from './ProgrammaticToolCalling';\nimport {\n BASH_SHELL_GUIDANCE,\n CODE_ARTIFACT_PATH_GUIDANCE,\n appendFailedExecutionFileReminder,\n getCodeBaseURL,\n} from './CodeExecutor';\nimport {\n clampCodeApiRunTimeoutMs,\n createCodeApiRunTimeoutSchema,\n resolveCodeApiRunTimeoutMs,\n} from './ptcTimeout';\nimport { Constants } from '@/common';\n\nconfig();\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_MAX_ROUND_TRIPS = 20;\nconst DEFAULT_RUN_TIMEOUT_MS = resolveCodeApiRunTimeoutMs();\n\n/** Bash reserved words that get `_tool` suffix when used as function names */\nconst BASH_RESERVED = new Set([\n 'if',\n 'then',\n 'else',\n 'elif',\n 'fi',\n 'case',\n 'esac',\n 'for',\n 'while',\n 'until',\n 'do',\n 'done',\n 'in',\n 'function',\n 'select',\n 'time',\n 'coproc',\n 'declare',\n 'typeset',\n 'local',\n 'readonly',\n 'export',\n 'unset',\n]);\n\n// ============================================================================\n// Description Components\n// ============================================================================\n\nconst STATELESS_WARNING = `CRITICAL - STATELESS EXECUTION:\nEach call is a fresh bash shell. Variables and state do NOT persist between calls.\nYou MUST complete your entire workflow in ONE code block.\nDO NOT split work across multiple calls expecting to reuse variables.`;\n\nconst CORE_RULES = `Rules:\n- One call: state does not persist\n- Tools are pre-defined as bash functions—DO NOT redefine them\n- Each tool function accepts a JSON string argument\n- Save tool output with raw=$(tool '{}'); printf '%s\\n' \"$raw\" > /mnt/data/file.json; direct tool > file may be empty\n- jq: use fromjson? // . on saved tool stdout and again on JSON-string fields; check types since arrays may contain strings\n- Only echo/printf output returns to the model\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\n- timeout caps one sandbox run/replay iteration, not the total multi-round-trip workflow`;\n\nconst ADDITIONAL_RULES =\n '- Tool names normalized: hyphens→underscores, reserved words get `_tool` suffix';\n\nconst EXAMPLES = `Example (Complete workflow in one call):\n # Query data and process\n data=$(query_database '{\"sql\": \"SELECT * FROM users\"}')\n echo \"$data\" | jq '.[] | .name'\n\nExample (Parallel calls):\n { sf=$(web_search '{\"query\": \"SF weather\"}'); printf '%s\\n' \"$sf\" > /mnt/data/sf.json; } &\n { ny=$(web_search '{\"query\": \"NY weather\"}'); printf '%s\\n' \"$ny\" > /mnt/data/ny.json; } &\n wait\n echo \"SF: $(jq -r . /mnt/data/sf.json)\"\n echo \"NY: $(jq -r . /mnt/data/ny.json)\"`;\n\nconst CODE_PARAM_DESCRIPTION = `Bash code that calls tools programmatically. Tools are available as bash functions.\n\n${STATELESS_WARNING}\n\nEach tool function accepts a JSON string as its argument.\nExample: tool_name '{\"key\": \"value\"}'\n\n${EXAMPLES}\n\n${CORE_RULES}`;\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nexport function createBashProgrammaticToolCallingSchema(\n maxRunTimeoutMs = DEFAULT_RUN_TIMEOUT_MS\n): ProgrammaticToolCallingJsonSchema {\n return {\n type: 'object',\n properties: {\n code: {\n type: 'string',\n minLength: 1,\n description: CODE_PARAM_DESCRIPTION,\n },\n timeout: createCodeApiRunTimeoutSchema(maxRunTimeoutMs),\n },\n required: ['code'],\n } as const;\n}\n\nexport const BashProgrammaticToolCallingSchema =\n createBashProgrammaticToolCallingSchema();\n\nexport const BashProgrammaticToolCallingName =\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING;\n\nexport const BashProgrammaticToolCallingDescription = `\nRun tools via bash code. Tools are available as bash functions that accept JSON string arguments.\n\n${STATELESS_WARNING}\n\n${CORE_RULES}\n${ADDITIONAL_RULES}\n\nWhen to use: shell pipelines, parallel execution (& and wait), file processing, text manipulation.\n\n${EXAMPLES}\n`.trim();\n\nexport const BashProgrammaticToolCallingDefinition = {\n name: BashProgrammaticToolCallingName,\n description: BashProgrammaticToolCallingDescription,\n schema: BashProgrammaticToolCallingSchema,\n} as const;\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Normalizes a tool name to a valid bash function identifier.\n * 1. Replace hyphens, spaces, dots with underscores\n * 2. Remove any other invalid characters\n * 3. Prefix with underscore if starts with number\n * 4. Append `_tool` if it's a bash reserved word\n */\nexport function normalizeToBashIdentifier(name: string): string {\n let normalized = name.replace(/[-\\s.]/g, '_');\n normalized = normalized.replace(/[^a-zA-Z0-9_]/g, '');\n\n if (/^[0-9]/.test(normalized)) {\n normalized = '_' + normalized;\n }\n\n if (BASH_RESERVED.has(normalized)) {\n normalized = normalized + '_tool';\n }\n\n return normalized;\n}\n\n/**\n * Extracts tool names that are actually called in the bash code.\n * Bash functions are invoked as commands (no parentheses), so we match\n * the normalized name as a word boundary.\n */\nexport function extractUsedBashToolNames(\n code: string,\n toolNameMap: Map<string, string>\n): Set<string> {\n const usedTools = new Set<string>();\n\n for (const [bashName, originalName] of toolNameMap) {\n const escapedName = bashName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(`\\\\b${escapedName}\\\\b`, 'g');\n\n if (pattern.test(code)) {\n usedTools.add(originalName);\n }\n }\n\n return usedTools;\n}\n\n/**\n * Filters tool definitions to only include tools actually used in the bash code.\n */\nexport function filterBashToolsByUsage(\n toolDefs: t.LCTool[],\n code: string,\n debug = false\n): t.LCTool[] {\n const toolNameMap = new Map<string, string>();\n for (const def of toolDefs) {\n const bashName = normalizeToBashIdentifier(def.name);\n toolNameMap.set(bashName, def.name);\n }\n\n const usedToolNames = extractUsedBashToolNames(code, toolNameMap);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Tool filtering: found ${usedToolNames.size}/${toolDefs.length} tools in code`\n );\n if (usedToolNames.size > 0) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Matched tools: ${Array.from(usedToolNames).join(', ')}`\n );\n }\n }\n\n if (usedToolNames.size === 0) {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n '[BashPTC Debug] No tools detected in code - sending all tools as fallback'\n );\n }\n return toolDefs;\n }\n\n return toolDefs.filter((def) => usedToolNames.has(def.name));\n}\n\n// ============================================================================\n// Tool Factory\n// ============================================================================\n\n/**\n * Creates a Bash Programmatic Tool Calling tool for multi-tool orchestration.\n *\n * This tool enables AI agents to write bash scripts that orchestrate multiple\n * tool calls programmatically via the remote Code API, reducing LLM round-trips.\n *\n * The tool map must be provided at runtime via config.toolCall (injected by ToolNode).\n */\nexport function createBashProgrammaticToolCallingTool(\n initParams: t.BashProgrammaticToolCallingParams = {}\n): DynamicStructuredTool {\n const baseUrl = initParams.baseUrl ?? getCodeBaseURL();\n const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;\n const maxRunTimeoutMs = resolveCodeApiRunTimeoutMs(initParams.runTimeoutMs);\n const proxy = initParams.proxy ?? process.env.PROXY;\n const debug = initParams.debug ?? process.env.BASH_PTC_DEBUG === 'true';\n const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;\n\n return tool(\n async (rawParams, config) => {\n const params = rawParams as { code: string; timeout?: number };\n const { code } = params;\n const timeout = clampCodeApiRunTimeoutMs(params.timeout, maxRunTimeoutMs);\n\n const toolCall = (config.toolCall ?? {}) as ToolCall &\n Partial<t.ProgrammaticCache> & {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n const { toolMap, toolDefs, session_id, _injected_files } = toolCall;\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error(\n 'No toolMap provided. ' +\n 'ToolNode should inject this from AgentContext when invoked through the graph.'\n );\n }\n\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error(\n 'No tool definitions provided. ' +\n 'Either pass tools in the input or ensure ToolNode injects toolDefs.'\n );\n }\n\n let roundTrip = 0;\n\n try {\n // ====================================================================\n // Phase 1: Filter tools and make initial request\n // ====================================================================\n\n const effectiveTools = filterBashToolsByUsage(toolDefs, code, debug);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Sending ${effectiveTools.length} tools to API ` +\n `(filtered from ${toolDefs.length})`\n );\n }\n\n /* `/files/<session_id>` HTTP fallback removed — codeapi's\n * sessionAuth requires kind/id query params unavailable at\n * this point. See `CodeExecutor.ts` for full rationale. */\n let files: t.CodeEnvFile[] | undefined;\n if (_injected_files && _injected_files.length > 0) {\n files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n // eslint-disable-next-line no-console\n console.debug(\n `[BashProgrammaticToolCalling] No injected files for session_id=${session_id} — exec will run without input files`\n );\n }\n\n let response = await makeRequest(\n EXEC_ENDPOINT,\n {\n lang: 'bash',\n code,\n tools: effectiveTools,\n session_id,\n timeout,\n ...(files && files.length > 0 ? { files } : {}),\n },\n proxy,\n initParams.authHeaders\n );\n\n // ====================================================================\n // Phase 2: Handle response loop\n // ====================================================================\n\n while (response.status === 'tool_call_required') {\n roundTrip++;\n\n if (roundTrip > maxRoundTrips) {\n throw new Error(\n `Exceeded maximum round trips (${maxRoundTrips}). ` +\n 'This may indicate an infinite loop, excessive tool calls, ' +\n 'or a logic error in your code.'\n );\n }\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`\n );\n }\n\n const toolResults = await executeTools(\n response.tool_calls ?? [],\n toolMap,\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING\n );\n\n response = await makeRequest(\n EXEC_ENDPOINT,\n {\n continuation_token: response.continuation_token,\n tool_results: toolResults,\n },\n proxy,\n initParams.authHeaders\n );\n }\n\n // ====================================================================\n // Phase 3: Handle final state\n // ====================================================================\n\n if (response.status === 'completed') {\n return formatCompletedResponse(response, code);\n }\n\n if (response.status === 'error') {\n throw new Error(\n `Execution error: ${response.error}` +\n (response.stderr != null && response.stderr !== ''\n ? `\\n\\nStderr:\\n${response.stderr}`\n : '')\n );\n }\n\n throw new Error(`Unexpected response status: ${response.status}`);\n } catch (error) {\n const messageWithReminder = appendFailedExecutionFileReminder(\n (error as Error).message,\n code\n );\n throw new Error(\n `Bash programmatic execution failed: ${messageWithReminder}`\n );\n }\n },\n {\n name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n description: BashProgrammaticToolCallingDescription,\n schema: createBashProgrammaticToolCallingSchema(maxRunTimeoutMs),\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":["config","resolveCodeApiRunTimeoutMs","CODE_ARTIFACT_PATH_GUIDANCE","BASH_SHELL_GUIDANCE","createCodeApiRunTimeoutSchema","Constants","getCodeBaseURL","tool","clampCodeApiRunTimeoutMs","makeRequest","executeTools","formatCompletedResponse","appendFailedExecutionFileReminder"],"mappings":"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"}
1
+ {"version":3,"file":"BashProgrammaticToolCalling.cjs","sources":["../../../src/tools/BashProgrammaticToolCalling.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { ProgrammaticToolCallingJsonSchema } from './ptcTimeout';\nimport type * as t from '@/types';\nimport {\n makeRequest,\n executeTools,\n formatCompletedResponse,\n} from './ProgrammaticToolCalling';\nimport {\n BASH_SHELL_GUIDANCE,\n CODE_ARTIFACT_PATH_GUIDANCE,\n appendFailedExecutionFileReminder,\n getCodeBaseURL,\n} from './CodeExecutor';\nimport {\n clampCodeApiRunTimeoutMs,\n createCodeApiRunTimeoutSchema,\n resolveCodeApiRunTimeoutMs,\n} from './ptcTimeout';\nimport { Constants } from '@/common';\n\nconfig();\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_MAX_ROUND_TRIPS = 20;\nconst DEFAULT_RUN_TIMEOUT_MS = resolveCodeApiRunTimeoutMs();\n\n/** Bash reserved words that get `_tool` suffix when used as function names */\nconst BASH_RESERVED = new Set([\n 'if',\n 'then',\n 'else',\n 'elif',\n 'fi',\n 'case',\n 'esac',\n 'for',\n 'while',\n 'until',\n 'do',\n 'done',\n 'in',\n 'function',\n 'select',\n 'time',\n 'coproc',\n 'declare',\n 'typeset',\n 'local',\n 'readonly',\n 'export',\n 'unset',\n]);\n\n// ============================================================================\n// Description Components\n// ============================================================================\n\nconst STATELESS_WARNING = `CRITICAL - STATELESS EXECUTION:\nEach call is a fresh bash shell. Variables and state do NOT persist between calls.\nYou MUST complete your entire workflow in ONE code block.\nDO NOT split work across multiple calls expecting to reuse variables.`;\n\nconst CORE_RULES = `Rules:\n- One call: state does not persist\n- Tools are pre-defined as bash functions—DO NOT redefine them\n- Each tool function accepts a JSON string argument\n- Save tool output with raw=$(tool '{}'); printf '%s\\n' \"$raw\" > /mnt/data/file.json; direct tool > file may be empty\n- Tool stdout is normalized to one compact JSON value when possible; parse saved stdout once, then use fromjson? // . only for JSON-string fields\n- Only echo/printf output returns to the model\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\n- timeout caps one sandbox run/replay iteration, not the total multi-round-trip workflow`;\n\nconst ADDITIONAL_RULES =\n '- Tool names normalized: hyphens→underscores, reserved words get `_tool` suffix';\n\nconst EXAMPLES = `Example (Complete workflow in one call):\n # Query data and process\n data=$(query_database '{\"sql\": \"SELECT * FROM users\"}')\n echo \"$data\" | jq '.[] | .name'\n\nExample (Parallel calls):\n { sf=$(web_search '{\"query\": \"SF weather\"}'); printf '%s\\n' \"$sf\" > /mnt/data/sf.json; } &\n { ny=$(web_search '{\"query\": \"NY weather\"}'); printf '%s\\n' \"$ny\" > /mnt/data/ny.json; } &\n wait\n echo \"SF: $(jq -r . /mnt/data/sf.json)\"\n echo \"NY: $(jq -r . /mnt/data/ny.json)\"`;\n\nconst CODE_PARAM_DESCRIPTION = `Bash code that calls tools programmatically. Tools are available as bash functions.\n\n${STATELESS_WARNING}\n\nEach tool function accepts a JSON string as its argument.\nExample: tool_name '{\"key\": \"value\"}'\n\n${EXAMPLES}\n\n${CORE_RULES}`;\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nexport function createBashProgrammaticToolCallingSchema(\n maxRunTimeoutMs = DEFAULT_RUN_TIMEOUT_MS\n): ProgrammaticToolCallingJsonSchema {\n return {\n type: 'object',\n properties: {\n code: {\n type: 'string',\n minLength: 1,\n description: CODE_PARAM_DESCRIPTION,\n },\n timeout: createCodeApiRunTimeoutSchema(maxRunTimeoutMs),\n },\n required: ['code'],\n } as const;\n}\n\nexport const BashProgrammaticToolCallingSchema =\n createBashProgrammaticToolCallingSchema();\n\nexport const BashProgrammaticToolCallingName =\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING;\n\nexport const BashProgrammaticToolCallingDescription = `\nRun tools via bash code. Tools are available as bash functions that accept JSON string arguments.\n\n${STATELESS_WARNING}\n\n${CORE_RULES}\n${ADDITIONAL_RULES}\n\nWhen to use: shell pipelines, parallel execution (& and wait), file processing, text manipulation.\n\n${EXAMPLES}\n`.trim();\n\nexport const BashProgrammaticToolCallingDefinition = {\n name: BashProgrammaticToolCallingName,\n description: BashProgrammaticToolCallingDescription,\n schema: BashProgrammaticToolCallingSchema,\n} as const;\n\nfunction maybeParseJsonResultString(result: unknown): unknown {\n if (typeof result !== 'string') {\n return result;\n }\n\n const trimmed = result.trim();\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n return result;\n }\n\n try {\n return JSON.parse(trimmed) as unknown;\n } catch {\n return result;\n }\n}\n\nexport function normalizeBashToolResultsForReplay(\n toolResults: t.PTCToolResult[]\n): t.PTCToolResult[] {\n return toolResults.map((toolResult) => {\n if (toolResult.is_error) {\n return toolResult;\n }\n\n return {\n ...toolResult,\n result: maybeParseJsonResultString(toolResult.result),\n };\n });\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Normalizes a tool name to a valid bash function identifier.\n * 1. Replace hyphens, spaces, dots with underscores\n * 2. Remove any other invalid characters\n * 3. Prefix with underscore if starts with number\n * 4. Append `_tool` if it's a bash reserved word\n */\nexport function normalizeToBashIdentifier(name: string): string {\n let normalized = name.replace(/[-\\s.]/g, '_');\n normalized = normalized.replace(/[^a-zA-Z0-9_]/g, '');\n\n if (/^[0-9]/.test(normalized)) {\n normalized = '_' + normalized;\n }\n\n if (BASH_RESERVED.has(normalized)) {\n normalized = normalized + '_tool';\n }\n\n return normalized;\n}\n\n/**\n * Extracts tool names that are actually called in the bash code.\n * Bash functions are invoked as commands (no parentheses), so we match\n * the normalized name as a word boundary.\n */\nexport function extractUsedBashToolNames(\n code: string,\n toolNameMap: Map<string, string>\n): Set<string> {\n const usedTools = new Set<string>();\n\n for (const [bashName, originalName] of toolNameMap) {\n const escapedName = bashName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(`\\\\b${escapedName}\\\\b`, 'g');\n\n if (pattern.test(code)) {\n usedTools.add(originalName);\n }\n }\n\n return usedTools;\n}\n\n/**\n * Filters tool definitions to only include tools actually used in the bash code.\n */\nexport function filterBashToolsByUsage(\n toolDefs: t.LCTool[],\n code: string,\n debug = false\n): t.LCTool[] {\n const toolNameMap = new Map<string, string>();\n for (const def of toolDefs) {\n const bashName = normalizeToBashIdentifier(def.name);\n toolNameMap.set(bashName, def.name);\n }\n\n const usedToolNames = extractUsedBashToolNames(code, toolNameMap);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Tool filtering: found ${usedToolNames.size}/${toolDefs.length} tools in code`\n );\n if (usedToolNames.size > 0) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Matched tools: ${Array.from(usedToolNames).join(', ')}`\n );\n }\n }\n\n if (usedToolNames.size === 0) {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n '[BashPTC Debug] No tools detected in code - sending all tools as fallback'\n );\n }\n return toolDefs;\n }\n\n return toolDefs.filter((def) => usedToolNames.has(def.name));\n}\n\n// ============================================================================\n// Tool Factory\n// ============================================================================\n\n/**\n * Creates a Bash Programmatic Tool Calling tool for multi-tool orchestration.\n *\n * This tool enables AI agents to write bash scripts that orchestrate multiple\n * tool calls programmatically via the remote Code API, reducing LLM round-trips.\n *\n * The tool map must be provided at runtime via config.toolCall (injected by ToolNode).\n */\nexport function createBashProgrammaticToolCallingTool(\n initParams: t.BashProgrammaticToolCallingParams = {}\n): DynamicStructuredTool {\n const baseUrl = initParams.baseUrl ?? getCodeBaseURL();\n const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;\n const maxRunTimeoutMs = resolveCodeApiRunTimeoutMs(initParams.runTimeoutMs);\n const proxy = initParams.proxy ?? process.env.PROXY;\n const debug = initParams.debug ?? process.env.BASH_PTC_DEBUG === 'true';\n const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;\n\n return tool(\n async (rawParams, config) => {\n const params = rawParams as { code: string; timeout?: number };\n const { code } = params;\n const timeout = clampCodeApiRunTimeoutMs(params.timeout, maxRunTimeoutMs);\n\n const toolCall = (config.toolCall ?? {}) as ToolCall &\n Partial<t.ProgrammaticCache> & {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n const { toolMap, toolDefs, session_id, _injected_files } = toolCall;\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error(\n 'No toolMap provided. ' +\n 'ToolNode should inject this from AgentContext when invoked through the graph.'\n );\n }\n\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error(\n 'No tool definitions provided. ' +\n 'Either pass tools in the input or ensure ToolNode injects toolDefs.'\n );\n }\n\n let roundTrip = 0;\n\n try {\n // ====================================================================\n // Phase 1: Filter tools and make initial request\n // ====================================================================\n\n const effectiveTools = filterBashToolsByUsage(toolDefs, code, debug);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Sending ${effectiveTools.length} tools to API ` +\n `(filtered from ${toolDefs.length})`\n );\n }\n\n /* `/files/<session_id>` HTTP fallback removed — codeapi's\n * sessionAuth requires kind/id query params unavailable at\n * this point. See `CodeExecutor.ts` for full rationale. */\n let files: t.CodeEnvFile[] | undefined;\n if (_injected_files && _injected_files.length > 0) {\n files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n // eslint-disable-next-line no-console\n console.debug(\n `[BashProgrammaticToolCalling] No injected files for session_id=${session_id} — exec will run without input files`\n );\n }\n\n let response = await makeRequest(\n EXEC_ENDPOINT,\n {\n lang: 'bash',\n code,\n tools: effectiveTools,\n session_id,\n timeout,\n ...(files && files.length > 0 ? { files } : {}),\n },\n proxy,\n initParams.authHeaders\n );\n\n // ====================================================================\n // Phase 2: Handle response loop\n // ====================================================================\n\n while (response.status === 'tool_call_required') {\n roundTrip++;\n\n if (roundTrip > maxRoundTrips) {\n throw new Error(\n `Exceeded maximum round trips (${maxRoundTrips}). ` +\n 'This may indicate an infinite loop, excessive tool calls, ' +\n 'or a logic error in your code.'\n );\n }\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`\n );\n }\n\n const toolResults = normalizeBashToolResultsForReplay(\n await executeTools(\n response.tool_calls ?? [],\n toolMap,\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING\n )\n );\n\n response = await makeRequest(\n EXEC_ENDPOINT,\n {\n continuation_token: response.continuation_token,\n tool_results: toolResults,\n },\n proxy,\n initParams.authHeaders\n );\n }\n\n // ====================================================================\n // Phase 3: Handle final state\n // ====================================================================\n\n if (response.status === 'completed') {\n return formatCompletedResponse(response, code);\n }\n\n if (response.status === 'error') {\n throw new Error(\n `Execution error: ${response.error}` +\n (response.stderr != null && response.stderr !== ''\n ? `\\n\\nStderr:\\n${response.stderr}`\n : '')\n );\n }\n\n throw new Error(`Unexpected response status: ${response.status}`);\n } catch (error) {\n const messageWithReminder = appendFailedExecutionFileReminder(\n (error as Error).message,\n code\n );\n throw new Error(\n `Bash programmatic execution failed: ${messageWithReminder}`\n );\n }\n },\n {\n name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n description: BashProgrammaticToolCallingDescription,\n schema: createBashProgrammaticToolCallingSchema(maxRunTimeoutMs),\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":["config","resolveCodeApiRunTimeoutMs","CODE_ARTIFACT_PATH_GUIDANCE","BASH_SHELL_GUIDANCE","createCodeApiRunTimeoutSchema","Constants","getCodeBaseURL","tool","clampCodeApiRunTimeoutMs","makeRequest","executeTools","formatCompletedResponse","appendFailedExecutionFileReminder"],"mappings":"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"}
@@ -4,6 +4,7 @@ var _enum = require('../common/enum.cjs');
4
4
 
5
5
  const DEFAULT_CODE_API_RUN_TIMEOUT_MS = 15_000;
6
6
  const MIN_CODE_API_RUN_TIMEOUT_MS = 1_000;
7
+ const MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS = 300_000;
7
8
  function normalizeTimeoutMs(value) {
8
9
  if (value == null || !Number.isFinite(value)) {
9
10
  return undefined;
@@ -36,19 +37,24 @@ function clampCodeApiRunTimeoutMs(timeoutMs, maxRunTimeoutMs = resolveCodeApiRun
36
37
  }
37
38
  function createCodeApiRunTimeoutSchema(maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()) {
38
39
  const normalizedMaxRunTimeoutMs = normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;
40
+ const normalizedSchemaMaxRunTimeoutMs = Math.max(normalizedMaxRunTimeoutMs, MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS);
39
41
  const formattedTimeout = formatTimeout(normalizedMaxRunTimeoutMs);
42
+ const formattedSchemaMaxTimeout = formatTimeout(normalizedSchemaMaxRunTimeoutMs);
40
43
  return {
41
44
  type: 'integer',
42
45
  minimum: MIN_CODE_API_RUN_TIMEOUT_MS,
43
- maximum: normalizedMaxRunTimeoutMs,
46
+ maximum: normalizedSchemaMaxRunTimeoutMs,
44
47
  default: normalizedMaxRunTimeoutMs,
45
48
  description: 'Maximum wall-clock time in milliseconds for one sandbox run or replay iteration. ' +
46
49
  'This is not the total multi-round-trip task budget. ' +
47
- `Default: ${formattedTimeout}. Max: ${formattedTimeout}.`,
50
+ `Default: ${formattedTimeout}. ` +
51
+ 'Accepted values above the configured cap are clamped before execution. ' +
52
+ `Schema max: ${formattedSchemaMaxTimeout}. Configured cap: ${formattedTimeout}.`,
48
53
  };
49
54
  }
50
55
 
51
56
  exports.DEFAULT_CODE_API_RUN_TIMEOUT_MS = DEFAULT_CODE_API_RUN_TIMEOUT_MS;
57
+ exports.MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS = MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS;
52
58
  exports.MIN_CODE_API_RUN_TIMEOUT_MS = MIN_CODE_API_RUN_TIMEOUT_MS;
53
59
  exports.clampCodeApiRunTimeoutMs = clampCodeApiRunTimeoutMs;
54
60
  exports.createCodeApiRunTimeoutSchema = createCodeApiRunTimeoutSchema;
@@ -1 +1 @@
1
- {"version":3,"file":"ptcTimeout.cjs","sources":["../../../src/tools/ptcTimeout.ts"],"sourcesContent":["import { EnvVar } from '@/common';\n\nexport const DEFAULT_CODE_API_RUN_TIMEOUT_MS = 15_000;\nexport const MIN_CODE_API_RUN_TIMEOUT_MS = 1_000;\n\ntype TimeoutSchema = {\n type: 'integer';\n minimum: number;\n maximum: number;\n default: number;\n description: string;\n};\n\nexport type ProgrammaticToolCallingJsonSchema = {\n type: 'object';\n properties: {\n code: {\n type: 'string';\n minLength: number;\n description: string;\n };\n timeout: TimeoutSchema;\n };\n required: readonly ['code'];\n};\n\nfunction normalizeTimeoutMs(value: number | undefined): number | undefined {\n if (value == null || !Number.isFinite(value)) {\n return undefined;\n }\n\n return Math.max(MIN_CODE_API_RUN_TIMEOUT_MS, Math.floor(value));\n}\n\nfunction parseTimeoutMs(value: string | undefined): number | undefined {\n if (value == null || value.trim() === '') {\n return undefined;\n }\n\n return normalizeTimeoutMs(Number(value));\n}\n\nfunction formatTimeout(timeoutMs: number): string {\n return timeoutMs % 1000 === 0\n ? `${timeoutMs / 1000} seconds`\n : `${timeoutMs} milliseconds`;\n}\n\nexport function resolveCodeApiRunTimeoutMs(override?: number): number {\n return (\n normalizeTimeoutMs(override) ??\n parseTimeoutMs(process.env[EnvVar.CODE_API_RUN_TIMEOUT_MS]) ??\n DEFAULT_CODE_API_RUN_TIMEOUT_MS\n );\n}\n\nexport function clampCodeApiRunTimeoutMs(\n timeoutMs: number | undefined,\n maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()\n): number {\n const normalizedMaxRunTimeoutMs =\n normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;\n const normalizedTimeoutMs = normalizeTimeoutMs(timeoutMs);\n\n if (normalizedTimeoutMs == null) {\n return normalizedMaxRunTimeoutMs;\n }\n\n return Math.min(normalizedTimeoutMs, normalizedMaxRunTimeoutMs);\n}\n\nexport function createCodeApiRunTimeoutSchema(\n maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()\n): TimeoutSchema {\n const normalizedMaxRunTimeoutMs =\n normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;\n const formattedTimeout = formatTimeout(normalizedMaxRunTimeoutMs);\n\n return {\n type: 'integer',\n minimum: MIN_CODE_API_RUN_TIMEOUT_MS,\n maximum: normalizedMaxRunTimeoutMs,\n default: normalizedMaxRunTimeoutMs,\n description:\n 'Maximum wall-clock time in milliseconds for one sandbox run or replay iteration. ' +\n 'This is not the total multi-round-trip task budget. ' +\n `Default: ${formattedTimeout}. Max: ${formattedTimeout}.`,\n };\n}\n"],"names":["EnvVar"],"mappings":";;;;AAEO,MAAM,+BAA+B,GAAG;AACxC,MAAM,2BAA2B,GAAG;AAuB3C,SAAS,kBAAkB,CAAC,KAAyB,EAAA;AACnD,IAAA,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AAC5C,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,2BAA2B,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACjE;AAEA,SAAS,cAAc,CAAC,KAAyB,EAAA;IAC/C,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;AACxC,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,OAAO,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1C;AAEA,SAAS,aAAa,CAAC,SAAiB,EAAA;AACtC,IAAA,OAAO,SAAS,GAAG,IAAI,KAAK;AAC1B,UAAE,CAAA,EAAG,SAAS,GAAG,IAAI,CAAA,QAAA;AACrB,UAAE,CAAA,EAAG,SAAS,CAAA,aAAA,CAAe;AACjC;AAEM,SAAU,0BAA0B,CAAC,QAAiB,EAAA;AAC1D,IAAA,QACE,kBAAkB,CAAC,QAAQ,CAAC;QAC5B,cAAc,CAAC,OAAO,CAAC,GAAG,CAACA,YAAM,CAAC,uBAAuB,CAAC,CAAC;AAC3D,QAAA,+BAA+B;AAEnC;AAEM,SAAU,wBAAwB,CACtC,SAA6B,EAC7B,eAAe,GAAG,0BAA0B,EAAE,EAAA;IAE9C,MAAM,yBAAyB,GAC7B,kBAAkB,CAAC,eAAe,CAAC,IAAI,+BAA+B;AACxE,IAAA,MAAM,mBAAmB,GAAG,kBAAkB,CAAC,SAAS,CAAC;AAEzD,IAAA,IAAI,mBAAmB,IAAI,IAAI,EAAE;AAC/B,QAAA,OAAO,yBAAyB;IAClC;IAEA,OAAO,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,yBAAyB,CAAC;AACjE;SAEgB,6BAA6B,CAC3C,eAAe,GAAG,0BAA0B,EAAE,EAAA;IAE9C,MAAM,yBAAyB,GAC7B,kBAAkB,CAAC,eAAe,CAAC,IAAI,+BAA+B;AACxE,IAAA,MAAM,gBAAgB,GAAG,aAAa,CAAC,yBAAyB,CAAC;IAEjE,OAAO;AACL,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,OAAO,EAAE,2BAA2B;AACpC,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,WAAW,EACT,mFAAmF;YACnF,sDAAsD;YACtD,CAAA,SAAA,EAAY,gBAAgB,CAAA,OAAA,EAAU,gBAAgB,CAAA,CAAA,CAAG;KAC5D;AACH;;;;;;;;"}
1
+ {"version":3,"file":"ptcTimeout.cjs","sources":["../../../src/tools/ptcTimeout.ts"],"sourcesContent":["import { EnvVar } from '@/common';\n\nexport const DEFAULT_CODE_API_RUN_TIMEOUT_MS = 15_000;\nexport const MIN_CODE_API_RUN_TIMEOUT_MS = 1_000;\nexport const MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS = 300_000;\n\ntype TimeoutSchema = {\n type: 'integer';\n minimum: number;\n maximum: number;\n default: number;\n description: string;\n};\n\nexport type ProgrammaticToolCallingJsonSchema = {\n type: 'object';\n properties: {\n code: {\n type: 'string';\n minLength: number;\n description: string;\n };\n timeout: TimeoutSchema;\n };\n required: readonly ['code'];\n};\n\nfunction normalizeTimeoutMs(value: number | undefined): number | undefined {\n if (value == null || !Number.isFinite(value)) {\n return undefined;\n }\n\n return Math.max(MIN_CODE_API_RUN_TIMEOUT_MS, Math.floor(value));\n}\n\nfunction parseTimeoutMs(value: string | undefined): number | undefined {\n if (value == null || value.trim() === '') {\n return undefined;\n }\n\n return normalizeTimeoutMs(Number(value));\n}\n\nfunction formatTimeout(timeoutMs: number): string {\n return timeoutMs % 1000 === 0\n ? `${timeoutMs / 1000} seconds`\n : `${timeoutMs} milliseconds`;\n}\n\nexport function resolveCodeApiRunTimeoutMs(override?: number): number {\n return (\n normalizeTimeoutMs(override) ??\n parseTimeoutMs(process.env[EnvVar.CODE_API_RUN_TIMEOUT_MS]) ??\n DEFAULT_CODE_API_RUN_TIMEOUT_MS\n );\n}\n\nexport function clampCodeApiRunTimeoutMs(\n timeoutMs: number | undefined,\n maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()\n): number {\n const normalizedMaxRunTimeoutMs =\n normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;\n const normalizedTimeoutMs = normalizeTimeoutMs(timeoutMs);\n\n if (normalizedTimeoutMs == null) {\n return normalizedMaxRunTimeoutMs;\n }\n\n return Math.min(normalizedTimeoutMs, normalizedMaxRunTimeoutMs);\n}\n\nexport function createCodeApiRunTimeoutSchema(\n maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()\n): TimeoutSchema {\n const normalizedMaxRunTimeoutMs =\n normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;\n const normalizedSchemaMaxRunTimeoutMs = Math.max(\n normalizedMaxRunTimeoutMs,\n MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS\n );\n const formattedTimeout = formatTimeout(normalizedMaxRunTimeoutMs);\n const formattedSchemaMaxTimeout = formatTimeout(\n normalizedSchemaMaxRunTimeoutMs\n );\n\n return {\n type: 'integer',\n minimum: MIN_CODE_API_RUN_TIMEOUT_MS,\n maximum: normalizedSchemaMaxRunTimeoutMs,\n default: normalizedMaxRunTimeoutMs,\n description:\n 'Maximum wall-clock time in milliseconds for one sandbox run or replay iteration. ' +\n 'This is not the total multi-round-trip task budget. ' +\n `Default: ${formattedTimeout}. ` +\n 'Accepted values above the configured cap are clamped before execution. ' +\n `Schema max: ${formattedSchemaMaxTimeout}. Configured cap: ${formattedTimeout}.`,\n };\n}\n"],"names":["EnvVar"],"mappings":";;;;AAEO,MAAM,+BAA+B,GAAG;AACxC,MAAM,2BAA2B,GAAG;AACpC,MAAM,kCAAkC,GAAG;AAuBlD,SAAS,kBAAkB,CAAC,KAAyB,EAAA;AACnD,IAAA,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AAC5C,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,2BAA2B,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACjE;AAEA,SAAS,cAAc,CAAC,KAAyB,EAAA;IAC/C,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;AACxC,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,OAAO,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1C;AAEA,SAAS,aAAa,CAAC,SAAiB,EAAA;AACtC,IAAA,OAAO,SAAS,GAAG,IAAI,KAAK;AAC1B,UAAE,CAAA,EAAG,SAAS,GAAG,IAAI,CAAA,QAAA;AACrB,UAAE,CAAA,EAAG,SAAS,CAAA,aAAA,CAAe;AACjC;AAEM,SAAU,0BAA0B,CAAC,QAAiB,EAAA;AAC1D,IAAA,QACE,kBAAkB,CAAC,QAAQ,CAAC;QAC5B,cAAc,CAAC,OAAO,CAAC,GAAG,CAACA,YAAM,CAAC,uBAAuB,CAAC,CAAC;AAC3D,QAAA,+BAA+B;AAEnC;AAEM,SAAU,wBAAwB,CACtC,SAA6B,EAC7B,eAAe,GAAG,0BAA0B,EAAE,EAAA;IAE9C,MAAM,yBAAyB,GAC7B,kBAAkB,CAAC,eAAe,CAAC,IAAI,+BAA+B;AACxE,IAAA,MAAM,mBAAmB,GAAG,kBAAkB,CAAC,SAAS,CAAC;AAEzD,IAAA,IAAI,mBAAmB,IAAI,IAAI,EAAE;AAC/B,QAAA,OAAO,yBAAyB;IAClC;IAEA,OAAO,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,yBAAyB,CAAC;AACjE;SAEgB,6BAA6B,CAC3C,eAAe,GAAG,0BAA0B,EAAE,EAAA;IAE9C,MAAM,yBAAyB,GAC7B,kBAAkB,CAAC,eAAe,CAAC,IAAI,+BAA+B;IACxE,MAAM,+BAA+B,GAAG,IAAI,CAAC,GAAG,CAC9C,yBAAyB,EACzB,kCAAkC,CACnC;AACD,IAAA,MAAM,gBAAgB,GAAG,aAAa,CAAC,yBAAyB,CAAC;AACjE,IAAA,MAAM,yBAAyB,GAAG,aAAa,CAC7C,+BAA+B,CAChC;IAED,OAAO;AACL,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,OAAO,EAAE,2BAA2B;AACpC,QAAA,OAAO,EAAE,+BAA+B;AACxC,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,WAAW,EACT,mFAAmF;YACnF,sDAAsD;AACtD,YAAA,CAAA,SAAA,EAAY,gBAAgB,CAAA,EAAA,CAAI;YAChC,yEAAyE;YACzE,CAAA,YAAA,EAAe,yBAAyB,CAAA,kBAAA,EAAqB,gBAAgB,CAAA,CAAA,CAAG;KACnF;AACH;;;;;;;;;"}
package/dist/esm/main.mjs CHANGED
@@ -21,7 +21,7 @@ export { Calculator, CalculatorSchema, CalculatorToolDefinition, CalculatorToolD
21
21
  export { BASH_SHELL_GUIDANCE, CODE_ARTIFACT_PATH_GUIDANCE, CodeExecutionToolDefinition, CodeExecutionToolDescription, CodeExecutionToolName, CodeExecutionToolSchema, FAILED_EXECUTION_FILE_REMINDER, TMP_SCRATCH_OUTPUT_REMINDER, appendFailedExecutionFileReminder, appendTmpScratchReminder, buildCodeApiHttpErrorMessage, createCodeExecutionTool, emptyOutputMessage, getCodeBaseURL, resolveCodeApiAuthHeaders } from './tools/CodeExecutor.mjs';
22
22
  export { BashExecutionToolDefinition, BashExecutionToolDescription, BashExecutionToolName, BashExecutionToolSchema, BashToolOutputReferencesGuide, buildBashExecutionToolDescription, createBashExecutionTool } from './tools/BashExecutor.mjs';
23
23
  export { ProgrammaticToolCallingDefinition, ProgrammaticToolCallingDescription, ProgrammaticToolCallingName, ProgrammaticToolCallingSchema, createProgrammaticToolCallingSchema, createProgrammaticToolCallingTool, executeTools, extractUsedToolNames, fetchSessionFiles, filterToolsByUsage, formatCompletedResponse, makeRequest, normalizeToPythonIdentifier, unwrapToolResponse } from './tools/ProgrammaticToolCalling.mjs';
24
- export { BashProgrammaticToolCallingDefinition, BashProgrammaticToolCallingDescription, BashProgrammaticToolCallingName, BashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingTool, extractUsedBashToolNames, filterBashToolsByUsage, normalizeToBashIdentifier } from './tools/BashProgrammaticToolCalling.mjs';
24
+ export { BashProgrammaticToolCallingDefinition, BashProgrammaticToolCallingDescription, BashProgrammaticToolCallingName, BashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingTool, extractUsedBashToolNames, filterBashToolsByUsage, normalizeBashToolResultsForReplay, normalizeToBashIdentifier } from './tools/BashProgrammaticToolCalling.mjs';
25
25
  export { SkillToolDefinition, SkillToolDescription, SkillToolName, SkillToolSchema } from './tools/SkillTool.mjs';
26
26
  export { SubagentToolDefinition, SubagentToolDescription, SubagentToolName, SubagentToolSchema, buildSubagentToolParams, createSubagentToolDefinition } from './tools/SubagentTool.mjs';
27
27
  export { SubagentExecutor, buildChildInputs, filterSubagentResult, resolveSubagentConfigs, summarizeEvent } from './tools/subagent/SubagentExecutor.mjs';
@@ -49,7 +49,7 @@ const CORE_RULES = `Rules:
49
49
  - Tools are pre-defined as bash functions—DO NOT redefine them
50
50
  - Each tool function accepts a JSON string argument
51
51
  - Save tool output with raw=$(tool '{}'); printf '%s\n' "$raw" > /mnt/data/file.json; direct tool > file may be empty
52
- - jq: use fromjson? // . on saved tool stdout and again on JSON-string fields; check types since arrays may contain strings
52
+ - Tool stdout is normalized to one compact JSON value when possible; parse saved stdout once, then use fromjson? // . only for JSON-string fields
53
53
  - Only echo/printf output returns to the model
54
54
  - ${CODE_ARTIFACT_PATH_GUIDANCE}
55
55
  - ${BASH_SHELL_GUIDANCE}
@@ -112,6 +112,32 @@ const BashProgrammaticToolCallingDefinition = {
112
112
  description: BashProgrammaticToolCallingDescription,
113
113
  schema: BashProgrammaticToolCallingSchema,
114
114
  };
115
+ function maybeParseJsonResultString(result) {
116
+ if (typeof result !== 'string') {
117
+ return result;
118
+ }
119
+ const trimmed = result.trim();
120
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
121
+ return result;
122
+ }
123
+ try {
124
+ return JSON.parse(trimmed);
125
+ }
126
+ catch {
127
+ return result;
128
+ }
129
+ }
130
+ function normalizeBashToolResultsForReplay(toolResults) {
131
+ return toolResults.map((toolResult) => {
132
+ if (toolResult.is_error) {
133
+ return toolResult;
134
+ }
135
+ return {
136
+ ...toolResult,
137
+ result: maybeParseJsonResultString(toolResult.result),
138
+ };
139
+ });
140
+ }
115
141
  // ============================================================================
116
142
  // Helper Functions
117
143
  // ============================================================================
@@ -252,7 +278,7 @@ function createBashProgrammaticToolCallingTool(initParams = {}) {
252
278
  // eslint-disable-next-line no-console
253
279
  console.log(`[BashPTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`);
254
280
  }
255
- const toolResults = await executeTools(response.tool_calls ?? [], toolMap, Constants.BASH_PROGRAMMATIC_TOOL_CALLING);
281
+ const toolResults = normalizeBashToolResultsForReplay(await executeTools(response.tool_calls ?? [], toolMap, Constants.BASH_PROGRAMMATIC_TOOL_CALLING));
256
282
  response = await makeRequest(EXEC_ENDPOINT, {
257
283
  continuation_token: response.continuation_token,
258
284
  tool_results: toolResults,
@@ -284,5 +310,5 @@ function createBashProgrammaticToolCallingTool(initParams = {}) {
284
310
  });
285
311
  }
286
312
 
287
- export { BashProgrammaticToolCallingDefinition, BashProgrammaticToolCallingDescription, BashProgrammaticToolCallingName, BashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingTool, extractUsedBashToolNames, filterBashToolsByUsage, normalizeToBashIdentifier };
313
+ export { BashProgrammaticToolCallingDefinition, BashProgrammaticToolCallingDescription, BashProgrammaticToolCallingName, BashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingTool, extractUsedBashToolNames, filterBashToolsByUsage, normalizeBashToolResultsForReplay, normalizeToBashIdentifier };
288
314
  //# sourceMappingURL=BashProgrammaticToolCalling.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"BashProgrammaticToolCalling.mjs","sources":["../../../src/tools/BashProgrammaticToolCalling.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { ProgrammaticToolCallingJsonSchema } from './ptcTimeout';\nimport type * as t from '@/types';\nimport {\n makeRequest,\n executeTools,\n formatCompletedResponse,\n} from './ProgrammaticToolCalling';\nimport {\n BASH_SHELL_GUIDANCE,\n CODE_ARTIFACT_PATH_GUIDANCE,\n appendFailedExecutionFileReminder,\n getCodeBaseURL,\n} from './CodeExecutor';\nimport {\n clampCodeApiRunTimeoutMs,\n createCodeApiRunTimeoutSchema,\n resolveCodeApiRunTimeoutMs,\n} from './ptcTimeout';\nimport { Constants } from '@/common';\n\nconfig();\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_MAX_ROUND_TRIPS = 20;\nconst DEFAULT_RUN_TIMEOUT_MS = resolveCodeApiRunTimeoutMs();\n\n/** Bash reserved words that get `_tool` suffix when used as function names */\nconst BASH_RESERVED = new Set([\n 'if',\n 'then',\n 'else',\n 'elif',\n 'fi',\n 'case',\n 'esac',\n 'for',\n 'while',\n 'until',\n 'do',\n 'done',\n 'in',\n 'function',\n 'select',\n 'time',\n 'coproc',\n 'declare',\n 'typeset',\n 'local',\n 'readonly',\n 'export',\n 'unset',\n]);\n\n// ============================================================================\n// Description Components\n// ============================================================================\n\nconst STATELESS_WARNING = `CRITICAL - STATELESS EXECUTION:\nEach call is a fresh bash shell. Variables and state do NOT persist between calls.\nYou MUST complete your entire workflow in ONE code block.\nDO NOT split work across multiple calls expecting to reuse variables.`;\n\nconst CORE_RULES = `Rules:\n- One call: state does not persist\n- Tools are pre-defined as bash functions—DO NOT redefine them\n- Each tool function accepts a JSON string argument\n- Save tool output with raw=$(tool '{}'); printf '%s\\n' \"$raw\" > /mnt/data/file.json; direct tool > file may be empty\n- jq: use fromjson? // . on saved tool stdout and again on JSON-string fields; check types since arrays may contain strings\n- Only echo/printf output returns to the model\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\n- timeout caps one sandbox run/replay iteration, not the total multi-round-trip workflow`;\n\nconst ADDITIONAL_RULES =\n '- Tool names normalized: hyphens→underscores, reserved words get `_tool` suffix';\n\nconst EXAMPLES = `Example (Complete workflow in one call):\n # Query data and process\n data=$(query_database '{\"sql\": \"SELECT * FROM users\"}')\n echo \"$data\" | jq '.[] | .name'\n\nExample (Parallel calls):\n { sf=$(web_search '{\"query\": \"SF weather\"}'); printf '%s\\n' \"$sf\" > /mnt/data/sf.json; } &\n { ny=$(web_search '{\"query\": \"NY weather\"}'); printf '%s\\n' \"$ny\" > /mnt/data/ny.json; } &\n wait\n echo \"SF: $(jq -r . /mnt/data/sf.json)\"\n echo \"NY: $(jq -r . /mnt/data/ny.json)\"`;\n\nconst CODE_PARAM_DESCRIPTION = `Bash code that calls tools programmatically. Tools are available as bash functions.\n\n${STATELESS_WARNING}\n\nEach tool function accepts a JSON string as its argument.\nExample: tool_name '{\"key\": \"value\"}'\n\n${EXAMPLES}\n\n${CORE_RULES}`;\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nexport function createBashProgrammaticToolCallingSchema(\n maxRunTimeoutMs = DEFAULT_RUN_TIMEOUT_MS\n): ProgrammaticToolCallingJsonSchema {\n return {\n type: 'object',\n properties: {\n code: {\n type: 'string',\n minLength: 1,\n description: CODE_PARAM_DESCRIPTION,\n },\n timeout: createCodeApiRunTimeoutSchema(maxRunTimeoutMs),\n },\n required: ['code'],\n } as const;\n}\n\nexport const BashProgrammaticToolCallingSchema =\n createBashProgrammaticToolCallingSchema();\n\nexport const BashProgrammaticToolCallingName =\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING;\n\nexport const BashProgrammaticToolCallingDescription = `\nRun tools via bash code. Tools are available as bash functions that accept JSON string arguments.\n\n${STATELESS_WARNING}\n\n${CORE_RULES}\n${ADDITIONAL_RULES}\n\nWhen to use: shell pipelines, parallel execution (& and wait), file processing, text manipulation.\n\n${EXAMPLES}\n`.trim();\n\nexport const BashProgrammaticToolCallingDefinition = {\n name: BashProgrammaticToolCallingName,\n description: BashProgrammaticToolCallingDescription,\n schema: BashProgrammaticToolCallingSchema,\n} as const;\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Normalizes a tool name to a valid bash function identifier.\n * 1. Replace hyphens, spaces, dots with underscores\n * 2. Remove any other invalid characters\n * 3. Prefix with underscore if starts with number\n * 4. Append `_tool` if it's a bash reserved word\n */\nexport function normalizeToBashIdentifier(name: string): string {\n let normalized = name.replace(/[-\\s.]/g, '_');\n normalized = normalized.replace(/[^a-zA-Z0-9_]/g, '');\n\n if (/^[0-9]/.test(normalized)) {\n normalized = '_' + normalized;\n }\n\n if (BASH_RESERVED.has(normalized)) {\n normalized = normalized + '_tool';\n }\n\n return normalized;\n}\n\n/**\n * Extracts tool names that are actually called in the bash code.\n * Bash functions are invoked as commands (no parentheses), so we match\n * the normalized name as a word boundary.\n */\nexport function extractUsedBashToolNames(\n code: string,\n toolNameMap: Map<string, string>\n): Set<string> {\n const usedTools = new Set<string>();\n\n for (const [bashName, originalName] of toolNameMap) {\n const escapedName = bashName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(`\\\\b${escapedName}\\\\b`, 'g');\n\n if (pattern.test(code)) {\n usedTools.add(originalName);\n }\n }\n\n return usedTools;\n}\n\n/**\n * Filters tool definitions to only include tools actually used in the bash code.\n */\nexport function filterBashToolsByUsage(\n toolDefs: t.LCTool[],\n code: string,\n debug = false\n): t.LCTool[] {\n const toolNameMap = new Map<string, string>();\n for (const def of toolDefs) {\n const bashName = normalizeToBashIdentifier(def.name);\n toolNameMap.set(bashName, def.name);\n }\n\n const usedToolNames = extractUsedBashToolNames(code, toolNameMap);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Tool filtering: found ${usedToolNames.size}/${toolDefs.length} tools in code`\n );\n if (usedToolNames.size > 0) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Matched tools: ${Array.from(usedToolNames).join(', ')}`\n );\n }\n }\n\n if (usedToolNames.size === 0) {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n '[BashPTC Debug] No tools detected in code - sending all tools as fallback'\n );\n }\n return toolDefs;\n }\n\n return toolDefs.filter((def) => usedToolNames.has(def.name));\n}\n\n// ============================================================================\n// Tool Factory\n// ============================================================================\n\n/**\n * Creates a Bash Programmatic Tool Calling tool for multi-tool orchestration.\n *\n * This tool enables AI agents to write bash scripts that orchestrate multiple\n * tool calls programmatically via the remote Code API, reducing LLM round-trips.\n *\n * The tool map must be provided at runtime via config.toolCall (injected by ToolNode).\n */\nexport function createBashProgrammaticToolCallingTool(\n initParams: t.BashProgrammaticToolCallingParams = {}\n): DynamicStructuredTool {\n const baseUrl = initParams.baseUrl ?? getCodeBaseURL();\n const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;\n const maxRunTimeoutMs = resolveCodeApiRunTimeoutMs(initParams.runTimeoutMs);\n const proxy = initParams.proxy ?? process.env.PROXY;\n const debug = initParams.debug ?? process.env.BASH_PTC_DEBUG === 'true';\n const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;\n\n return tool(\n async (rawParams, config) => {\n const params = rawParams as { code: string; timeout?: number };\n const { code } = params;\n const timeout = clampCodeApiRunTimeoutMs(params.timeout, maxRunTimeoutMs);\n\n const toolCall = (config.toolCall ?? {}) as ToolCall &\n Partial<t.ProgrammaticCache> & {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n const { toolMap, toolDefs, session_id, _injected_files } = toolCall;\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error(\n 'No toolMap provided. ' +\n 'ToolNode should inject this from AgentContext when invoked through the graph.'\n );\n }\n\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error(\n 'No tool definitions provided. ' +\n 'Either pass tools in the input or ensure ToolNode injects toolDefs.'\n );\n }\n\n let roundTrip = 0;\n\n try {\n // ====================================================================\n // Phase 1: Filter tools and make initial request\n // ====================================================================\n\n const effectiveTools = filterBashToolsByUsage(toolDefs, code, debug);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Sending ${effectiveTools.length} tools to API ` +\n `(filtered from ${toolDefs.length})`\n );\n }\n\n /* `/files/<session_id>` HTTP fallback removed — codeapi's\n * sessionAuth requires kind/id query params unavailable at\n * this point. See `CodeExecutor.ts` for full rationale. */\n let files: t.CodeEnvFile[] | undefined;\n if (_injected_files && _injected_files.length > 0) {\n files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n // eslint-disable-next-line no-console\n console.debug(\n `[BashProgrammaticToolCalling] No injected files for session_id=${session_id} — exec will run without input files`\n );\n }\n\n let response = await makeRequest(\n EXEC_ENDPOINT,\n {\n lang: 'bash',\n code,\n tools: effectiveTools,\n session_id,\n timeout,\n ...(files && files.length > 0 ? { files } : {}),\n },\n proxy,\n initParams.authHeaders\n );\n\n // ====================================================================\n // Phase 2: Handle response loop\n // ====================================================================\n\n while (response.status === 'tool_call_required') {\n roundTrip++;\n\n if (roundTrip > maxRoundTrips) {\n throw new Error(\n `Exceeded maximum round trips (${maxRoundTrips}). ` +\n 'This may indicate an infinite loop, excessive tool calls, ' +\n 'or a logic error in your code.'\n );\n }\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`\n );\n }\n\n const toolResults = await executeTools(\n response.tool_calls ?? [],\n toolMap,\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING\n );\n\n response = await makeRequest(\n EXEC_ENDPOINT,\n {\n continuation_token: response.continuation_token,\n tool_results: toolResults,\n },\n proxy,\n initParams.authHeaders\n );\n }\n\n // ====================================================================\n // Phase 3: Handle final state\n // ====================================================================\n\n if (response.status === 'completed') {\n return formatCompletedResponse(response, code);\n }\n\n if (response.status === 'error') {\n throw new Error(\n `Execution error: ${response.error}` +\n (response.stderr != null && response.stderr !== ''\n ? `\\n\\nStderr:\\n${response.stderr}`\n : '')\n );\n }\n\n throw new Error(`Unexpected response status: ${response.status}`);\n } catch (error) {\n const messageWithReminder = appendFailedExecutionFileReminder(\n (error as Error).message,\n code\n );\n throw new Error(\n `Bash programmatic execution failed: ${messageWithReminder}`\n );\n }\n },\n {\n name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n description: BashProgrammaticToolCallingDescription,\n schema: createBashProgrammaticToolCallingSchema(maxRunTimeoutMs),\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BashProgrammaticToolCalling.mjs","sources":["../../../src/tools/BashProgrammaticToolCalling.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { ProgrammaticToolCallingJsonSchema } from './ptcTimeout';\nimport type * as t from '@/types';\nimport {\n makeRequest,\n executeTools,\n formatCompletedResponse,\n} from './ProgrammaticToolCalling';\nimport {\n BASH_SHELL_GUIDANCE,\n CODE_ARTIFACT_PATH_GUIDANCE,\n appendFailedExecutionFileReminder,\n getCodeBaseURL,\n} from './CodeExecutor';\nimport {\n clampCodeApiRunTimeoutMs,\n createCodeApiRunTimeoutSchema,\n resolveCodeApiRunTimeoutMs,\n} from './ptcTimeout';\nimport { Constants } from '@/common';\n\nconfig();\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_MAX_ROUND_TRIPS = 20;\nconst DEFAULT_RUN_TIMEOUT_MS = resolveCodeApiRunTimeoutMs();\n\n/** Bash reserved words that get `_tool` suffix when used as function names */\nconst BASH_RESERVED = new Set([\n 'if',\n 'then',\n 'else',\n 'elif',\n 'fi',\n 'case',\n 'esac',\n 'for',\n 'while',\n 'until',\n 'do',\n 'done',\n 'in',\n 'function',\n 'select',\n 'time',\n 'coproc',\n 'declare',\n 'typeset',\n 'local',\n 'readonly',\n 'export',\n 'unset',\n]);\n\n// ============================================================================\n// Description Components\n// ============================================================================\n\nconst STATELESS_WARNING = `CRITICAL - STATELESS EXECUTION:\nEach call is a fresh bash shell. Variables and state do NOT persist between calls.\nYou MUST complete your entire workflow in ONE code block.\nDO NOT split work across multiple calls expecting to reuse variables.`;\n\nconst CORE_RULES = `Rules:\n- One call: state does not persist\n- Tools are pre-defined as bash functions—DO NOT redefine them\n- Each tool function accepts a JSON string argument\n- Save tool output with raw=$(tool '{}'); printf '%s\\n' \"$raw\" > /mnt/data/file.json; direct tool > file may be empty\n- Tool stdout is normalized to one compact JSON value when possible; parse saved stdout once, then use fromjson? // . only for JSON-string fields\n- Only echo/printf output returns to the model\n- ${CODE_ARTIFACT_PATH_GUIDANCE}\n- ${BASH_SHELL_GUIDANCE}\n- timeout caps one sandbox run/replay iteration, not the total multi-round-trip workflow`;\n\nconst ADDITIONAL_RULES =\n '- Tool names normalized: hyphens→underscores, reserved words get `_tool` suffix';\n\nconst EXAMPLES = `Example (Complete workflow in one call):\n # Query data and process\n data=$(query_database '{\"sql\": \"SELECT * FROM users\"}')\n echo \"$data\" | jq '.[] | .name'\n\nExample (Parallel calls):\n { sf=$(web_search '{\"query\": \"SF weather\"}'); printf '%s\\n' \"$sf\" > /mnt/data/sf.json; } &\n { ny=$(web_search '{\"query\": \"NY weather\"}'); printf '%s\\n' \"$ny\" > /mnt/data/ny.json; } &\n wait\n echo \"SF: $(jq -r . /mnt/data/sf.json)\"\n echo \"NY: $(jq -r . /mnt/data/ny.json)\"`;\n\nconst CODE_PARAM_DESCRIPTION = `Bash code that calls tools programmatically. Tools are available as bash functions.\n\n${STATELESS_WARNING}\n\nEach tool function accepts a JSON string as its argument.\nExample: tool_name '{\"key\": \"value\"}'\n\n${EXAMPLES}\n\n${CORE_RULES}`;\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nexport function createBashProgrammaticToolCallingSchema(\n maxRunTimeoutMs = DEFAULT_RUN_TIMEOUT_MS\n): ProgrammaticToolCallingJsonSchema {\n return {\n type: 'object',\n properties: {\n code: {\n type: 'string',\n minLength: 1,\n description: CODE_PARAM_DESCRIPTION,\n },\n timeout: createCodeApiRunTimeoutSchema(maxRunTimeoutMs),\n },\n required: ['code'],\n } as const;\n}\n\nexport const BashProgrammaticToolCallingSchema =\n createBashProgrammaticToolCallingSchema();\n\nexport const BashProgrammaticToolCallingName =\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING;\n\nexport const BashProgrammaticToolCallingDescription = `\nRun tools via bash code. Tools are available as bash functions that accept JSON string arguments.\n\n${STATELESS_WARNING}\n\n${CORE_RULES}\n${ADDITIONAL_RULES}\n\nWhen to use: shell pipelines, parallel execution (& and wait), file processing, text manipulation.\n\n${EXAMPLES}\n`.trim();\n\nexport const BashProgrammaticToolCallingDefinition = {\n name: BashProgrammaticToolCallingName,\n description: BashProgrammaticToolCallingDescription,\n schema: BashProgrammaticToolCallingSchema,\n} as const;\n\nfunction maybeParseJsonResultString(result: unknown): unknown {\n if (typeof result !== 'string') {\n return result;\n }\n\n const trimmed = result.trim();\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n return result;\n }\n\n try {\n return JSON.parse(trimmed) as unknown;\n } catch {\n return result;\n }\n}\n\nexport function normalizeBashToolResultsForReplay(\n toolResults: t.PTCToolResult[]\n): t.PTCToolResult[] {\n return toolResults.map((toolResult) => {\n if (toolResult.is_error) {\n return toolResult;\n }\n\n return {\n ...toolResult,\n result: maybeParseJsonResultString(toolResult.result),\n };\n });\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Normalizes a tool name to a valid bash function identifier.\n * 1. Replace hyphens, spaces, dots with underscores\n * 2. Remove any other invalid characters\n * 3. Prefix with underscore if starts with number\n * 4. Append `_tool` if it's a bash reserved word\n */\nexport function normalizeToBashIdentifier(name: string): string {\n let normalized = name.replace(/[-\\s.]/g, '_');\n normalized = normalized.replace(/[^a-zA-Z0-9_]/g, '');\n\n if (/^[0-9]/.test(normalized)) {\n normalized = '_' + normalized;\n }\n\n if (BASH_RESERVED.has(normalized)) {\n normalized = normalized + '_tool';\n }\n\n return normalized;\n}\n\n/**\n * Extracts tool names that are actually called in the bash code.\n * Bash functions are invoked as commands (no parentheses), so we match\n * the normalized name as a word boundary.\n */\nexport function extractUsedBashToolNames(\n code: string,\n toolNameMap: Map<string, string>\n): Set<string> {\n const usedTools = new Set<string>();\n\n for (const [bashName, originalName] of toolNameMap) {\n const escapedName = bashName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(`\\\\b${escapedName}\\\\b`, 'g');\n\n if (pattern.test(code)) {\n usedTools.add(originalName);\n }\n }\n\n return usedTools;\n}\n\n/**\n * Filters tool definitions to only include tools actually used in the bash code.\n */\nexport function filterBashToolsByUsage(\n toolDefs: t.LCTool[],\n code: string,\n debug = false\n): t.LCTool[] {\n const toolNameMap = new Map<string, string>();\n for (const def of toolDefs) {\n const bashName = normalizeToBashIdentifier(def.name);\n toolNameMap.set(bashName, def.name);\n }\n\n const usedToolNames = extractUsedBashToolNames(code, toolNameMap);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Tool filtering: found ${usedToolNames.size}/${toolDefs.length} tools in code`\n );\n if (usedToolNames.size > 0) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Matched tools: ${Array.from(usedToolNames).join(', ')}`\n );\n }\n }\n\n if (usedToolNames.size === 0) {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n '[BashPTC Debug] No tools detected in code - sending all tools as fallback'\n );\n }\n return toolDefs;\n }\n\n return toolDefs.filter((def) => usedToolNames.has(def.name));\n}\n\n// ============================================================================\n// Tool Factory\n// ============================================================================\n\n/**\n * Creates a Bash Programmatic Tool Calling tool for multi-tool orchestration.\n *\n * This tool enables AI agents to write bash scripts that orchestrate multiple\n * tool calls programmatically via the remote Code API, reducing LLM round-trips.\n *\n * The tool map must be provided at runtime via config.toolCall (injected by ToolNode).\n */\nexport function createBashProgrammaticToolCallingTool(\n initParams: t.BashProgrammaticToolCallingParams = {}\n): DynamicStructuredTool {\n const baseUrl = initParams.baseUrl ?? getCodeBaseURL();\n const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;\n const maxRunTimeoutMs = resolveCodeApiRunTimeoutMs(initParams.runTimeoutMs);\n const proxy = initParams.proxy ?? process.env.PROXY;\n const debug = initParams.debug ?? process.env.BASH_PTC_DEBUG === 'true';\n const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;\n\n return tool(\n async (rawParams, config) => {\n const params = rawParams as { code: string; timeout?: number };\n const { code } = params;\n const timeout = clampCodeApiRunTimeoutMs(params.timeout, maxRunTimeoutMs);\n\n const toolCall = (config.toolCall ?? {}) as ToolCall &\n Partial<t.ProgrammaticCache> & {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n const { toolMap, toolDefs, session_id, _injected_files } = toolCall;\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error(\n 'No toolMap provided. ' +\n 'ToolNode should inject this from AgentContext when invoked through the graph.'\n );\n }\n\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error(\n 'No tool definitions provided. ' +\n 'Either pass tools in the input or ensure ToolNode injects toolDefs.'\n );\n }\n\n let roundTrip = 0;\n\n try {\n // ====================================================================\n // Phase 1: Filter tools and make initial request\n // ====================================================================\n\n const effectiveTools = filterBashToolsByUsage(toolDefs, code, debug);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Sending ${effectiveTools.length} tools to API ` +\n `(filtered from ${toolDefs.length})`\n );\n }\n\n /* `/files/<session_id>` HTTP fallback removed — codeapi's\n * sessionAuth requires kind/id query params unavailable at\n * this point. See `CodeExecutor.ts` for full rationale. */\n let files: t.CodeEnvFile[] | undefined;\n if (_injected_files && _injected_files.length > 0) {\n files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n // eslint-disable-next-line no-console\n console.debug(\n `[BashProgrammaticToolCalling] No injected files for session_id=${session_id} — exec will run without input files`\n );\n }\n\n let response = await makeRequest(\n EXEC_ENDPOINT,\n {\n lang: 'bash',\n code,\n tools: effectiveTools,\n session_id,\n timeout,\n ...(files && files.length > 0 ? { files } : {}),\n },\n proxy,\n initParams.authHeaders\n );\n\n // ====================================================================\n // Phase 2: Handle response loop\n // ====================================================================\n\n while (response.status === 'tool_call_required') {\n roundTrip++;\n\n if (roundTrip > maxRoundTrips) {\n throw new Error(\n `Exceeded maximum round trips (${maxRoundTrips}). ` +\n 'This may indicate an infinite loop, excessive tool calls, ' +\n 'or a logic error in your code.'\n );\n }\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[BashPTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`\n );\n }\n\n const toolResults = normalizeBashToolResultsForReplay(\n await executeTools(\n response.tool_calls ?? [],\n toolMap,\n Constants.BASH_PROGRAMMATIC_TOOL_CALLING\n )\n );\n\n response = await makeRequest(\n EXEC_ENDPOINT,\n {\n continuation_token: response.continuation_token,\n tool_results: toolResults,\n },\n proxy,\n initParams.authHeaders\n );\n }\n\n // ====================================================================\n // Phase 3: Handle final state\n // ====================================================================\n\n if (response.status === 'completed') {\n return formatCompletedResponse(response, code);\n }\n\n if (response.status === 'error') {\n throw new Error(\n `Execution error: ${response.error}` +\n (response.stderr != null && response.stderr !== ''\n ? `\\n\\nStderr:\\n${response.stderr}`\n : '')\n );\n }\n\n throw new Error(`Unexpected response status: ${response.status}`);\n } catch (error) {\n const messageWithReminder = appendFailedExecutionFileReminder(\n (error as Error).message,\n code\n );\n throw new Error(\n `Bash programmatic execution failed: ${messageWithReminder}`\n );\n }\n },\n {\n name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n description: BashProgrammaticToolCallingDescription,\n schema: createBashProgrammaticToolCallingSchema(maxRunTimeoutMs),\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { EnvVar } from '../common/enum.mjs';
2
2
 
3
3
  const DEFAULT_CODE_API_RUN_TIMEOUT_MS = 15_000;
4
4
  const MIN_CODE_API_RUN_TIMEOUT_MS = 1_000;
5
+ const MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS = 300_000;
5
6
  function normalizeTimeoutMs(value) {
6
7
  if (value == null || !Number.isFinite(value)) {
7
8
  return undefined;
@@ -34,17 +35,21 @@ function clampCodeApiRunTimeoutMs(timeoutMs, maxRunTimeoutMs = resolveCodeApiRun
34
35
  }
35
36
  function createCodeApiRunTimeoutSchema(maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()) {
36
37
  const normalizedMaxRunTimeoutMs = normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;
38
+ const normalizedSchemaMaxRunTimeoutMs = Math.max(normalizedMaxRunTimeoutMs, MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS);
37
39
  const formattedTimeout = formatTimeout(normalizedMaxRunTimeoutMs);
40
+ const formattedSchemaMaxTimeout = formatTimeout(normalizedSchemaMaxRunTimeoutMs);
38
41
  return {
39
42
  type: 'integer',
40
43
  minimum: MIN_CODE_API_RUN_TIMEOUT_MS,
41
- maximum: normalizedMaxRunTimeoutMs,
44
+ maximum: normalizedSchemaMaxRunTimeoutMs,
42
45
  default: normalizedMaxRunTimeoutMs,
43
46
  description: 'Maximum wall-clock time in milliseconds for one sandbox run or replay iteration. ' +
44
47
  'This is not the total multi-round-trip task budget. ' +
45
- `Default: ${formattedTimeout}. Max: ${formattedTimeout}.`,
48
+ `Default: ${formattedTimeout}. ` +
49
+ 'Accepted values above the configured cap are clamped before execution. ' +
50
+ `Schema max: ${formattedSchemaMaxTimeout}. Configured cap: ${formattedTimeout}.`,
46
51
  };
47
52
  }
48
53
 
49
- export { DEFAULT_CODE_API_RUN_TIMEOUT_MS, MIN_CODE_API_RUN_TIMEOUT_MS, clampCodeApiRunTimeoutMs, createCodeApiRunTimeoutSchema, resolveCodeApiRunTimeoutMs };
54
+ export { DEFAULT_CODE_API_RUN_TIMEOUT_MS, MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS, MIN_CODE_API_RUN_TIMEOUT_MS, clampCodeApiRunTimeoutMs, createCodeApiRunTimeoutSchema, resolveCodeApiRunTimeoutMs };
50
55
  //# sourceMappingURL=ptcTimeout.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ptcTimeout.mjs","sources":["../../../src/tools/ptcTimeout.ts"],"sourcesContent":["import { EnvVar } from '@/common';\n\nexport const DEFAULT_CODE_API_RUN_TIMEOUT_MS = 15_000;\nexport const MIN_CODE_API_RUN_TIMEOUT_MS = 1_000;\n\ntype TimeoutSchema = {\n type: 'integer';\n minimum: number;\n maximum: number;\n default: number;\n description: string;\n};\n\nexport type ProgrammaticToolCallingJsonSchema = {\n type: 'object';\n properties: {\n code: {\n type: 'string';\n minLength: number;\n description: string;\n };\n timeout: TimeoutSchema;\n };\n required: readonly ['code'];\n};\n\nfunction normalizeTimeoutMs(value: number | undefined): number | undefined {\n if (value == null || !Number.isFinite(value)) {\n return undefined;\n }\n\n return Math.max(MIN_CODE_API_RUN_TIMEOUT_MS, Math.floor(value));\n}\n\nfunction parseTimeoutMs(value: string | undefined): number | undefined {\n if (value == null || value.trim() === '') {\n return undefined;\n }\n\n return normalizeTimeoutMs(Number(value));\n}\n\nfunction formatTimeout(timeoutMs: number): string {\n return timeoutMs % 1000 === 0\n ? `${timeoutMs / 1000} seconds`\n : `${timeoutMs} milliseconds`;\n}\n\nexport function resolveCodeApiRunTimeoutMs(override?: number): number {\n return (\n normalizeTimeoutMs(override) ??\n parseTimeoutMs(process.env[EnvVar.CODE_API_RUN_TIMEOUT_MS]) ??\n DEFAULT_CODE_API_RUN_TIMEOUT_MS\n );\n}\n\nexport function clampCodeApiRunTimeoutMs(\n timeoutMs: number | undefined,\n maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()\n): number {\n const normalizedMaxRunTimeoutMs =\n normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;\n const normalizedTimeoutMs = normalizeTimeoutMs(timeoutMs);\n\n if (normalizedTimeoutMs == null) {\n return normalizedMaxRunTimeoutMs;\n }\n\n return Math.min(normalizedTimeoutMs, normalizedMaxRunTimeoutMs);\n}\n\nexport function createCodeApiRunTimeoutSchema(\n maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()\n): TimeoutSchema {\n const normalizedMaxRunTimeoutMs =\n normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;\n const formattedTimeout = formatTimeout(normalizedMaxRunTimeoutMs);\n\n return {\n type: 'integer',\n minimum: MIN_CODE_API_RUN_TIMEOUT_MS,\n maximum: normalizedMaxRunTimeoutMs,\n default: normalizedMaxRunTimeoutMs,\n description:\n 'Maximum wall-clock time in milliseconds for one sandbox run or replay iteration. ' +\n 'This is not the total multi-round-trip task budget. ' +\n `Default: ${formattedTimeout}. Max: ${formattedTimeout}.`,\n };\n}\n"],"names":[],"mappings":";;AAEO,MAAM,+BAA+B,GAAG;AACxC,MAAM,2BAA2B,GAAG;AAuB3C,SAAS,kBAAkB,CAAC,KAAyB,EAAA;AACnD,IAAA,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AAC5C,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,2BAA2B,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACjE;AAEA,SAAS,cAAc,CAAC,KAAyB,EAAA;IAC/C,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;AACxC,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,OAAO,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1C;AAEA,SAAS,aAAa,CAAC,SAAiB,EAAA;AACtC,IAAA,OAAO,SAAS,GAAG,IAAI,KAAK;AAC1B,UAAE,CAAA,EAAG,SAAS,GAAG,IAAI,CAAA,QAAA;AACrB,UAAE,CAAA,EAAG,SAAS,CAAA,aAAA,CAAe;AACjC;AAEM,SAAU,0BAA0B,CAAC,QAAiB,EAAA;AAC1D,IAAA,QACE,kBAAkB,CAAC,QAAQ,CAAC;QAC5B,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;AAC3D,QAAA,+BAA+B;AAEnC;AAEM,SAAU,wBAAwB,CACtC,SAA6B,EAC7B,eAAe,GAAG,0BAA0B,EAAE,EAAA;IAE9C,MAAM,yBAAyB,GAC7B,kBAAkB,CAAC,eAAe,CAAC,IAAI,+BAA+B;AACxE,IAAA,MAAM,mBAAmB,GAAG,kBAAkB,CAAC,SAAS,CAAC;AAEzD,IAAA,IAAI,mBAAmB,IAAI,IAAI,EAAE;AAC/B,QAAA,OAAO,yBAAyB;IAClC;IAEA,OAAO,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,yBAAyB,CAAC;AACjE;SAEgB,6BAA6B,CAC3C,eAAe,GAAG,0BAA0B,EAAE,EAAA;IAE9C,MAAM,yBAAyB,GAC7B,kBAAkB,CAAC,eAAe,CAAC,IAAI,+BAA+B;AACxE,IAAA,MAAM,gBAAgB,GAAG,aAAa,CAAC,yBAAyB,CAAC;IAEjE,OAAO;AACL,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,OAAO,EAAE,2BAA2B;AACpC,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,WAAW,EACT,mFAAmF;YACnF,sDAAsD;YACtD,CAAA,SAAA,EAAY,gBAAgB,CAAA,OAAA,EAAU,gBAAgB,CAAA,CAAA,CAAG;KAC5D;AACH;;;;"}
1
+ {"version":3,"file":"ptcTimeout.mjs","sources":["../../../src/tools/ptcTimeout.ts"],"sourcesContent":["import { EnvVar } from '@/common';\n\nexport const DEFAULT_CODE_API_RUN_TIMEOUT_MS = 15_000;\nexport const MIN_CODE_API_RUN_TIMEOUT_MS = 1_000;\nexport const MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS = 300_000;\n\ntype TimeoutSchema = {\n type: 'integer';\n minimum: number;\n maximum: number;\n default: number;\n description: string;\n};\n\nexport type ProgrammaticToolCallingJsonSchema = {\n type: 'object';\n properties: {\n code: {\n type: 'string';\n minLength: number;\n description: string;\n };\n timeout: TimeoutSchema;\n };\n required: readonly ['code'];\n};\n\nfunction normalizeTimeoutMs(value: number | undefined): number | undefined {\n if (value == null || !Number.isFinite(value)) {\n return undefined;\n }\n\n return Math.max(MIN_CODE_API_RUN_TIMEOUT_MS, Math.floor(value));\n}\n\nfunction parseTimeoutMs(value: string | undefined): number | undefined {\n if (value == null || value.trim() === '') {\n return undefined;\n }\n\n return normalizeTimeoutMs(Number(value));\n}\n\nfunction formatTimeout(timeoutMs: number): string {\n return timeoutMs % 1000 === 0\n ? `${timeoutMs / 1000} seconds`\n : `${timeoutMs} milliseconds`;\n}\n\nexport function resolveCodeApiRunTimeoutMs(override?: number): number {\n return (\n normalizeTimeoutMs(override) ??\n parseTimeoutMs(process.env[EnvVar.CODE_API_RUN_TIMEOUT_MS]) ??\n DEFAULT_CODE_API_RUN_TIMEOUT_MS\n );\n}\n\nexport function clampCodeApiRunTimeoutMs(\n timeoutMs: number | undefined,\n maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()\n): number {\n const normalizedMaxRunTimeoutMs =\n normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;\n const normalizedTimeoutMs = normalizeTimeoutMs(timeoutMs);\n\n if (normalizedTimeoutMs == null) {\n return normalizedMaxRunTimeoutMs;\n }\n\n return Math.min(normalizedTimeoutMs, normalizedMaxRunTimeoutMs);\n}\n\nexport function createCodeApiRunTimeoutSchema(\n maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()\n): TimeoutSchema {\n const normalizedMaxRunTimeoutMs =\n normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;\n const normalizedSchemaMaxRunTimeoutMs = Math.max(\n normalizedMaxRunTimeoutMs,\n MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS\n );\n const formattedTimeout = formatTimeout(normalizedMaxRunTimeoutMs);\n const formattedSchemaMaxTimeout = formatTimeout(\n normalizedSchemaMaxRunTimeoutMs\n );\n\n return {\n type: 'integer',\n minimum: MIN_CODE_API_RUN_TIMEOUT_MS,\n maximum: normalizedSchemaMaxRunTimeoutMs,\n default: normalizedMaxRunTimeoutMs,\n description:\n 'Maximum wall-clock time in milliseconds for one sandbox run or replay iteration. ' +\n 'This is not the total multi-round-trip task budget. ' +\n `Default: ${formattedTimeout}. ` +\n 'Accepted values above the configured cap are clamped before execution. ' +\n `Schema max: ${formattedSchemaMaxTimeout}. Configured cap: ${formattedTimeout}.`,\n };\n}\n"],"names":[],"mappings":";;AAEO,MAAM,+BAA+B,GAAG;AACxC,MAAM,2BAA2B,GAAG;AACpC,MAAM,kCAAkC,GAAG;AAuBlD,SAAS,kBAAkB,CAAC,KAAyB,EAAA;AACnD,IAAA,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AAC5C,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,2BAA2B,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACjE;AAEA,SAAS,cAAc,CAAC,KAAyB,EAAA;IAC/C,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;AACxC,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,OAAO,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1C;AAEA,SAAS,aAAa,CAAC,SAAiB,EAAA;AACtC,IAAA,OAAO,SAAS,GAAG,IAAI,KAAK;AAC1B,UAAE,CAAA,EAAG,SAAS,GAAG,IAAI,CAAA,QAAA;AACrB,UAAE,CAAA,EAAG,SAAS,CAAA,aAAA,CAAe;AACjC;AAEM,SAAU,0BAA0B,CAAC,QAAiB,EAAA;AAC1D,IAAA,QACE,kBAAkB,CAAC,QAAQ,CAAC;QAC5B,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;AAC3D,QAAA,+BAA+B;AAEnC;AAEM,SAAU,wBAAwB,CACtC,SAA6B,EAC7B,eAAe,GAAG,0BAA0B,EAAE,EAAA;IAE9C,MAAM,yBAAyB,GAC7B,kBAAkB,CAAC,eAAe,CAAC,IAAI,+BAA+B;AACxE,IAAA,MAAM,mBAAmB,GAAG,kBAAkB,CAAC,SAAS,CAAC;AAEzD,IAAA,IAAI,mBAAmB,IAAI,IAAI,EAAE;AAC/B,QAAA,OAAO,yBAAyB;IAClC;IAEA,OAAO,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,yBAAyB,CAAC;AACjE;SAEgB,6BAA6B,CAC3C,eAAe,GAAG,0BAA0B,EAAE,EAAA;IAE9C,MAAM,yBAAyB,GAC7B,kBAAkB,CAAC,eAAe,CAAC,IAAI,+BAA+B;IACxE,MAAM,+BAA+B,GAAG,IAAI,CAAC,GAAG,CAC9C,yBAAyB,EACzB,kCAAkC,CACnC;AACD,IAAA,MAAM,gBAAgB,GAAG,aAAa,CAAC,yBAAyB,CAAC;AACjE,IAAA,MAAM,yBAAyB,GAAG,aAAa,CAC7C,+BAA+B,CAChC;IAED,OAAO;AACL,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,OAAO,EAAE,2BAA2B;AACpC,QAAA,OAAO,EAAE,+BAA+B;AACxC,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,WAAW,EACT,mFAAmF;YACnF,sDAAsD;AACtD,YAAA,CAAA,SAAA,EAAY,gBAAgB,CAAA,EAAA,CAAI;YAChC,yEAAyE;YACzE,CAAA,YAAA,EAAe,yBAAyB,CAAA,kBAAA,EAAqB,gBAAgB,CAAA,CAAA,CAAG;KACnF;AACH;;;;"}
@@ -11,6 +11,7 @@ export declare const BashProgrammaticToolCallingDefinition: {
11
11
  readonly description: string;
12
12
  readonly schema: ProgrammaticToolCallingJsonSchema;
13
13
  };
14
+ export declare function normalizeBashToolResultsForReplay(toolResults: t.PTCToolResult[]): t.PTCToolResult[];
14
15
  /**
15
16
  * Normalizes a tool name to a valid bash function identifier.
16
17
  * 1. Replace hyphens, spaces, dots with underscores
@@ -1,5 +1,6 @@
1
1
  export declare const DEFAULT_CODE_API_RUN_TIMEOUT_MS = 15000;
2
2
  export declare const MIN_CODE_API_RUN_TIMEOUT_MS = 1000;
3
+ export declare const MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS = 300000;
3
4
  type TimeoutSchema = {
4
5
  type: 'integer';
5
6
  minimum: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.1.94",
3
+ "version": "3.1.96",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -71,7 +71,7 @@ const CORE_RULES = `Rules:
71
71
  - Tools are pre-defined as bash functions—DO NOT redefine them
72
72
  - Each tool function accepts a JSON string argument
73
73
  - Save tool output with raw=$(tool '{}'); printf '%s\n' "$raw" > /mnt/data/file.json; direct tool > file may be empty
74
- - jq: use fromjson? // . on saved tool stdout and again on JSON-string fields; check types since arrays may contain strings
74
+ - Tool stdout is normalized to one compact JSON value when possible; parse saved stdout once, then use fromjson? // . only for JSON-string fields
75
75
  - Only echo/printf output returns to the model
76
76
  - ${CODE_ARTIFACT_PATH_GUIDANCE}
77
77
  - ${BASH_SHELL_GUIDANCE}
@@ -149,6 +149,38 @@ export const BashProgrammaticToolCallingDefinition = {
149
149
  schema: BashProgrammaticToolCallingSchema,
150
150
  } as const;
151
151
 
152
+ function maybeParseJsonResultString(result: unknown): unknown {
153
+ if (typeof result !== 'string') {
154
+ return result;
155
+ }
156
+
157
+ const trimmed = result.trim();
158
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
159
+ return result;
160
+ }
161
+
162
+ try {
163
+ return JSON.parse(trimmed) as unknown;
164
+ } catch {
165
+ return result;
166
+ }
167
+ }
168
+
169
+ export function normalizeBashToolResultsForReplay(
170
+ toolResults: t.PTCToolResult[]
171
+ ): t.PTCToolResult[] {
172
+ return toolResults.map((toolResult) => {
173
+ if (toolResult.is_error) {
174
+ return toolResult;
175
+ }
176
+
177
+ return {
178
+ ...toolResult,
179
+ result: maybeParseJsonResultString(toolResult.result),
180
+ };
181
+ });
182
+ }
183
+
152
184
  // ============================================================================
153
185
  // Helper Functions
154
186
  // ============================================================================
@@ -355,10 +387,12 @@ export function createBashProgrammaticToolCallingTool(
355
387
  );
356
388
  }
357
389
 
358
- const toolResults = await executeTools(
359
- response.tool_calls ?? [],
360
- toolMap,
361
- Constants.BASH_PROGRAMMATIC_TOOL_CALLING
390
+ const toolResults = normalizeBashToolResultsForReplay(
391
+ await executeTools(
392
+ response.tool_calls ?? [],
393
+ toolMap,
394
+ Constants.BASH_PROGRAMMATIC_TOOL_CALLING
395
+ )
362
396
  );
363
397
 
364
398
  response = await makeRequest(
@@ -16,6 +16,7 @@ import { createBashProgrammaticToolCallingTool } from '../BashProgrammaticToolCa
16
16
  import {
17
17
  clampCodeApiRunTimeoutMs,
18
18
  createCodeApiRunTimeoutSchema,
19
+ MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS,
19
20
  } from '../ptcTimeout';
20
21
  import {
21
22
  createLocalProgrammaticToolCallingTool,
@@ -33,6 +34,8 @@ type FetchMock = jest.MockedFunction<
33
34
 
34
35
  type CodeApiRequestBody = {
35
36
  timeout?: number;
37
+ continuation_token?: string;
38
+ tool_results?: t.PTCToolResult[];
36
39
  };
37
40
 
38
41
  type TimeoutSchemaForTest = {
@@ -267,6 +270,26 @@ describe('CodeAPI auth header injection', () => {
267
270
  expect(requestBodyAt(0).timeout).toBe(15000);
268
271
  });
269
272
 
273
+ it('accepts larger programmatic timeout inputs and clamps before execution', async () => {
274
+ const tool = createProgrammaticToolCallingTool({
275
+ runTimeoutMs: 15000,
276
+ });
277
+
278
+ await tool.invoke(
279
+ { code: 'result = await lookup_user()\nprint(result)', timeout: 30000 },
280
+ {
281
+ toolCall: {
282
+ name: 'programmatic_code_execution',
283
+ args: {},
284
+ toolMap: toolMap(),
285
+ toolDefs,
286
+ },
287
+ }
288
+ );
289
+
290
+ expect(requestBodyAt(0).timeout).toBe(15000);
291
+ });
292
+
270
293
  it('defaults bash programmatic timeout to the configured CodeAPI run cap', async () => {
271
294
  const tool = createBashProgrammaticToolCallingTool({
272
295
  runTimeoutMs: 15000,
@@ -287,14 +310,36 @@ describe('CodeAPI auth header injection', () => {
287
310
  expect(requestBodyAt(0).timeout).toBe(15000);
288
311
  });
289
312
 
313
+ it('accepts larger bash programmatic timeout inputs and clamps before execution', async () => {
314
+ const tool = createBashProgrammaticToolCallingTool({
315
+ runTimeoutMs: 15000,
316
+ });
317
+
318
+ await tool.invoke(
319
+ { code: 'lookup_user "{}"', timeout: 30000 },
320
+ {
321
+ toolCall: {
322
+ name: 'bash_programmatic_code_execution',
323
+ args: {},
324
+ toolMap: toolMap(),
325
+ toolDefs,
326
+ },
327
+ }
328
+ );
329
+
330
+ expect(requestBodyAt(0).timeout).toBe(15000);
331
+ });
332
+
290
333
  it('describes the PTC timeout as a single sandbox run cap', () => {
291
334
  const schema = createCodeApiRunTimeoutSchema(15000);
292
335
 
293
336
  expect(clampCodeApiRunTimeoutMs(60000, 15000)).toBe(15000);
294
337
  expect(schema.default).toBe(15000);
295
- expect(schema.maximum).toBe(15000);
338
+ expect(schema.maximum).toBe(MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS);
296
339
  expect(schema.description).toContain('one sandbox run');
297
340
  expect(schema.description).toContain('not the total multi-round-trip');
341
+ expect(schema.description).toContain('clamped before execution');
342
+ expect(schema.description).toContain('Configured cap: 15 seconds');
298
343
  });
299
344
 
300
345
  it('keeps local programmatic timeout schemas aligned with local execution defaults', () => {
@@ -344,6 +389,58 @@ describe('CodeAPI auth header injection', () => {
344
389
  );
345
390
  });
346
391
 
392
+ it('normalizes JSON-looking bash programmatic tool results before continuation', async () => {
393
+ fetchMock
394
+ .mockResolvedValueOnce(
395
+ jsonResponse({
396
+ status: 'tool_call_required',
397
+ continuation_token: 'continue_123',
398
+ tool_calls: [{ id: 'call_001', name: 'lookup_user', input: {} }],
399
+ })
400
+ )
401
+ .mockResolvedValueOnce(completedResponse('done'));
402
+ const tool = createBashProgrammaticToolCallingTool();
403
+ const customToolMap = new Map([
404
+ [
405
+ 'lookup_user',
406
+ {
407
+ name: 'lookup_user',
408
+ invoke: jest.fn(async () =>
409
+ JSON.stringify({
410
+ result: {
411
+ data: [{ id: 'user_123', name: 'Ada' }],
412
+ },
413
+ })
414
+ ),
415
+ },
416
+ ],
417
+ ]) as unknown as t.ToolMap;
418
+
419
+ await tool.invoke(
420
+ { code: 'lookup_user "{}"' },
421
+ {
422
+ toolCall: {
423
+ name: 'bash_programmatic_code_execution',
424
+ args: {},
425
+ toolMap: customToolMap,
426
+ toolDefs,
427
+ },
428
+ }
429
+ );
430
+
431
+ expect(requestBodyAt(1).tool_results).toEqual([
432
+ {
433
+ call_id: 'call_001',
434
+ result: {
435
+ result: {
436
+ data: [{ id: 'user_123', name: 'Ada' }],
437
+ },
438
+ },
439
+ is_error: false,
440
+ },
441
+ ]);
442
+ });
443
+
347
444
  it('reminds that failed bash programmatic executions do not register new files', async () => {
348
445
  fetchMock.mockResolvedValueOnce(
349
446
  jsonResponse({
@@ -16,7 +16,10 @@ import {
16
16
  normalizeToPythonIdentifier,
17
17
  unwrapToolResponse,
18
18
  } from '../ProgrammaticToolCalling';
19
- import { createBashProgrammaticToolCallingSchema } from '../BashProgrammaticToolCalling';
19
+ import {
20
+ createBashProgrammaticToolCallingSchema,
21
+ normalizeBashToolResultsForReplay,
22
+ } from '../BashProgrammaticToolCalling';
20
23
  import {
21
24
  createProgrammaticToolRegistry,
22
25
  createGetTeamMembersTool,
@@ -40,9 +43,11 @@ describe('ProgrammaticToolCalling', () => {
40
43
  const schema = createBashProgrammaticToolCallingSchema();
41
44
  const description = schema.properties.code.description;
42
45
 
43
- expect(description).toContain('jq: use fromjson? // .');
44
- expect(description).toContain('again on JSON-string fields');
45
- expect(description).toContain('arrays may contain strings');
46
+ expect(description).toContain(
47
+ 'Tool stdout is normalized to one compact JSON value'
48
+ );
49
+ expect(description).toContain('use fromjson? // .');
50
+ expect(description).toContain('only for JSON-string fields');
46
51
  expect(description).toContain('raw=$(tool');
47
52
  expect(description).toContain('direct tool > file may be empty');
48
53
  expect(description).toContain('/mnt/data/sf.json');
@@ -53,6 +58,69 @@ describe('ProgrammaticToolCalling', () => {
53
58
  });
54
59
  });
55
60
 
61
+ describe('normalizeBashToolResultsForReplay', () => {
62
+ it('decodes JSON-looking string results before bash replay', () => {
63
+ const results = normalizeBashToolResultsForReplay([
64
+ {
65
+ call_id: 'call_001',
66
+ result: JSON.stringify({
67
+ result: {
68
+ data: [{ station_id: 'USW00094725', winter_days: 91 }],
69
+ },
70
+ }),
71
+ is_error: false,
72
+ },
73
+ ]);
74
+
75
+ expect(results[0].result).toEqual({
76
+ result: {
77
+ data: [{ station_id: 'USW00094725', winter_days: 91 }],
78
+ },
79
+ });
80
+ });
81
+
82
+ it('decodes JSON-looking array string results before bash replay', () => {
83
+ const results = normalizeBashToolResultsForReplay([
84
+ {
85
+ call_id: 'call_001',
86
+ result: '[{"name":"tempAvg"},{"name":"snowDepth"}]',
87
+ is_error: false,
88
+ },
89
+ ]);
90
+
91
+ expect(results[0].result).toEqual([
92
+ { name: 'tempAvg' },
93
+ { name: 'snowDepth' },
94
+ ]);
95
+ });
96
+
97
+ it('preserves plain strings, invalid JSON strings, and error results', () => {
98
+ const errorJson = '{"message":"tool failed"}';
99
+ const results = normalizeBashToolResultsForReplay([
100
+ {
101
+ call_id: 'call_001',
102
+ result: 'plain text',
103
+ is_error: false,
104
+ },
105
+ {
106
+ call_id: 'call_002',
107
+ result: '{not json}',
108
+ is_error: false,
109
+ },
110
+ {
111
+ call_id: 'call_003',
112
+ result: errorJson,
113
+ is_error: true,
114
+ error_message: 'tool failed',
115
+ },
116
+ ]);
117
+
118
+ expect(results[0].result).toBe('plain text');
119
+ expect(results[1].result).toBe('{not json}');
120
+ expect(results[2].result).toBe(errorJson);
121
+ });
122
+ });
123
+
56
124
  describe('executeTools', () => {
57
125
  let toolMap: t.ToolMap;
58
126
 
@@ -106,6 +174,49 @@ describe('ProgrammaticToolCalling', () => {
106
174
  });
107
175
  });
108
176
 
177
+ it('parses ClickHouse-style JSON strings with SQL punctuation for object-schema tools', async () => {
178
+ const invoke = jest.fn<
179
+ (_input: unknown, _config: unknown) => Promise<unknown>
180
+ >(async (input) => input);
181
+ const customTool = {
182
+ name: 'run_select_query_mcp_ClickHouse',
183
+ schema: {
184
+ type: 'object',
185
+ properties: {
186
+ serviceId: { type: 'string' },
187
+ query: { type: 'string' },
188
+ },
189
+ required: ['serviceId', 'query'],
190
+ },
191
+ invoke,
192
+ } as unknown as t.GenericTool;
193
+ const customToolMap: t.ToolMap = new Map([
194
+ ['run_select_query_mcp_ClickHouse', customTool],
195
+ ]);
196
+ const input = {
197
+ serviceId: '45886e06-932b-4cff-bb49-3f7281d80717',
198
+ query:
199
+ 'SELECT name, round(avg(tempAvg)/10.0, 2) AS avg_temp_c ' +
200
+ 'FROM system.columns WHERE database=\'default\' ' +
201
+ 'AND table=\'uk_prices_3\' AND tempAvg != -9999',
202
+ };
203
+ const toolCalls: t.PTCToolCall[] = [
204
+ {
205
+ id: 'call_001',
206
+ name: 'run_select_query_mcp_ClickHouse',
207
+ input: JSON.stringify(input),
208
+ },
209
+ ];
210
+
211
+ const results = await executeTools(toolCalls, customToolMap);
212
+
213
+ expect(results[0].is_error).toBe(false);
214
+ expect(results[0].result).toEqual(input);
215
+ expect(invoke).toHaveBeenCalledWith(input, {
216
+ metadata: { [Constants.PROGRAMMATIC_TOOL_CALLING]: true },
217
+ });
218
+ });
219
+
109
220
  it('preserves JSON-looking strings for string-input tools', async () => {
110
221
  const invoke = jest.fn<
111
222
  (_input: unknown, _config: unknown) => Promise<unknown>
@@ -133,6 +244,39 @@ describe('ProgrammaticToolCalling', () => {
133
244
  });
134
245
  });
135
246
 
247
+ it('preserves ClickHouse-style JSON strings for raw string-input tools', async () => {
248
+ const invoke = jest.fn<
249
+ (_input: unknown, _config: unknown) => Promise<unknown>
250
+ >(async (input) => input);
251
+ const customTool = {
252
+ name: 'string_tool',
253
+ schema: { type: 'string' },
254
+ invoke,
255
+ } as unknown as t.GenericTool;
256
+ const customToolMap: t.ToolMap = new Map([['string_tool', customTool]]);
257
+ const input = JSON.stringify({
258
+ serviceId: '45886e06-932b-4cff-bb49-3f7281d80717',
259
+ query:
260
+ 'SELECT round(avg(tempAvg)/10.0, 2) AS avg_temp_c ' +
261
+ 'FROM default.weather_noaa_mt WHERE database=\'default\'',
262
+ });
263
+ const toolCalls: t.PTCToolCall[] = [
264
+ {
265
+ id: 'call_001',
266
+ name: 'string_tool',
267
+ input,
268
+ },
269
+ ];
270
+
271
+ const results = await executeTools(toolCalls, customToolMap);
272
+
273
+ expect(results[0].is_error).toBe(false);
274
+ expect(results[0].result).toBe(input);
275
+ expect(invoke).toHaveBeenCalledWith(input, {
276
+ metadata: { [Constants.PROGRAMMATIC_TOOL_CALLING]: true },
277
+ });
278
+ });
279
+
136
280
  it('stringifies object inputs before invoking string-input tools', async () => {
137
281
  const invoke = jest.fn<
138
282
  (_input: unknown, _config: unknown) => Promise<unknown>
@@ -2,6 +2,7 @@ import { EnvVar } from '@/common';
2
2
 
3
3
  export const DEFAULT_CODE_API_RUN_TIMEOUT_MS = 15_000;
4
4
  export const MIN_CODE_API_RUN_TIMEOUT_MS = 1_000;
5
+ export const MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS = 300_000;
5
6
 
6
7
  type TimeoutSchema = {
7
8
  type: 'integer';
@@ -74,16 +75,25 @@ export function createCodeApiRunTimeoutSchema(
74
75
  ): TimeoutSchema {
75
76
  const normalizedMaxRunTimeoutMs =
76
77
  normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;
78
+ const normalizedSchemaMaxRunTimeoutMs = Math.max(
79
+ normalizedMaxRunTimeoutMs,
80
+ MAX_CODE_API_RUN_TIMEOUT_SCHEMA_MS
81
+ );
77
82
  const formattedTimeout = formatTimeout(normalizedMaxRunTimeoutMs);
83
+ const formattedSchemaMaxTimeout = formatTimeout(
84
+ normalizedSchemaMaxRunTimeoutMs
85
+ );
78
86
 
79
87
  return {
80
88
  type: 'integer',
81
89
  minimum: MIN_CODE_API_RUN_TIMEOUT_MS,
82
- maximum: normalizedMaxRunTimeoutMs,
90
+ maximum: normalizedSchemaMaxRunTimeoutMs,
83
91
  default: normalizedMaxRunTimeoutMs,
84
92
  description:
85
93
  'Maximum wall-clock time in milliseconds for one sandbox run or replay iteration. ' +
86
94
  'This is not the total multi-round-trip task budget. ' +
87
- `Default: ${formattedTimeout}. Max: ${formattedTimeout}.`,
95
+ `Default: ${formattedTimeout}. ` +
96
+ 'Accepted values above the configured cap are clamped before execution. ' +
97
+ `Schema max: ${formattedSchemaMaxTimeout}. Configured cap: ${formattedTimeout}.`,
88
98
  };
89
99
  }