@librechat/agents 3.1.79 → 3.1.80-dev.1
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/tools/BashExecutor.cjs +13 -2
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +21 -5
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +12 -4
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +73 -40
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +13 -2
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +2 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +21 -5
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +12 -4
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +73 -40
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -1
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
- package/dist/types/types/tools.d.ts +106 -17
- package/package.json +1 -1
- package/src/scripts/code_exec_multi_session.ts +4 -4
- package/src/tools/BashExecutor.ts +14 -3
- package/src/tools/BashProgrammaticToolCalling.ts +6 -6
- package/src/tools/CodeExecutor.ts +22 -6
- package/src/tools/ProgrammaticToolCalling.ts +17 -10
- package/src/tools/ToolNode.ts +96 -48
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +9 -2
- package/src/tools/__tests__/ToolNode.session.test.ts +152 -50
- package/src/tools/local/LocalExecutionTools.ts +2 -2
- package/src/tools/local/LocalProgrammaticToolCalling.ts +23 -6
- package/src/types/tools.ts +103 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalExecutionTools.mjs","sources":["../../../../src/tools/local/LocalExecutionTools.ts"],"sourcesContent":["import { tool } from '@langchain/core/tools';\nimport type { DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport {\n CodeExecutionToolName,\n CodeExecutionToolSchema,\n} from '@/tools/CodeExecutor';\nimport {\n BashExecutionToolName,\n BashExecutionToolSchema,\n BashToolOutputReferencesGuide,\n} from '@/tools/BashExecutor';\nimport {\n executeLocalBash,\n executeLocalBashWithArgs,\n executeLocalCode,\n getLocalCwd,\n getLocalSessionId,\n} from './LocalExecutionEngine';\nimport { Constants } from '@/common';\n\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nexport const LocalCodeExecutionToolDescription = `\nRuns code on the local machine in the configured working directory. Unlike the remote Code API sandbox, this tool can see the local repository, installed runtimes, environment variables, and filesystem available to the host process.\n\nUsage:\n- The remote sandbox API remains the default; this description applies only when local execution mode is enabled.\n- Local commands can use the Anthropic sandbox runtime when local.sandbox.enabled=true and @anthropic-ai/sandbox-runtime is installed.\n- Commands execute in the local working directory and may modify local files.\n- Input code is already displayed to the user, so do not repeat it unless asked.\n- Output is not displayed unless you print it explicitly.\n`.trim();\n\nexport const LocalBashExecutionToolDescription = `\nRuns bash commands on the local machine in the configured working directory. Unlike the remote Code API sandbox, this tool can see the local repository, installed tools, environment variables, and filesystem available to the host process.\n\nUsage:\n- The remote sandbox API remains the default; this description applies only when local execution mode is enabled.\n- Local commands can use the Anthropic sandbox runtime when local.sandbox.enabled=true and @anthropic-ai/sandbox-runtime is installed.\n- Commands execute in the local working directory and may modify local files.\n- Output is not displayed unless you print it explicitly.\n- Prefer project-native commands and inspect files before changing them.\n`.trim();\n\nfunction formatLocalOutput(\n result: {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n timedOut: boolean;\n overflowKilled?: boolean;\n signal?: string;\n fullOutputPath?: string;\n },\n cwd: string\n): string {\n let formatted = '';\n if (result.stdout !== '') {\n formatted += `stdout:\\n${result.stdout}\\n`;\n } else {\n formatted += emptyOutputMessage;\n }\n\n if (result.stderr !== '') {\n formatted += `stderr:\\n${result.stderr}\\n`;\n }\n\n if (result.exitCode != null && result.exitCode !== 0) {\n formatted += `exit_code: ${result.exitCode}\\n`;\n }\n\n if (result.timedOut) {\n formatted += 'timed_out: true\\n';\n }\n\n if (result.overflowKilled === true) {\n // Surface the force-kill explicitly so the model treats this as a\n // failure rather than misreading \"exit_code: 137 + truncated\n // stdout\" as a normal completion. (Codex P1 — pre-fix the close\n // handler swallowed the overflow flag and exitCode was null on\n // signal-killed processes.)\n formatted +=\n 'killed: true (output exceeded local.maxSpawnedBytes; process tree was terminated)\\n';\n } else if (result.signal != null) {\n // Generic signal kill: `kill -9 $$` from inside the script,\n // native crash, OS OOM killer, etc. Codex P2 generalization of\n // the overflow case.\n formatted += `killed: true (signal=${result.signal})\\n`;\n }\n\n if (result.fullOutputPath != null) {\n formatted += `full_output_path: ${result.fullOutputPath} (output exceeded the configured cap; use bash to inspect — the file holds the complete stdout/stderr)\\n`;\n }\n\n formatted += `working_directory: ${cwd}`;\n return formatted.trim();\n}\n\nexport function createLocalCodeExecutionTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n lang: string;\n code: string;\n args?: string[];\n };\n const cwd = getLocalCwd(config);\n const result = await executeLocalCode(input, config);\n return [\n formatLocalOutput(result, cwd),\n {\n session_id: getLocalSessionId(config),\n files: [],\n },\n ];\n },\n {\n name: CodeExecutionToolName,\n description: LocalCodeExecutionToolDescription,\n schema: CodeExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalBashExecutionTool(options?: {\n config?: t.LocalExecutionConfig;\n enableToolOutputReferences?: boolean;\n}): DynamicStructuredTool {\n const config = options?.config ?? {};\n return tool(\n async (rawInput) => {\n const input = rawInput as { command: string; args?: string[] };\n const cwd = getLocalCwd(config);\n // Use the standard `bash -lc <command> -- <args...>` form so\n // `$1`, `$2`, … resolve correctly inside `command`. The\n // previous implementation appended args literally to the\n // command string (`${command} ${args.join(' ')}`), which\n // doesn't populate positional parameters and silently broke\n // shell snippets like `command: 'echo \"$1\"'`.\n const result =\n input.args != null && input.args.length > 0\n ? await executeLocalBashWithArgs(input.command, input.args, config)\n : await executeLocalBash(input.command, config);\n return [\n formatLocalOutput(result, cwd),\n {\n session_id: getLocalSessionId(config),\n files: [],\n },\n ];\n },\n {\n name: BashExecutionToolName,\n description:\n options?.enableToolOutputReferences === true\n ? `${LocalBashExecutionToolDescription}\\n\\n${BashToolOutputReferencesGuide}`\n : LocalBashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":[],"mappings":";;;;;;AAqBA,MAAM,kBAAkB,GACtB,4DAA4D;AAEvD,MAAM,iCAAiC,GAAG;;;;;;;;;CAShD,CAAC,IAAI;AAEC,MAAM,iCAAiC,GAAG;;;;;;;;;CAShD,CAAC,IAAI;AAEN,SAAS,iBAAiB,CACxB,MAQC,EACD,GAAW,EAAA;IAEX,IAAI,SAAS,GAAG,EAAE;AAClB,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE;AACxB,QAAA,SAAS,IAAI,CAAA,SAAA,EAAY,MAAM,CAAC,MAAM,IAAI;IAC5C;SAAO;QACL,SAAS,IAAI,kBAAkB;IACjC;AAEA,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE;AACxB,QAAA,SAAS,IAAI,CAAA,SAAA,EAAY,MAAM,CAAC,MAAM,IAAI;IAC5C;AAEA,IAAA,IAAI,MAAM,CAAC,QAAQ,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE;AACpD,QAAA,SAAS,IAAI,CAAA,WAAA,EAAc,MAAM,CAAC,QAAQ,IAAI;IAChD;AAEA,IAAA,IAAI,MAAM,CAAC,QAAQ,EAAE;QACnB,SAAS,IAAI,mBAAmB;IAClC;AAEA,IAAA,IAAI,MAAM,CAAC,cAAc,KAAK,IAAI,EAAE;;;;;;QAMlC,SAAS;AACP,YAAA,qFAAqF;IACzF;AAAO,SAAA,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE;;;;AAIhC,QAAA,SAAS,IAAI,CAAA,qBAAA,EAAwB,MAAM,CAAC,MAAM,KAAK;IACzD;AAEA,IAAA,IAAI,MAAM,CAAC,cAAc,IAAI,IAAI,EAAE;AACjC,QAAA,SAAS,IAAI,CAAA,kBAAA,EAAqB,MAAM,CAAC,cAAc,0GAA0G;IACnK;AAEA,IAAA,SAAS,IAAI,CAAA,mBAAA,EAAsB,GAAG,CAAA,CAAE;AACxC,IAAA,OAAO,SAAS,CAAC,IAAI,EAAE;AACzB;AAEM,SAAU,4BAA4B,CAC1C,MAAA,GAAiC,EAAE,EAAA;AAEnC,IAAA,OAAO,IAAI,CACT,OAAO,QAAQ,KAAI;QACjB,MAAM,KAAK,GAAG,QAIb;AACD,QAAA,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC;QACpD,OAAO;AACL,YAAA,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC;AAC9B,YAAA;AACE,gBAAA,UAAU,EAAE,iBAAiB,CAAC,MAAM,CAAC;AACrC,gBAAA,KAAK,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"LocalExecutionTools.mjs","sources":["../../../../src/tools/local/LocalExecutionTools.ts"],"sourcesContent":["import { tool } from '@langchain/core/tools';\nimport type { DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport {\n CodeExecutionToolName,\n CodeExecutionToolSchema,\n} from '@/tools/CodeExecutor';\nimport {\n BashExecutionToolName,\n BashExecutionToolSchema,\n BashToolOutputReferencesGuide,\n} from '@/tools/BashExecutor';\nimport {\n executeLocalBash,\n executeLocalBashWithArgs,\n executeLocalCode,\n getLocalCwd,\n getLocalSessionId,\n} from './LocalExecutionEngine';\nimport { Constants } from '@/common';\n\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nexport const LocalCodeExecutionToolDescription = `\nRuns code on the local machine in the configured working directory. Unlike the remote Code API sandbox, this tool can see the local repository, installed runtimes, environment variables, and filesystem available to the host process.\n\nUsage:\n- The remote sandbox API remains the default; this description applies only when local execution mode is enabled.\n- Local commands can use the Anthropic sandbox runtime when local.sandbox.enabled=true and @anthropic-ai/sandbox-runtime is installed.\n- Commands execute in the local working directory and may modify local files.\n- Input code is already displayed to the user, so do not repeat it unless asked.\n- Output is not displayed unless you print it explicitly.\n`.trim();\n\nexport const LocalBashExecutionToolDescription = `\nRuns bash commands on the local machine in the configured working directory. Unlike the remote Code API sandbox, this tool can see the local repository, installed tools, environment variables, and filesystem available to the host process.\n\nUsage:\n- The remote sandbox API remains the default; this description applies only when local execution mode is enabled.\n- Local commands can use the Anthropic sandbox runtime when local.sandbox.enabled=true and @anthropic-ai/sandbox-runtime is installed.\n- Commands execute in the local working directory and may modify local files.\n- Output is not displayed unless you print it explicitly.\n- Prefer project-native commands and inspect files before changing them.\n`.trim();\n\nfunction formatLocalOutput(\n result: {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n timedOut: boolean;\n overflowKilled?: boolean;\n signal?: string;\n fullOutputPath?: string;\n },\n cwd: string\n): string {\n let formatted = '';\n if (result.stdout !== '') {\n formatted += `stdout:\\n${result.stdout}\\n`;\n } else {\n formatted += emptyOutputMessage;\n }\n\n if (result.stderr !== '') {\n formatted += `stderr:\\n${result.stderr}\\n`;\n }\n\n if (result.exitCode != null && result.exitCode !== 0) {\n formatted += `exit_code: ${result.exitCode}\\n`;\n }\n\n if (result.timedOut) {\n formatted += 'timed_out: true\\n';\n }\n\n if (result.overflowKilled === true) {\n // Surface the force-kill explicitly so the model treats this as a\n // failure rather than misreading \"exit_code: 137 + truncated\n // stdout\" as a normal completion. (Codex P1 — pre-fix the close\n // handler swallowed the overflow flag and exitCode was null on\n // signal-killed processes.)\n formatted +=\n 'killed: true (output exceeded local.maxSpawnedBytes; process tree was terminated)\\n';\n } else if (result.signal != null) {\n // Generic signal kill: `kill -9 $$` from inside the script,\n // native crash, OS OOM killer, etc. Codex P2 generalization of\n // the overflow case.\n formatted += `killed: true (signal=${result.signal})\\n`;\n }\n\n if (result.fullOutputPath != null) {\n formatted += `full_output_path: ${result.fullOutputPath} (output exceeded the configured cap; use bash to inspect — the file holds the complete stdout/stderr)\\n`;\n }\n\n formatted += `working_directory: ${cwd}`;\n return formatted.trim();\n}\n\nexport function createLocalCodeExecutionTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n lang: string;\n code: string;\n args?: string[];\n };\n const cwd = getLocalCwd(config);\n const result = await executeLocalCode(input, config);\n return [\n formatLocalOutput(result, cwd),\n {\n session_id: getLocalSessionId(config),\n files: [],\n } satisfies t.CodeExecutionArtifact,\n ];\n },\n {\n name: CodeExecutionToolName,\n description: LocalCodeExecutionToolDescription,\n schema: CodeExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalBashExecutionTool(options?: {\n config?: t.LocalExecutionConfig;\n enableToolOutputReferences?: boolean;\n}): DynamicStructuredTool {\n const config = options?.config ?? {};\n return tool(\n async (rawInput) => {\n const input = rawInput as { command: string; args?: string[] };\n const cwd = getLocalCwd(config);\n // Use the standard `bash -lc <command> -- <args...>` form so\n // `$1`, `$2`, … resolve correctly inside `command`. The\n // previous implementation appended args literally to the\n // command string (`${command} ${args.join(' ')}`), which\n // doesn't populate positional parameters and silently broke\n // shell snippets like `command: 'echo \"$1\"'`.\n const result =\n input.args != null && input.args.length > 0\n ? await executeLocalBashWithArgs(input.command, input.args, config)\n : await executeLocalBash(input.command, config);\n return [\n formatLocalOutput(result, cwd),\n {\n session_id: getLocalSessionId(config),\n files: [],\n } satisfies t.CodeExecutionArtifact,\n ];\n },\n {\n name: BashExecutionToolName,\n description:\n options?.enableToolOutputReferences === true\n ? `${LocalBashExecutionToolDescription}\\n\\n${BashToolOutputReferencesGuide}`\n : LocalBashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalProgrammaticToolCalling.mjs","sources":["../../../../src/tools/local/LocalProgrammaticToolCalling.ts"],"sourcesContent":["import { randomBytes, randomUUID, timingSafeEqual } from 'crypto';\nimport { createServer } from 'http';\nimport { tool } from '@langchain/core/tools';\nimport type { AddressInfo } from 'net';\nimport type { IncomingMessage, ServerResponse } from 'http';\nimport type { DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { executeHooks } from '@/hooks';\nimport {\n executeTools,\n filterToolsByUsage,\n formatCompletedResponse,\n normalizeToPythonIdentifier,\n ProgrammaticToolCallingName,\n ProgrammaticToolCallingSchema,\n ProgrammaticToolCallingDescription,\n} from '@/tools/ProgrammaticToolCalling';\nimport {\n BashProgrammaticToolCallingSchema,\n BashProgrammaticToolCallingDescription,\n filterBashToolsByUsage,\n normalizeToBashIdentifier,\n} from '@/tools/BashProgrammaticToolCalling';\nimport {\n executeLocalBash,\n executeLocalCode,\n getLocalSessionId,\n shellQuote,\n} from './LocalExecutionEngine';\nimport { Constants } from '@/common';\n\nconst DEFAULT_TIMEOUT = 60000;\nconst LocalProgrammaticToolCallingSchema = {\n ...ProgrammaticToolCallingSchema,\n properties: {\n ...ProgrammaticToolCallingSchema.properties,\n lang: {\n type: 'string',\n enum: ['py', 'python', 'bash', 'sh'],\n default: 'bash',\n description:\n 'Local engine runtime for orchestration code. Defaults to bash; use py/python for Python orchestration.',\n },\n },\n} as const;\n\ntype ToolBridge = {\n url: string;\n token: string;\n close: () => Promise<void>;\n};\n\ntype ToolRequest = {\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n};\n\nconst BRIDGE_AUTH_HEADER = 'x-librechat-bridge-token';\n\nfunction constantTimeEquals(a: string, b: string): boolean {\n const aBuf = Buffer.from(a, 'utf8');\n const bBuf = Buffer.from(b, 'utf8');\n if (aBuf.length !== bBuf.length) {\n return false;\n }\n return timingSafeEqual(aBuf, bBuf);\n}\n\ntype LocalProgrammaticRuntime = 'python' | 'bash';\n\ntype LocalProgrammaticParams = {\n code: string;\n timeout?: number;\n lang?: string;\n runtime?: string;\n language?: string;\n};\n\ntype ToolFilter = (toolDefs: t.LCTool[], code: string) => t.LCTool[];\n\nfunction resolveRuntime(params: LocalProgrammaticParams): LocalProgrammaticRuntime {\n const rawRuntime = params.lang ?? params.runtime ?? params.language ?? 'bash';\n return rawRuntime === 'py' || rawRuntime === 'python' ? 'python' : 'bash';\n}\n\nfunction toSerializable(value: unknown): unknown {\n if (value === undefined) {\n return null;\n }\n return value;\n}\n\nasync function readRequestBody(req: IncomingMessage): Promise<ToolRequest> {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n const raw = Buffer.concat(chunks).toString('utf8');\n if (raw === '') {\n return {};\n }\n return JSON.parse(raw) as ToolRequest;\n}\n\nfunction writeJson(res: ServerResponse, status: number, value: unknown): void {\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(value));\n}\n\n/**\n * Run the host's `PreToolUse` hook chain for a single bridge call.\n * Returns the (possibly rewritten) input and a `denyReason` if any\n * matcher returned `decision: 'deny'` or `'ask'`. `'ask'` collapses\n * to deny because the bridge can't raise a LangGraph interrupt from\n * inside an HTTP handler — fail-closed matches the rest of the SDK\n * when HITL is unavailable.\n *\n * @internal Exported for tests so the deny / allow / updatedInput /\n * ask branches can be exercised without standing up the full HTTP\n * bridge.\n */\nexport async function applyPreToolUseHooksForBridge(\n hookContext: t.ProgrammaticHookContext,\n toolName: string,\n toolUseId: string,\n toolInput: Record<string, unknown>\n): Promise<{ input: Record<string, unknown>; denyReason?: string }> {\n if (hookContext.registry == null) {\n return { input: toolInput };\n }\n const result = await executeHooks({\n registry: hookContext.registry,\n input: {\n hook_event_name: 'PreToolUse',\n runId: hookContext.runId,\n threadId: hookContext.threadId,\n agentId: hookContext.agentId,\n toolName,\n toolInput,\n toolUseId,\n stepId: '',\n turn: 0,\n },\n sessionId: hookContext.runId,\n matchQuery: toolName,\n }).catch(() => undefined);\n if (result == null) {\n return { input: toolInput };\n }\n const nextInput =\n result.updatedInput != null\n ? (result.updatedInput as Record<string, unknown>)\n : toolInput;\n if (result.decision === 'deny' || result.decision === 'ask') {\n return {\n input: nextInput,\n denyReason:\n result.reason ??\n (result.decision === 'ask'\n ? `Tool \"${toolName}\" requires human approval; bridge cannot raise an interrupt — denying.`\n : `Tool \"${toolName}\" denied by PreToolUse hook.`),\n };\n }\n return { input: nextInput };\n}\n\nasync function createToolBridge(\n toolMap: t.ToolMap,\n hookContext?: t.ProgrammaticHookContext\n): Promise<ToolBridge> {\n const token = randomBytes(32).toString('hex');\n const server = createServer((req, res) => {\n // `?mode=text` returns the already-serialized result as the body\n // (or the error message at non-2xx). Python/Node callers stay on\n // JSON; bash callers using curl can avoid pulling in a JSON\n // parser dependency (Codex P2 #19 — `python3` was a hard\n // requirement for the bash bridge, breaking minimal containers).\n const url = new URL(req.url ?? '/', 'http://127.0.0.1');\n const isTextMode = url.searchParams.get('mode') === 'text';\n if (req.method !== 'POST' || url.pathname !== '/tool') {\n if (isTextMode) {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n } else {\n writeJson(res, 404, { error: 'Not found' });\n }\n return;\n }\n\n const presented = req.headers[BRIDGE_AUTH_HEADER];\n const presentedToken = Array.isArray(presented) ? presented[0] : presented;\n if (\n typeof presentedToken !== 'string' ||\n !constantTimeEquals(presentedToken, token)\n ) {\n if (isTextMode) {\n res.writeHead(401, { 'Content-Type': 'text/plain' });\n res.end('Unauthorized');\n } else {\n writeJson(res, 401, { error: 'Unauthorized' });\n }\n return;\n }\n\n readRequestBody(req)\n .then(async (body) => {\n if (typeof body.name !== 'string' || body.name === '') {\n const message = 'Tool request is missing a tool name.';\n if (isTextMode) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end(message);\n } else {\n writeJson(res, 400, {\n call_id: body.id ?? 'invalid',\n result: null,\n is_error: true,\n error_message: message,\n });\n }\n return;\n }\n\n const callId = body.id ?? `local_call_${randomUUID()}`;\n let effectiveInput: Record<string, unknown> = body.input ?? {};\n if (hookContext != null) {\n const gate = await applyPreToolUseHooksForBridge(\n hookContext,\n body.name,\n callId,\n effectiveInput\n );\n if (gate.denyReason != null) {\n const denyMsg = gate.denyReason;\n if (isTextMode) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n res.end(denyMsg);\n } else {\n writeJson(res, 500, {\n call_id: callId,\n result: null,\n is_error: true,\n error_message: denyMsg,\n });\n }\n return;\n }\n effectiveInput = gate.input;\n }\n\n const [result] = await executeTools(\n [\n {\n id: callId,\n name: body.name,\n input: effectiveInput,\n },\n ],\n toolMap\n );\n\n if (isTextMode) {\n if (result.is_error === true) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n res.end(result.error_message ?? `Tool ${body.name} failed`);\n } else {\n const value = toSerializable(result.result);\n const text =\n typeof value === 'string' ? value : JSON.stringify(value);\n res.writeHead(200, { 'Content-Type': 'text/plain' });\n res.end(text);\n }\n return;\n }\n\n writeJson(res, 200, {\n ...result,\n result: toSerializable(result.result),\n });\n })\n .catch((error: Error) => {\n if (isTextMode) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n res.end(error.message);\n } else {\n writeJson(res, 500, {\n call_id: 'error',\n result: null,\n is_error: true,\n error_message: error.message,\n });\n }\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(0, '127.0.0.1', resolve);\n });\n\n const address = server.address() as AddressInfo;\n return {\n url: `http://127.0.0.1:${address.port}/tool`,\n token,\n close: () =>\n new Promise((resolve, reject) => {\n server.close((error) => (error ? reject(error) : resolve()));\n }),\n };\n}\n\nfunction indent(code: string): string {\n return code\n .split('\\n')\n .map((line) => ` ${line}`)\n .join('\\n');\n}\n\nfunction createPythonProgram(\n code: string,\n toolDefs: t.LCTool[],\n bridgeUrl: string,\n bridgeToken: string\n): string {\n const functionDefs = toolDefs\n .map((def) => {\n const pythonName = normalizeToPythonIdentifier(def.name);\n return [\n `async def ${pythonName}(**kwargs):`,\n ` return await __librechat_call_tool(${JSON.stringify(def.name)}, kwargs)`,\n ].join('\\n');\n })\n .join('\\n\\n');\n\n return `\nimport asyncio\nimport json\nimport urllib.request\n\n__LIBRECHAT_TOOL_BRIDGE = ${JSON.stringify(bridgeUrl)}\n__LIBRECHAT_TOOL_TOKEN = ${JSON.stringify(bridgeToken)}\n\nasync def __librechat_call_tool(name, payload):\n body = json.dumps({\"name\": name, \"input\": payload}).encode(\"utf-8\")\n headers = {\n \"Content-Type\": \"application/json\",\n ${JSON.stringify(BRIDGE_AUTH_HEADER)}: __LIBRECHAT_TOOL_TOKEN,\n }\n\n def request():\n req = urllib.request.Request(__LIBRECHAT_TOOL_BRIDGE, data=body, headers=headers, method=\"POST\")\n with urllib.request.urlopen(req, timeout=300) as response:\n return response.read().decode(\"utf-8\")\n\n raw = await asyncio.to_thread(request)\n result = json.loads(raw)\n if result.get(\"is_error\"):\n raise RuntimeError(result.get(\"error_message\") or f\"Tool {name} failed\")\n return result.get(\"result\")\n\n${functionDefs}\n\nasync def __librechat_main():\n${indent(code)}\n\nasyncio.run(__librechat_main())\n`.trimStart();\n}\n\nexport function _createBashProgramForTests(\n code: string,\n toolDefs: t.LCTool[],\n bridgeUrl: string,\n bridgeToken: string\n): string {\n return createBashProgram(code, toolDefs, bridgeUrl, bridgeToken);\n}\n\nfunction createBashProgram(\n code: string,\n toolDefs: t.LCTool[],\n bridgeUrl: string,\n bridgeToken: string\n): string {\n const functions = toolDefs\n .map((def) => {\n const bashName = normalizeToBashIdentifier(def.name);\n return [\n `${bashName}() {`,\n ' local payload=\"${1:-}\"',\n ' if [ -z \"$payload\" ]; then payload=\\'{}\\'; fi',\n ` __librechat_call_tool ${shellQuote(def.name)} \"$payload\"`,\n '}',\n ].join('\\n');\n })\n .join('\\n\\n');\n\n return `\n__LIBRECHAT_TOOL_BRIDGE=${shellQuote(bridgeUrl)}\n__LIBRECHAT_TOOL_HEADER=${shellQuote(BRIDGE_AUTH_HEADER)}\n__LIBRECHAT_TOOL_TOKEN=${shellQuote(bridgeToken)}\n\n# Bridge call helper. Tries curl first (universally available, no\n# JSON parser needed thanks to the bridge's ?mode=text endpoint),\n# falls back to python3 for environments without curl. Codex P2 #19\n# flagged that the prior python3-only path broke minimal containers\n# (and Windows hosts without a python3 binary on PATH). Tool names\n# come from Constants.* and are always safe identifiers, so we can\n# splice them into JSON without an escape pass.\n__librechat_call_tool() {\n local tool_name=\"$1\"\n local payload=\"$2\"\n if command -v curl >/dev/null 2>&1; then\n local body=\"{\\\\\"name\\\\\":\\\\\"$tool_name\\\\\",\\\\\"input\\\\\":$payload}\"\n local response\n local http_code\n response=$(curl -sS -X POST \\\n -H \"Content-Type: application/json\" \\\n -H \"$__LIBRECHAT_TOOL_HEADER: $__LIBRECHAT_TOOL_TOKEN\" \\\n --data-binary \"$body\" \\\n -w '\\\\n__LIBRECHAT_HTTP_CODE_%{http_code}__' \\\n \"$__LIBRECHAT_TOOL_BRIDGE?mode=text\")\n http_code=$(printf '%s' \"$response\" | sed -n 's/.*__LIBRECHAT_HTTP_CODE_\\\\([0-9][0-9]*\\\\)__$/\\\\1/p')\n local body_only\n body_only=$(printf '%s' \"$response\" | sed 's/__LIBRECHAT_HTTP_CODE_[0-9][0-9]*__$//')\n if [ \"$http_code\" = \"200\" ]; then\n printf '%s' \"$body_only\"\n return 0\n fi\n printf '%s\\\\n' \"$body_only\" >&2\n return 1\n elif command -v python3 >/dev/null 2>&1; then\n python3 - \"$__LIBRECHAT_TOOL_BRIDGE\" \"$tool_name\" \"$payload\" \"$__LIBRECHAT_TOOL_HEADER\" \"$__LIBRECHAT_TOOL_TOKEN\" <<'PY'\nimport json\nimport sys\nimport urllib.request\n\nurl, name, payload, header, token = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]\nbody = json.dumps({\"name\": name, \"input\": json.loads(payload)}).encode(\"utf-8\")\nreq = urllib.request.Request(url, data=body, headers={\"Content-Type\": \"application/json\", header: token}, method=\"POST\")\nwith urllib.request.urlopen(req, timeout=300) as response:\n result = json.loads(response.read().decode(\"utf-8\"))\nif result.get(\"is_error\"):\n print(result.get(\"error_message\") or f\"Tool {name} failed\", file=sys.stderr)\n sys.exit(1)\nvalue = result.get(\"result\")\nif isinstance(value, str):\n print(value)\nelse:\n print(json.dumps(value))\nPY\n else\n printf 'librechat: tool bridge needs either curl or python3 on PATH\\\\n' >&2\n return 1\n fi\n}\n\n${functions}\n\n${code}\n`.trimStart();\n}\n\nfunction getProgrammaticContext(config?: {\n toolCall?: unknown;\n}): Partial<t.ProgrammaticCache> {\n return (config?.toolCall ?? {}) as Partial<t.ProgrammaticCache>;\n}\n\nfunction createEffectiveToolMap(\n toolMap: t.ToolMap,\n toolDefs: t.LCTool[],\n code: string,\n filterTools: ToolFilter\n): { effectiveTools: t.LCTool[]; effectiveMap: t.ToolMap } {\n const effectiveTools = filterTools(toolDefs, code);\n const effectiveMap = new Map<string, t.GenericTool>(\n effectiveTools\n .map((def) => {\n const executable = toolMap.get(def.name);\n return executable == null\n ? undefined\n : ([def.name, executable] as [string, t.GenericTool]);\n })\n .filter((entry): entry is [string, t.GenericTool] => entry != null)\n );\n\n return { effectiveTools, effectiveMap };\n}\n\nasync function runLocalProgrammaticTool(args: {\n params: LocalProgrammaticParams;\n config?: { toolCall?: unknown };\n localConfig: t.LocalExecutionConfig;\n runtime: LocalProgrammaticRuntime;\n}): Promise<[string, t.ProgrammaticExecutionArtifact]> {\n const { toolMap, toolDefs, hookContext } = getProgrammaticContext(args.config);\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error('No toolMap provided for local programmatic execution.');\n }\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error('No tool definitions provided for local programmatic execution.');\n }\n\n const { effectiveTools, effectiveMap } = createEffectiveToolMap(\n toolMap,\n toolDefs,\n args.params.code,\n args.runtime === 'bash' ? filterBashToolsByUsage : filterToolsByUsage\n );\n const bridge = await createToolBridge(effectiveMap, hookContext);\n\n try {\n const timeoutMs = args.params.timeout ?? args.localConfig.timeoutMs ?? DEFAULT_TIMEOUT;\n const result =\n args.runtime === 'bash'\n ? await executeLocalBash(\n createBashProgram(args.params.code, effectiveTools, bridge.url, bridge.token),\n { ...args.localConfig, timeoutMs }\n )\n : await executeLocalCode(\n {\n lang: 'py',\n code: createPythonProgram(args.params.code, effectiveTools, bridge.url, bridge.token),\n },\n { ...args.localConfig, timeoutMs }\n );\n\n if (result.exitCode !== 0 || result.timedOut) {\n throw new Error(\n result.stderr !== ''\n ? result.stderr\n : `Local ${args.runtime} programmatic execution exited with code ${\n result.exitCode ?? 'unknown'\n }`\n );\n }\n\n return formatCompletedResponse({\n status: 'completed',\n session_id: getLocalSessionId(args.localConfig),\n stdout: result.stdout,\n stderr: result.stderr,\n files: [],\n });\n } finally {\n await bridge.close();\n }\n}\n\nexport function createLocalProgrammaticToolCallingTool(\n localConfig: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n return tool(\n async (rawParams, config) => {\n const params = rawParams as LocalProgrammaticParams;\n return runLocalProgrammaticTool({\n params,\n config,\n localConfig,\n runtime: resolveRuntime(params),\n });\n },\n {\n name: ProgrammaticToolCallingName,\n description: `${ProgrammaticToolCallingDescription}\\n\\nLocal engine: runs bash by default, or Python when \\`lang\\` is \\`py\\` or \\`python\\`, on the host machine and calls tools through an in-process localhost bridge.`,\n schema: LocalProgrammaticToolCallingSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalBashProgrammaticToolCallingTool(\n localConfig: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n return tool(\n async (rawParams, config) => {\n const params = rawParams as LocalProgrammaticParams;\n return runLocalProgrammaticTool({\n params,\n config,\n localConfig,\n runtime: 'bash',\n });\n },\n {\n name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n description: `${BashProgrammaticToolCallingDescription}\\n\\nLocal engine: runs this bash orchestration code on the host machine and calls tools through an in-process localhost bridge.`,\n schema: BashProgrammaticToolCallingSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LocalProgrammaticToolCalling.mjs","sources":["../../../../src/tools/local/LocalProgrammaticToolCalling.ts"],"sourcesContent":["import { randomBytes, randomUUID, timingSafeEqual } from 'crypto';\nimport { createServer } from 'http';\nimport { tool } from '@langchain/core/tools';\nimport type { AddressInfo } from 'net';\nimport type { IncomingMessage, ServerResponse } from 'http';\nimport type { DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { executeHooks } from '@/hooks';\nimport {\n executeTools,\n filterToolsByUsage,\n formatCompletedResponse,\n normalizeToPythonIdentifier,\n ProgrammaticToolCallingName,\n ProgrammaticToolCallingSchema,\n ProgrammaticToolCallingDescription,\n} from '@/tools/ProgrammaticToolCalling';\nimport {\n BashProgrammaticToolCallingSchema,\n BashProgrammaticToolCallingDescription,\n filterBashToolsByUsage,\n normalizeToBashIdentifier,\n} from '@/tools/BashProgrammaticToolCalling';\nimport {\n executeLocalBash,\n executeLocalCode,\n getLocalSessionId,\n shellQuote,\n} from './LocalExecutionEngine';\nimport { Constants } from '@/common';\n\nconst DEFAULT_TIMEOUT = 60000;\nconst LocalProgrammaticToolCallingSchema = {\n ...ProgrammaticToolCallingSchema,\n properties: {\n ...ProgrammaticToolCallingSchema.properties,\n lang: {\n type: 'string',\n enum: ['py', 'python', 'bash', 'sh'],\n default: 'bash',\n description:\n 'Local engine runtime for orchestration code. Defaults to bash; use py/python for Python orchestration.',\n },\n },\n} as const;\n\ntype ToolBridge = {\n url: string;\n token: string;\n close: () => Promise<void>;\n};\n\ntype ToolRequest = {\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n};\n\nconst BRIDGE_AUTH_HEADER = 'x-librechat-bridge-token';\n\nfunction constantTimeEquals(a: string, b: string): boolean {\n const aBuf = Buffer.from(a, 'utf8');\n const bBuf = Buffer.from(b, 'utf8');\n if (aBuf.length !== bBuf.length) {\n return false;\n }\n return timingSafeEqual(aBuf, bBuf);\n}\n\ntype LocalProgrammaticRuntime = 'python' | 'bash';\n\ntype LocalProgrammaticParams = {\n code: string;\n timeout?: number;\n lang?: string;\n runtime?: string;\n language?: string;\n};\n\ntype ToolFilter = (toolDefs: t.LCTool[], code: string) => t.LCTool[];\n\nfunction resolveRuntime(\n params: LocalProgrammaticParams\n): LocalProgrammaticRuntime {\n const rawRuntime = params.lang ?? params.runtime ?? params.language ?? 'bash';\n return rawRuntime === 'py' || rawRuntime === 'python' ? 'python' : 'bash';\n}\n\nfunction toSerializable(value: unknown): unknown {\n if (value === undefined) {\n return null;\n }\n return value;\n}\n\nasync function readRequestBody(req: IncomingMessage): Promise<ToolRequest> {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n const raw = Buffer.concat(chunks).toString('utf8');\n if (raw === '') {\n return {};\n }\n return JSON.parse(raw) as ToolRequest;\n}\n\nfunction writeJson(res: ServerResponse, status: number, value: unknown): void {\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(value));\n}\n\n/**\n * Run the host's `PreToolUse` hook chain for a single bridge call.\n * Returns the (possibly rewritten) input and a `denyReason` if any\n * matcher returned `decision: 'deny'` or `'ask'`. `'ask'` collapses\n * to deny because the bridge can't raise a LangGraph interrupt from\n * inside an HTTP handler — fail-closed matches the rest of the SDK\n * when HITL is unavailable.\n *\n * @internal Exported for tests so the deny / allow / updatedInput /\n * ask branches can be exercised without standing up the full HTTP\n * bridge.\n */\nexport async function applyPreToolUseHooksForBridge(\n hookContext: t.ProgrammaticHookContext,\n toolName: string,\n toolUseId: string,\n toolInput: Record<string, unknown>\n): Promise<{ input: Record<string, unknown>; denyReason?: string }> {\n if (hookContext.registry == null) {\n return { input: toolInput };\n }\n const result = await executeHooks({\n registry: hookContext.registry,\n input: {\n hook_event_name: 'PreToolUse',\n runId: hookContext.runId,\n threadId: hookContext.threadId,\n agentId: hookContext.agentId,\n toolName,\n toolInput,\n toolUseId,\n stepId: '',\n turn: 0,\n },\n sessionId: hookContext.runId,\n matchQuery: toolName,\n }).catch(() => undefined);\n if (result == null) {\n return { input: toolInput };\n }\n const nextInput =\n result.updatedInput != null\n ? (result.updatedInput as Record<string, unknown>)\n : toolInput;\n if (result.decision === 'deny' || result.decision === 'ask') {\n return {\n input: nextInput,\n denyReason:\n result.reason ??\n (result.decision === 'ask'\n ? `Tool \"${toolName}\" requires human approval; bridge cannot raise an interrupt — denying.`\n : `Tool \"${toolName}\" denied by PreToolUse hook.`),\n };\n }\n return { input: nextInput };\n}\n\nasync function createToolBridge(\n toolMap: t.ToolMap,\n hookContext?: t.ProgrammaticHookContext\n): Promise<ToolBridge> {\n const token = randomBytes(32).toString('hex');\n const server = createServer((req, res) => {\n // `?mode=text` returns the already-serialized result as the body\n // (or the error message at non-2xx). Python/Node callers stay on\n // JSON; bash callers using curl can avoid pulling in a JSON\n // parser dependency (Codex P2 #19 — `python3` was a hard\n // requirement for the bash bridge, breaking minimal containers).\n const url = new URL(req.url ?? '/', 'http://127.0.0.1');\n const isTextMode = url.searchParams.get('mode') === 'text';\n if (req.method !== 'POST' || url.pathname !== '/tool') {\n if (isTextMode) {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n } else {\n writeJson(res, 404, { error: 'Not found' });\n }\n return;\n }\n\n const presented = req.headers[BRIDGE_AUTH_HEADER];\n const presentedToken = Array.isArray(presented) ? presented[0] : presented;\n if (\n typeof presentedToken !== 'string' ||\n !constantTimeEquals(presentedToken, token)\n ) {\n if (isTextMode) {\n res.writeHead(401, { 'Content-Type': 'text/plain' });\n res.end('Unauthorized');\n } else {\n writeJson(res, 401, { error: 'Unauthorized' });\n }\n return;\n }\n\n readRequestBody(req)\n .then(async (body) => {\n if (typeof body.name !== 'string' || body.name === '') {\n const message = 'Tool request is missing a tool name.';\n if (isTextMode) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end(message);\n } else {\n writeJson(res, 400, {\n call_id: body.id ?? 'invalid',\n result: null,\n is_error: true,\n error_message: message,\n });\n }\n return;\n }\n\n const callId = body.id ?? `local_call_${randomUUID()}`;\n let effectiveInput: Record<string, unknown> = body.input ?? {};\n if (hookContext != null) {\n const gate = await applyPreToolUseHooksForBridge(\n hookContext,\n body.name,\n callId,\n effectiveInput\n );\n if (gate.denyReason != null) {\n const denyMsg = gate.denyReason;\n if (isTextMode) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n res.end(denyMsg);\n } else {\n writeJson(res, 500, {\n call_id: callId,\n result: null,\n is_error: true,\n error_message: denyMsg,\n });\n }\n return;\n }\n effectiveInput = gate.input;\n }\n\n const [result] = await executeTools(\n [\n {\n id: callId,\n name: body.name,\n input: effectiveInput,\n },\n ],\n toolMap\n );\n\n if (isTextMode) {\n if (result.is_error === true) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n res.end(result.error_message ?? `Tool ${body.name} failed`);\n } else {\n const value = toSerializable(result.result);\n const text =\n typeof value === 'string' ? value : JSON.stringify(value);\n res.writeHead(200, { 'Content-Type': 'text/plain' });\n res.end(text);\n }\n return;\n }\n\n writeJson(res, 200, {\n ...result,\n result: toSerializable(result.result),\n });\n })\n .catch((error: Error) => {\n if (isTextMode) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n res.end(error.message);\n } else {\n writeJson(res, 500, {\n call_id: 'error',\n result: null,\n is_error: true,\n error_message: error.message,\n });\n }\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(0, '127.0.0.1', resolve);\n });\n\n const address = server.address() as AddressInfo;\n return {\n url: `http://127.0.0.1:${address.port}/tool`,\n token,\n close: () =>\n new Promise((resolve, reject) => {\n server.close((error) => (error ? reject(error) : resolve()));\n }),\n };\n}\n\nfunction indent(code: string): string {\n return code\n .split('\\n')\n .map((line) => ` ${line}`)\n .join('\\n');\n}\n\nfunction createPythonProgram(\n code: string,\n toolDefs: t.LCTool[],\n bridgeUrl: string,\n bridgeToken: string\n): string {\n const functionDefs = toolDefs\n .map((def) => {\n const pythonName = normalizeToPythonIdentifier(def.name);\n return [\n `async def ${pythonName}(**kwargs):`,\n ` return await __librechat_call_tool(${JSON.stringify(def.name)}, kwargs)`,\n ].join('\\n');\n })\n .join('\\n\\n');\n\n return `\nimport asyncio\nimport json\nimport urllib.request\n\n__LIBRECHAT_TOOL_BRIDGE = ${JSON.stringify(bridgeUrl)}\n__LIBRECHAT_TOOL_TOKEN = ${JSON.stringify(bridgeToken)}\n\nasync def __librechat_call_tool(name, payload):\n body = json.dumps({\"name\": name, \"input\": payload}).encode(\"utf-8\")\n headers = {\n \"Content-Type\": \"application/json\",\n ${JSON.stringify(BRIDGE_AUTH_HEADER)}: __LIBRECHAT_TOOL_TOKEN,\n }\n\n def request():\n req = urllib.request.Request(__LIBRECHAT_TOOL_BRIDGE, data=body, headers=headers, method=\"POST\")\n with urllib.request.urlopen(req, timeout=300) as response:\n return response.read().decode(\"utf-8\")\n\n raw = await asyncio.to_thread(request)\n result = json.loads(raw)\n if result.get(\"is_error\"):\n raise RuntimeError(result.get(\"error_message\") or f\"Tool {name} failed\")\n return result.get(\"result\")\n\n${functionDefs}\n\nasync def __librechat_main():\n${indent(code)}\n\nasyncio.run(__librechat_main())\n`.trimStart();\n}\n\nexport function _createBashProgramForTests(\n code: string,\n toolDefs: t.LCTool[],\n bridgeUrl: string,\n bridgeToken: string\n): string {\n return createBashProgram(code, toolDefs, bridgeUrl, bridgeToken);\n}\n\nfunction createBashProgram(\n code: string,\n toolDefs: t.LCTool[],\n bridgeUrl: string,\n bridgeToken: string\n): string {\n const functions = toolDefs\n .map((def) => {\n const bashName = normalizeToBashIdentifier(def.name);\n return [\n `${bashName}() {`,\n ' local payload=\"${1:-}\"',\n ' if [ -z \"$payload\" ]; then payload=\\'{}\\'; fi',\n ` __librechat_call_tool ${shellQuote(def.name)} \"$payload\"`,\n '}',\n ].join('\\n');\n })\n .join('\\n\\n');\n\n return `\n__LIBRECHAT_TOOL_BRIDGE=${shellQuote(bridgeUrl)}\n__LIBRECHAT_TOOL_HEADER=${shellQuote(BRIDGE_AUTH_HEADER)}\n__LIBRECHAT_TOOL_TOKEN=${shellQuote(bridgeToken)}\n\n# Bridge call helper. Tries curl first (universally available, no\n# JSON parser needed thanks to the bridge's ?mode=text endpoint),\n# falls back to python3 for environments without curl. Codex P2 #19\n# flagged that the prior python3-only path broke minimal containers\n# (and Windows hosts without a python3 binary on PATH). Tool names\n# come from Constants.* and are always safe identifiers, so we can\n# splice them into JSON without an escape pass.\n__librechat_call_tool() {\n local tool_name=\"$1\"\n local payload=\"$2\"\n if command -v curl >/dev/null 2>&1; then\n local body=\"{\\\\\"name\\\\\":\\\\\"$tool_name\\\\\",\\\\\"input\\\\\":$payload}\"\n local response\n local http_code\n response=$(curl -sS -X POST \\\n -H \"Content-Type: application/json\" \\\n -H \"$__LIBRECHAT_TOOL_HEADER: $__LIBRECHAT_TOOL_TOKEN\" \\\n --data-binary \"$body\" \\\n -w '\\\\n__LIBRECHAT_HTTP_CODE_%{http_code}__' \\\n \"$__LIBRECHAT_TOOL_BRIDGE?mode=text\")\n http_code=$(printf '%s' \"$response\" | sed -n 's/.*__LIBRECHAT_HTTP_CODE_\\\\([0-9][0-9]*\\\\)__$/\\\\1/p')\n local body_only\n body_only=$(printf '%s' \"$response\" | sed 's/__LIBRECHAT_HTTP_CODE_[0-9][0-9]*__$//')\n if [ \"$http_code\" = \"200\" ]; then\n printf '%s' \"$body_only\"\n return 0\n fi\n printf '%s\\\\n' \"$body_only\" >&2\n return 1\n elif command -v python3 >/dev/null 2>&1; then\n python3 - \"$__LIBRECHAT_TOOL_BRIDGE\" \"$tool_name\" \"$payload\" \"$__LIBRECHAT_TOOL_HEADER\" \"$__LIBRECHAT_TOOL_TOKEN\" <<'PY'\nimport json\nimport sys\nimport urllib.request\n\nurl, name, payload, header, token = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]\nbody = json.dumps({\"name\": name, \"input\": json.loads(payload)}).encode(\"utf-8\")\nreq = urllib.request.Request(url, data=body, headers={\"Content-Type\": \"application/json\", header: token}, method=\"POST\")\nwith urllib.request.urlopen(req, timeout=300) as response:\n result = json.loads(response.read().decode(\"utf-8\"))\nif result.get(\"is_error\"):\n print(result.get(\"error_message\") or f\"Tool {name} failed\", file=sys.stderr)\n sys.exit(1)\nvalue = result.get(\"result\")\nif isinstance(value, str):\n print(value)\nelse:\n print(json.dumps(value))\nPY\n else\n printf 'librechat: tool bridge needs either curl or python3 on PATH\\\\n' >&2\n return 1\n fi\n}\n\n${functions}\n\n${code}\n`.trimStart();\n}\n\nfunction getProgrammaticContext(config?: {\n toolCall?: unknown;\n}): Partial<t.ProgrammaticCache> {\n return (config?.toolCall ?? {}) as Partial<t.ProgrammaticCache>;\n}\n\nfunction createEffectiveToolMap(\n toolMap: t.ToolMap,\n toolDefs: t.LCTool[],\n code: string,\n filterTools: ToolFilter\n): { effectiveTools: t.LCTool[]; effectiveMap: t.ToolMap } {\n const effectiveTools = filterTools(toolDefs, code);\n const effectiveMap = new Map<string, t.GenericTool>(\n effectiveTools\n .map((def) => {\n const executable = toolMap.get(def.name);\n return executable == null\n ? undefined\n : ([def.name, executable] as [string, t.GenericTool]);\n })\n .filter((entry): entry is [string, t.GenericTool] => entry != null)\n );\n\n return { effectiveTools, effectiveMap };\n}\n\nasync function runLocalProgrammaticTool(args: {\n params: LocalProgrammaticParams;\n config?: { toolCall?: unknown };\n localConfig: t.LocalExecutionConfig;\n runtime: LocalProgrammaticRuntime;\n}): Promise<[string, t.ProgrammaticExecutionArtifact]> {\n const { toolMap, toolDefs, hookContext } = getProgrammaticContext(\n args.config\n );\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error('No toolMap provided for local programmatic execution.');\n }\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error(\n 'No tool definitions provided for local programmatic execution.'\n );\n }\n\n const { effectiveTools, effectiveMap } = createEffectiveToolMap(\n toolMap,\n toolDefs,\n args.params.code,\n args.runtime === 'bash' ? filterBashToolsByUsage : filterToolsByUsage\n );\n const bridge = await createToolBridge(effectiveMap, hookContext);\n\n try {\n const timeoutMs =\n args.params.timeout ?? args.localConfig.timeoutMs ?? DEFAULT_TIMEOUT;\n const result =\n args.runtime === 'bash'\n ? await executeLocalBash(\n createBashProgram(\n args.params.code,\n effectiveTools,\n bridge.url,\n bridge.token\n ),\n { ...args.localConfig, timeoutMs }\n )\n : await executeLocalCode(\n {\n lang: 'py',\n code: createPythonProgram(\n args.params.code,\n effectiveTools,\n bridge.url,\n bridge.token\n ),\n },\n { ...args.localConfig, timeoutMs }\n );\n\n if (result.exitCode !== 0 || result.timedOut) {\n throw new Error(\n result.stderr !== ''\n ? result.stderr\n : `Local ${args.runtime} programmatic execution exited with code ${\n result.exitCode ?? 'unknown'\n }`\n );\n }\n\n return formatCompletedResponse({\n status: 'completed',\n session_id: getLocalSessionId(args.localConfig),\n stdout: result.stdout,\n stderr: result.stderr,\n files: [],\n });\n } finally {\n await bridge.close();\n }\n}\n\nexport function createLocalProgrammaticToolCallingTool(\n localConfig: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n return tool(\n async (rawParams, config) => {\n const params = rawParams as LocalProgrammaticParams;\n return runLocalProgrammaticTool({\n params,\n config,\n localConfig,\n runtime: resolveRuntime(params),\n });\n },\n {\n name: ProgrammaticToolCallingName,\n description: `${ProgrammaticToolCallingDescription}\\n\\nLocal engine: runs bash by default, or Python when \\`lang\\` is \\`py\\` or \\`python\\`, on the host machine and calls tools through an in-process localhost bridge.`,\n schema: LocalProgrammaticToolCallingSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalBashProgrammaticToolCallingTool(\n localConfig: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n return tool(\n async (rawParams, config) => {\n const params = rawParams as LocalProgrammaticParams;\n return runLocalProgrammaticTool({\n params,\n config,\n localConfig,\n runtime: 'bash',\n });\n },\n {\n name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,\n description: `${BashProgrammaticToolCallingDescription}\\n\\nLocal engine: runs this bash orchestration code on the host machine and calls tools through an in-process localhost bridge.`,\n schema: BashProgrammaticToolCallingSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AA+BA,MAAM,eAAe,GAAG,KAAK;AAC7B,MAAM,kCAAkC,GAAG;AACzC,IAAA,GAAG,6BAA6B;AAChC,IAAA,UAAU,EAAE;QACV,GAAG,6BAA6B,CAAC,UAAU;AAC3C,QAAA,IAAI,EAAE;AACJ,YAAA,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC;AACpC,YAAA,OAAO,EAAE,MAAM;AACf,YAAA,WAAW,EACT,wGAAwG;AAC3G,SAAA;AACF,KAAA;CACO;AAcV,MAAM,kBAAkB,GAAG,0BAA0B;AAErD,SAAS,kBAAkB,CAAC,CAAS,EAAE,CAAS,EAAA;IAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC;IACnC,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE;AAC/B,QAAA,OAAO,KAAK;IACd;AACA,IAAA,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC;AACpC;AAcA,SAAS,cAAc,CACrB,MAA+B,EAAA;AAE/B,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM;AAC7E,IAAA,OAAO,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,QAAQ,GAAG,QAAQ,GAAG,MAAM;AAC3E;AAEA,SAAS,cAAc,CAAC,KAAc,EAAA;AACpC,IAAA,IAAI,KAAK,KAAK,SAAS,EAAE;AACvB,QAAA,OAAO,IAAI;IACb;AACA,IAAA,OAAO,KAAK;AACd;AAEA,eAAe,eAAe,CAAC,GAAoB,EAAA;IACjD,MAAM,MAAM,GAAa,EAAE;AAC3B,IAAA,WAAW,MAAM,KAAK,IAAI,GAAG,EAAE;QAC7B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClE;AACA,IAAA,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;AAClD,IAAA,IAAI,GAAG,KAAK,EAAE,EAAE;AACd,QAAA,OAAO,EAAE;IACX;AACA,IAAA,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB;AACvC;AAEA,SAAS,SAAS,CAAC,GAAmB,EAAE,MAAc,EAAE,KAAc,EAAA;IACpE,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;IAC7D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAChC;AAEA;;;;;;;;;;;AAWG;AACI,eAAe,6BAA6B,CACjD,WAAsC,EACtC,QAAgB,EAChB,SAAiB,EACjB,SAAkC,EAAA;AAElC,IAAA,IAAI,WAAW,CAAC,QAAQ,IAAI,IAAI,EAAE;AAChC,QAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;IAC7B;AACA,IAAA,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,QAAQ,EAAE,WAAW,CAAC,QAAQ;AAC9B,QAAA,KAAK,EAAE;AACL,YAAA,eAAe,EAAE,YAAY;YAC7B,KAAK,EAAE,WAAW,CAAC,KAAK;YACxB,QAAQ,EAAE,WAAW,CAAC,QAAQ;YAC9B,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,QAAQ;YACR,SAAS;YACT,SAAS;AACT,YAAA,MAAM,EAAE,EAAE;AACV,YAAA,IAAI,EAAE,CAAC;AACR,SAAA;QACD,SAAS,EAAE,WAAW,CAAC,KAAK;AAC5B,QAAA,UAAU,EAAE,QAAQ;KACrB,CAAC,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC;AACzB,IAAA,IAAI,MAAM,IAAI,IAAI,EAAE;AAClB,QAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;IAC7B;AACA,IAAA,MAAM,SAAS,GACb,MAAM,CAAC,YAAY,IAAI;UAClB,MAAM,CAAC;UACR,SAAS;AACf,IAAA,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,IAAI,MAAM,CAAC,QAAQ,KAAK,KAAK,EAAE;QAC3D,OAAO;AACL,YAAA,KAAK,EAAE,SAAS;YAChB,UAAU,EACR,MAAM,CAAC,MAAM;AACb,iBAAC,MAAM,CAAC,QAAQ,KAAK;sBACjB,CAAA,MAAA,EAAS,QAAQ,CAAA,sEAAA;AACnB,sBAAE,CAAA,MAAA,EAAS,QAAQ,CAAA,4BAAA,CAA8B,CAAC;SACvD;IACH;AACA,IAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;AAC7B;AAEA,eAAe,gBAAgB,CAC7B,OAAkB,EAClB,WAAuC,EAAA;IAEvC,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;IAC7C,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,KAAI;;;;;;AAMvC,QAAA,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC;AACvD,QAAA,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,MAAM;AAC1D,QAAA,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,EAAE;YACrD,IAAI,UAAU,EAAE;gBACd,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACpD,gBAAA,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC;YACtB;iBAAO;gBACL,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;YAC7C;YACA;QACF;QAEA,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC;AACjD,QAAA,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;QAC1E,IACE,OAAO,cAAc,KAAK,QAAQ;AAClC,YAAA,CAAC,kBAAkB,CAAC,cAAc,EAAE,KAAK,CAAC,EAC1C;YACA,IAAI,UAAU,EAAE;gBACd,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACpD,gBAAA,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;YACzB;iBAAO;gBACL,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;YAChD;YACA;QACF;QAEA,eAAe,CAAC,GAAG;AAChB,aAAA,IAAI,CAAC,OAAO,IAAI,KAAI;AACnB,YAAA,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,EAAE;gBACrD,MAAM,OAAO,GAAG,sCAAsC;gBACtD,IAAI,UAAU,EAAE;oBACd,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACpD,oBAAA,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;gBAClB;qBAAO;AACL,oBAAA,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE;AAClB,wBAAA,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,SAAS;AAC7B,wBAAA,MAAM,EAAE,IAAI;AACZ,wBAAA,QAAQ,EAAE,IAAI;AACd,wBAAA,aAAa,EAAE,OAAO;AACvB,qBAAA,CAAC;gBACJ;gBACA;YACF;YAEA,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,IAAI,CAAA,WAAA,EAAc,UAAU,EAAE,CAAA,CAAE;AACtD,YAAA,IAAI,cAAc,GAA4B,IAAI,CAAC,KAAK,IAAI,EAAE;AAC9D,YAAA,IAAI,WAAW,IAAI,IAAI,EAAE;AACvB,gBAAA,MAAM,IAAI,GAAG,MAAM,6BAA6B,CAC9C,WAAW,EACX,IAAI,CAAC,IAAI,EACT,MAAM,EACN,cAAc,CACf;AACD,gBAAA,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,EAAE;AAC3B,oBAAA,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU;oBAC/B,IAAI,UAAU,EAAE;wBACd,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACpD,wBAAA,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;oBAClB;yBAAO;AACL,wBAAA,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE;AAClB,4BAAA,OAAO,EAAE,MAAM;AACf,4BAAA,MAAM,EAAE,IAAI;AACZ,4BAAA,QAAQ,EAAE,IAAI;AACd,4BAAA,aAAa,EAAE,OAAO;AACvB,yBAAA,CAAC;oBACJ;oBACA;gBACF;AACA,gBAAA,cAAc,GAAG,IAAI,CAAC,KAAK;YAC7B;AAEA,YAAA,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,YAAY,CACjC;AACE,gBAAA;AACE,oBAAA,EAAE,EAAE,MAAM;oBACV,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,oBAAA,KAAK,EAAE,cAAc;AACtB,iBAAA;aACF,EACD,OAAO,CACR;YAED,IAAI,UAAU,EAAE;AACd,gBAAA,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE;oBAC5B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACpD,oBAAA,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,CAAA,KAAA,EAAQ,IAAI,CAAC,IAAI,CAAA,OAAA,CAAS,CAAC;gBAC7D;qBAAO;oBACL,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC;AAC3C,oBAAA,MAAM,IAAI,GACR,OAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;oBAC3D,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACpD,oBAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;gBACf;gBACA;YACF;AAEA,YAAA,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE;AAClB,gBAAA,GAAG,MAAM;AACT,gBAAA,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC;AACtC,aAAA,CAAC;AACJ,QAAA,CAAC;AACA,aAAA,KAAK,CAAC,CAAC,KAAY,KAAI;YACtB,IAAI,UAAU,EAAE;gBACd,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACpD,gBAAA,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;YACxB;iBAAO;AACL,gBAAA,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE;AAClB,oBAAA,OAAO,EAAE,OAAO;AAChB,oBAAA,MAAM,EAAE,IAAI;AACZ,oBAAA,QAAQ,EAAE,IAAI;oBACd,aAAa,EAAE,KAAK,CAAC,OAAO;AAC7B,iBAAA,CAAC;YACJ;AACF,QAAA,CAAC,CAAC;AACN,IAAA,CAAC,CAAC;IAEF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,KAAI;AAC1C,QAAA,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC;AACxC,IAAA,CAAC,CAAC;AAEF,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAiB;IAC/C,OAAO;AACL,QAAA,GAAG,EAAE,CAAA,iBAAA,EAAoB,OAAO,CAAC,IAAI,CAAA,KAAA,CAAO;QAC5C,KAAK;AACL,QAAA,KAAK,EAAE,MACL,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;YAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC;AAC9D,QAAA,CAAC,CAAC;KACL;AACH;AAEA,SAAS,MAAM,CAAC,IAAY,EAAA;AAC1B,IAAA,OAAO;SACJ,KAAK,CAAC,IAAI;SACV,GAAG,CAAC,CAAC,IAAI,KAAK,CAAA,EAAA,EAAK,IAAI,CAAA,CAAE;SACzB,IAAI,CAAC,IAAI,CAAC;AACf;AAEA,SAAS,mBAAmB,CAC1B,IAAY,EACZ,QAAoB,EACpB,SAAiB,EACjB,WAAmB,EAAA;IAEnB,MAAM,YAAY,GAAG;AAClB,SAAA,GAAG,CAAC,CAAC,GAAG,KAAI;QACX,MAAM,UAAU,GAAG,2BAA2B,CAAC,GAAG,CAAC,IAAI,CAAC;QACxD,OAAO;AACL,YAAA,CAAA,UAAA,EAAa,UAAU,CAAA,WAAA,CAAa;YACpC,CAAA,qCAAA,EAAwC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA,SAAA,CAAW;AAC5E,SAAA,CAAC,IAAI,CAAC,IAAI,CAAC;AACd,IAAA,CAAC;SACA,IAAI,CAAC,MAAM,CAAC;IAEf,OAAO;;;;;AAKmB,0BAAA,EAAA,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;AAC1B,yBAAA,EAAA,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;AAMhD,IAAA,EAAA,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAA;;;;;;;;;;;;;;EActC,YAAY;;;EAGZ,MAAM,CAAC,IAAI,CAAC;;;CAGb,CAAC,SAAS,EAAE;AACb;AAEM,SAAU,0BAA0B,CACxC,IAAY,EACZ,QAAoB,EACpB,SAAiB,EACjB,WAAmB,EAAA;IAEnB,OAAO,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,CAAC;AAClE;AAEA,SAAS,iBAAiB,CACxB,IAAY,EACZ,QAAoB,EACpB,SAAiB,EACjB,WAAmB,EAAA;IAEnB,MAAM,SAAS,GAAG;AACf,SAAA,GAAG,CAAC,CAAC,GAAG,KAAI;QACX,MAAM,QAAQ,GAAG,yBAAyB,CAAC,GAAG,CAAC,IAAI,CAAC;QACpD,OAAO;AACL,YAAA,CAAA,EAAG,QAAQ,CAAA,IAAA,CAAM;YACjB,0BAA0B;YAC1B,iDAAiD;AACjD,YAAA,CAAA,wBAAA,EAA2B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA,WAAA,CAAa;YAC5D,GAAG;AACJ,SAAA,CAAC,IAAI,CAAC,IAAI,CAAC;AACd,IAAA,CAAC;SACA,IAAI,CAAC,MAAM,CAAC;IAEf,OAAO;0BACiB,UAAU,CAAC,SAAS,CAAC;0BACrB,UAAU,CAAC,kBAAkB,CAAC;yBAC/B,UAAU,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyD9C,SAAS;;EAET,IAAI;CACL,CAAC,SAAS,EAAE;AACb;AAEA,SAAS,sBAAsB,CAAC,MAE/B,EAAA;AACC,IAAA,QAAQ,MAAM,EAAE,QAAQ,IAAI,EAAE;AAChC;AAEA,SAAS,sBAAsB,CAC7B,OAAkB,EAClB,QAAoB,EACpB,IAAY,EACZ,WAAuB,EAAA;IAEvB,MAAM,cAAc,GAAG,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC;AAClD,IAAA,MAAM,YAAY,GAAG,IAAI,GAAG,CAC1B;AACG,SAAA,GAAG,CAAC,CAAC,GAAG,KAAI;QACX,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;QACxC,OAAO,UAAU,IAAI;AACnB,cAAE;cACC,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAA6B;AACzD,IAAA,CAAC;SACA,MAAM,CAAC,CAAC,KAAK,KAAuC,KAAK,IAAI,IAAI,CAAC,CACtE;AAED,IAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE;AACzC;AAEA,eAAe,wBAAwB,CAAC,IAKvC,EAAA;AACC,IAAA,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,sBAAsB,CAC/D,IAAI,CAAC,MAAM,CACZ;IAED,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE;AACzC,QAAA,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC;IAC1E;IACA,IAAI,QAAQ,IAAI,IAAI,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;AAC7C,QAAA,MAAM,IAAI,KAAK,CACb,gEAAgE,CACjE;IACH;AAEA,IAAA,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,GAAG,sBAAsB,CAC7D,OAAO,EACP,QAAQ,EACR,IAAI,CAAC,MAAM,CAAC,IAAI,EAChB,IAAI,CAAC,OAAO,KAAK,MAAM,GAAG,sBAAsB,GAAG,kBAAkB,CACtE;IACD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,YAAY,EAAE,WAAW,CAAC;AAEhE,IAAA,IAAI;AACF,QAAA,MAAM,SAAS,GACb,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,SAAS,IAAI,eAAe;AACtE,QAAA,MAAM,MAAM,GACV,IAAI,CAAC,OAAO,KAAK;AACf,cAAE,MAAM,gBAAgB,CACtB,iBAAiB,CACf,IAAI,CAAC,MAAM,CAAC,IAAI,EAChB,cAAc,EACd,MAAM,CAAC,GAAG,EACV,MAAM,CAAC,KAAK,CACb,EACD,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE;cAElC,MAAM,gBAAgB,CACtB;AACE,gBAAA,IAAI,EAAE,IAAI;AACV,gBAAA,IAAI,EAAE,mBAAmB,CACvB,IAAI,CAAC,MAAM,CAAC,IAAI,EAChB,cAAc,EACd,MAAM,CAAC,GAAG,EACV,MAAM,CAAC,KAAK,CACb;aACF,EACD,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,CACnC;QAEL,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE;AAC5C,YAAA,MAAM,IAAI,KAAK,CACb,MAAM,CAAC,MAAM,KAAK;kBACd,MAAM,CAAC;AACT,kBAAE,CAAA,MAAA,EAAS,IAAI,CAAC,OAAO,CAAA,yCAAA,EACrB,MAAM,CAAC,QAAQ,IAAI,SACrB,CAAA,CAAE,CACL;QACH;AAEA,QAAA,OAAO,uBAAuB,CAAC;AAC7B,YAAA,MAAM,EAAE,WAAW;AACnB,YAAA,UAAU,EAAE,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC;YAC/C,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;AACrB,YAAA,KAAK,EAAE,EAAE;AACV,SAAA,CAAC;IACJ;YAAU;AACR,QAAA,MAAM,MAAM,CAAC,KAAK,EAAE;IACtB;AACF;AAEM,SAAU,sCAAsC,CACpD,WAAA,GAAsC,EAAE,EAAA;IAExC,OAAO,IAAI,CACT,OAAO,SAAS,EAAE,MAAM,KAAI;QAC1B,MAAM,MAAM,GAAG,SAAoC;AACnD,QAAA,OAAO,wBAAwB,CAAC;YAC9B,MAAM;YACN,MAAM;YACN,WAAW;AACX,YAAA,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC;AAChC,SAAA,CAAC;AACJ,IAAA,CAAC,EACD;AACE,QAAA,IAAI,EAAE,2BAA2B;QACjC,WAAW,EAAE,CAAA,EAAG,kCAAkC,CAAA,oKAAA,CAAsK;AACxN,QAAA,MAAM,EAAE,kCAAkC;QAC1C,cAAc,EAAE,SAAS,CAAC,oBAAoB;AAC/C,KAAA,CACF;AACH;AAEM,SAAU,0CAA0C,CACxD,WAAA,GAAsC,EAAE,EAAA;IAExC,OAAO,IAAI,CACT,OAAO,SAAS,EAAE,MAAM,KAAI;QAC1B,MAAM,MAAM,GAAG,SAAoC;AACnD,QAAA,OAAO,wBAAwB,CAAC;YAC9B,MAAM;YACN,MAAM;YACN,WAAW;AACX,YAAA,OAAO,EAAE,MAAM;AAChB,SAAA,CAAC;AACJ,IAAA,CAAC,EACD;QACE,IAAI,EAAE,SAAS,CAAC,8BAA8B;QAC9C,WAAW,EAAE,CAAA,EAAG,sCAAsC,CAAA,+HAAA,CAAiI;AACvL,QAAA,MAAM,EAAE,iCAAiC;QACzC,cAAc,EAAE,SAAS,CAAC,oBAAoB;AAC/C,KAAA,CACF;AACH;;;;"}
|
|
@@ -125,38 +125,112 @@ export type ToolEndEvent = {
|
|
|
125
125
|
index: number;
|
|
126
126
|
type?: 'tool_call';
|
|
127
127
|
};
|
|
128
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Closed set of resource kinds for sandbox file caching. Defined as a
|
|
130
|
+
* `as const` tuple so the runtime list and the TypeScript union can't
|
|
131
|
+
* drift on future additions — adding a new kind to the tuple updates
|
|
132
|
+
* both at once.
|
|
133
|
+
*
|
|
134
|
+
* - `skill`: shared per skill identity. Cross-user-within-tenant
|
|
135
|
+
* sharing. Codeapi sessionKey omits the user dimension. `version`
|
|
136
|
+
* is required (skill's monotonic counter scopes cache per revision).
|
|
137
|
+
* - `agent`: shared per agent identity. Same sharing semantic as
|
|
138
|
+
* skills.
|
|
139
|
+
* - `user`: user-private. Codeapi sessionKey is keyed by the
|
|
140
|
+
* requesting user from auth context. Used for chat attachments
|
|
141
|
+
* and code-output files.
|
|
142
|
+
*/
|
|
143
|
+
export declare const CODE_ENV_KINDS: readonly ["skill", "agent", "user"];
|
|
144
|
+
export type CodeEnvKind = (typeof CODE_ENV_KINDS)[number];
|
|
145
|
+
type CodeEnvFileBase = {
|
|
146
|
+
/**
|
|
147
|
+
* **Storage file id** — the per-file uuid the file_server returns
|
|
148
|
+
* at upload time. Identifies the bytes at
|
|
149
|
+
* `<storage_session_id>/<id>` in the object bucket; used by the
|
|
150
|
+
* worker to fetch the file and by codeapi's auth layer for the
|
|
151
|
+
* upload-existence check.
|
|
152
|
+
*
|
|
153
|
+
* Distinct from `resource_id` below — that's the entity that owns
|
|
154
|
+
* this file's storage session (skill `_id`, agent id, etc.), used
|
|
155
|
+
* for sessionKey resolution. Conflating the two caused
|
|
156
|
+
* shared-kind authorization to fail because the sessionKey
|
|
157
|
+
* re-derivation used the storage nanoid where the resource id was
|
|
158
|
+
* required. See codeapi #1455 review.
|
|
159
|
+
*/
|
|
129
160
|
id: string;
|
|
161
|
+
/**
|
|
162
|
+
* **Resource id** — the entity that owns this file's storage
|
|
163
|
+
* session. Skill `_id` for `kind: 'skill'`, agent id for `'agent'`,
|
|
164
|
+
* informational only for `'user'` (codeapi resolves user identity
|
|
165
|
+
* from auth context). Kept on the type for shape uniformity across
|
|
166
|
+
* kinds.
|
|
167
|
+
*
|
|
168
|
+
* Drives codeapi's `resolveSessionKey` switch — the sessionKey
|
|
169
|
+
* embeds this value (`<tenant>:<kind>:<resource_id>[:v:<version>]`)
|
|
170
|
+
* so cross-user-within-tenant sharing for shared kinds is a
|
|
171
|
+
* designed property of the kind switch.
|
|
172
|
+
*/
|
|
173
|
+
resource_id: string;
|
|
130
174
|
name: string;
|
|
131
|
-
session_id: string;
|
|
132
175
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* file plus a user attachment in the same call).
|
|
176
|
+
* Storage session — the long-lived bucket where this file's bytes
|
|
177
|
+
* live in object storage. Distinct from the (transient) execution
|
|
178
|
+
* session id that appears at the top level of an execute response;
|
|
179
|
+
* the two used to share the field name `session_id` and the
|
|
180
|
+
* conflation caused real bugs. See codeapi #1455 / agents #148.
|
|
139
181
|
*/
|
|
140
|
-
|
|
182
|
+
storage_session_id: string;
|
|
141
183
|
};
|
|
184
|
+
/**
|
|
185
|
+
* `CodeEnvFile` is a discriminated union on `kind`. `version` is
|
|
186
|
+
* statically required for `kind: 'skill'` and statically forbidden
|
|
187
|
+
* for `agent` / `user` — the constraint holds at compile time on
|
|
188
|
+
* every consumer, not just on codeapi's runtime validator.
|
|
189
|
+
*
|
|
190
|
+
* Codeapi switches on `kind` to derive the sessionKey for cache
|
|
191
|
+
* scoping (`<tenant>:<kind>:<id>[:v:<version>]`). Cross-user sharing
|
|
192
|
+
* for `kind: 'skill'` / `'agent'` is a designed property of the
|
|
193
|
+
* kind switch.
|
|
194
|
+
*/
|
|
195
|
+
export type CodeEnvFile = (CodeEnvFileBase & {
|
|
196
|
+
kind: 'skill';
|
|
197
|
+
version: number;
|
|
198
|
+
}) | (CodeEnvFileBase & {
|
|
199
|
+
kind: 'agent';
|
|
200
|
+
}) | (CodeEnvFileBase & {
|
|
201
|
+
kind: 'user';
|
|
202
|
+
});
|
|
142
203
|
export type CodeExecutionToolParams = undefined | {
|
|
204
|
+
/** Execution session — see `CodeSessionContext.session_id`. */
|
|
143
205
|
session_id?: string;
|
|
144
206
|
user_id?: string;
|
|
145
207
|
files?: CodeEnvFile[];
|
|
146
208
|
};
|
|
147
209
|
export type FileRef = {
|
|
210
|
+
/**
|
|
211
|
+
* Storage file id (the per-file uuid). See `CodeEnvFile.id` for
|
|
212
|
+
* the full motivation behind the storage-vs-resource split.
|
|
213
|
+
*/
|
|
148
214
|
id: string;
|
|
215
|
+
/**
|
|
216
|
+
* Resource id — the entity that owns this file's storage session.
|
|
217
|
+
* Optional on `FileRef` (post-execute artifact refs may not carry
|
|
218
|
+
* resource provenance for outputs); required on `CodeEnvFile`
|
|
219
|
+
* (the input wire shape).
|
|
220
|
+
*/
|
|
221
|
+
resource_id?: string;
|
|
149
222
|
name: string;
|
|
150
223
|
path?: string;
|
|
151
|
-
/** Session ID this file belongs to (for multi-session file tracking) */
|
|
152
|
-
session_id?: string;
|
|
153
224
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* `_injected_files` when a subsequent execute references a mix of
|
|
157
|
-
* files uploaded under different entities.
|
|
225
|
+
* Storage session this file lives in. See `CodeEnvFile.storage_session_id`
|
|
226
|
+
* for the full motivation.
|
|
158
227
|
*/
|
|
159
|
-
|
|
228
|
+
storage_session_id?: string;
|
|
229
|
+
/** Resource kind — see `CodeEnvFile.kind`. */
|
|
230
|
+
kind?: CodeEnvKind;
|
|
231
|
+
/** Resource version — see `CodeEnvFile.version`. Only meaningful when
|
|
232
|
+
* `kind === 'skill'`. */
|
|
233
|
+
version?: number;
|
|
160
234
|
/**
|
|
161
235
|
* `true` when the codeapi sandbox echoed this entry as an unchanged
|
|
162
236
|
* passthrough of an input the caller already owns (skill files,
|
|
@@ -169,6 +243,11 @@ export type FileRef = {
|
|
|
169
243
|
};
|
|
170
244
|
export type FileRefs = FileRef[];
|
|
171
245
|
export type ExecuteResult = {
|
|
246
|
+
/**
|
|
247
|
+
* Execution session id — the (transient) sandbox run that produced
|
|
248
|
+
* this output. Distinct from per-file `storage_session_id` on the
|
|
249
|
+
* files array.
|
|
250
|
+
*/
|
|
172
251
|
session_id: string;
|
|
173
252
|
stdout: string;
|
|
174
253
|
stderr: string;
|
|
@@ -224,6 +303,7 @@ export type ToolCallRequest = {
|
|
|
224
303
|
turn?: number;
|
|
225
304
|
/** Code execution session context for session continuity in event-driven mode */
|
|
226
305
|
codeSessionContext?: {
|
|
306
|
+
/** Execution session — see `CodeSessionContext.session_id`. */
|
|
227
307
|
session_id: string;
|
|
228
308
|
files?: CodeEnvFile[];
|
|
229
309
|
};
|
|
@@ -709,6 +789,7 @@ export type PTCToolResult = {
|
|
|
709
789
|
*/
|
|
710
790
|
export type ProgrammaticExecutionResponse = {
|
|
711
791
|
status: 'tool_call_required' | 'completed' | 'error' | unknown;
|
|
792
|
+
/** Execution session — see `CodeSessionContext.session_id`. */
|
|
712
793
|
session_id?: string;
|
|
713
794
|
/** Present when status='tool_call_required' */
|
|
714
795
|
continuation_token?: string;
|
|
@@ -724,6 +805,7 @@ export type ProgrammaticExecutionResponse = {
|
|
|
724
805
|
* Artifact returned by the PTC tool
|
|
725
806
|
*/
|
|
726
807
|
export type ProgrammaticExecutionArtifact = {
|
|
808
|
+
/** Execution session — see `CodeSessionContext.session_id`. */
|
|
727
809
|
session_id?: string;
|
|
728
810
|
files?: FileRefs;
|
|
729
811
|
};
|
|
@@ -749,7 +831,12 @@ export type ProgrammaticToolCallingParams = {
|
|
|
749
831
|
* Stored in Graph.sessions and injected into subsequent tool invocations.
|
|
750
832
|
*/
|
|
751
833
|
export type CodeSessionContext = {
|
|
752
|
-
/**
|
|
834
|
+
/**
|
|
835
|
+
* Execution session id — the (transient) sandbox run id. Used by
|
|
836
|
+
* ToolNode to thread session continuity into the next code-execution
|
|
837
|
+
* tool call. Distinct from per-file `storage_session_id` carried on
|
|
838
|
+
* `files`.
|
|
839
|
+
*/
|
|
753
840
|
session_id: string;
|
|
754
841
|
/** Files generated in this session (for context/tracking) */
|
|
755
842
|
files?: FileRefs;
|
|
@@ -761,6 +848,7 @@ export type CodeSessionContext = {
|
|
|
761
848
|
* Used to extract session context after tool completion.
|
|
762
849
|
*/
|
|
763
850
|
export type CodeExecutionArtifact = {
|
|
851
|
+
/** Execution session — see `CodeSessionContext.session_id`. */
|
|
764
852
|
session_id?: string;
|
|
765
853
|
files?: FileRefs;
|
|
766
854
|
};
|
|
@@ -774,3 +862,4 @@ export type ToolSessionContext = CodeSessionContext;
|
|
|
774
862
|
* Keys are tool constants (e.g., Constants.EXECUTE_CODE, Constants.PROGRAMMATIC_TOOL_CALLING).
|
|
775
863
|
*/
|
|
776
864
|
export type ToolSessionMap = Map<string, ToolSessionContext>;
|
|
865
|
+
export {};
|
package/package.json
CHANGED
|
@@ -46,7 +46,7 @@ function printSessionContext(run: Run<t.IState>, label: string): void {
|
|
|
46
46
|
console.log(` Latest session_id: ${session.session_id}`);
|
|
47
47
|
console.log(` Files tracked: ${session.files?.length ?? 0}`);
|
|
48
48
|
for (const file of session.files ?? []) {
|
|
49
|
-
console.log(` - ${file.name} (
|
|
49
|
+
console.log(` - ${file.name} (storage: ${file.storage_session_id})`);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -200,13 +200,13 @@ Tell me what version it shows.
|
|
|
200
200
|
|
|
201
201
|
if (finalSession) {
|
|
202
202
|
const files = finalSession.files ?? [];
|
|
203
|
-
const uniqueSessionIds = new Set(files.map((f) => f.
|
|
203
|
+
const uniqueSessionIds = new Set(files.map((f) => f.storage_session_id));
|
|
204
204
|
console.log(`\nTotal files tracked: ${files.length}`);
|
|
205
|
-
console.log(`Unique
|
|
205
|
+
console.log(`Unique storage_session_ids: ${uniqueSessionIds.size}`);
|
|
206
206
|
console.log('\nFiles:');
|
|
207
207
|
for (const file of files) {
|
|
208
208
|
console.log(
|
|
209
|
-
` - ${file.name} (
|
|
209
|
+
` - ${file.name} (storage: ${file.storage_session_id?.slice(0, 20)}...)`
|
|
210
210
|
);
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -163,8 +163,14 @@ function createBashExecutionTool(
|
|
|
163
163
|
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
164
164
|
|
|
165
165
|
return {
|
|
166
|
-
session_id,
|
|
166
|
+
storage_session_id: session_id,
|
|
167
|
+
/* `/files` fallback returns code-output files belonging
|
|
168
|
+
* to the user; tag them user-private. */
|
|
169
|
+
kind: 'user' as const,
|
|
167
170
|
id,
|
|
171
|
+
/* `resource_id` informational for `kind: 'user'` —
|
|
172
|
+
* codeapi derives sessionKey from auth context. */
|
|
173
|
+
resource_id: id,
|
|
168
174
|
name: file.metadata['original-filename'],
|
|
169
175
|
};
|
|
170
176
|
});
|
|
@@ -241,11 +247,16 @@ function createBashExecutionTool(
|
|
|
241
247
|
{
|
|
242
248
|
session_id: result.session_id,
|
|
243
249
|
files: result.files,
|
|
244
|
-
},
|
|
250
|
+
} satisfies t.CodeExecutionArtifact,
|
|
245
251
|
];
|
|
246
252
|
}
|
|
247
253
|
|
|
248
|
-
return [
|
|
254
|
+
return [
|
|
255
|
+
formattedOutput.trim(),
|
|
256
|
+
{
|
|
257
|
+
session_id: result.session_id,
|
|
258
|
+
} satisfies t.CodeExecutionArtifact,
|
|
259
|
+
];
|
|
249
260
|
} catch (error) {
|
|
250
261
|
throw new Error(
|
|
251
262
|
`Execution error:\n\n${(error as Error | undefined)?.message}`
|
|
@@ -252,12 +252,12 @@ export function createBashProgrammaticToolCallingTool(
|
|
|
252
252
|
const params = rawParams as { code: string; timeout?: number };
|
|
253
253
|
const { code, timeout = DEFAULT_TIMEOUT } = params;
|
|
254
254
|
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
255
|
+
const toolCall = (config.toolCall ?? {}) as ToolCall &
|
|
256
|
+
Partial<t.ProgrammaticCache> & {
|
|
257
|
+
session_id?: string;
|
|
258
|
+
_injected_files?: t.CodeEnvFile[];
|
|
259
|
+
};
|
|
260
|
+
const { toolMap, toolDefs, session_id, _injected_files } = toolCall;
|
|
261
261
|
|
|
262
262
|
if (toolMap == null || toolMap.size === 0) {
|
|
263
263
|
throw new Error(
|
|
@@ -135,8 +135,8 @@ function createCodeExecutionTool(
|
|
|
135
135
|
};
|
|
136
136
|
/**
|
|
137
137
|
* Extract session context from config.toolCall (injected by ToolNode).
|
|
138
|
-
* - session_id:
|
|
139
|
-
* - _injected_files: File refs to pass directly (avoids /files endpoint race condition)
|
|
138
|
+
* - session_id: associates with the previous run.
|
|
139
|
+
* - _injected_files: File refs to pass directly (avoids /files endpoint race condition).
|
|
140
140
|
*/
|
|
141
141
|
const { session_id, _injected_files } = (config.toolCall ?? {}) as {
|
|
142
142
|
session_id?: string;
|
|
@@ -153,7 +153,10 @@ function createCodeExecutionTool(
|
|
|
153
153
|
/**
|
|
154
154
|
* File injection priority:
|
|
155
155
|
* 1. Use _injected_files from ToolNode (avoids /files endpoint race condition)
|
|
156
|
-
* 2. Fall back to fetching from /files endpoint if session_id
|
|
156
|
+
* 2. Fall back to fetching from /files endpoint if session_id
|
|
157
|
+
* provided but no injected files. The /files lookup still uses the
|
|
158
|
+
* same id value — codeapi stores output files under the exec id as
|
|
159
|
+
* their storage prefix, so the two values coincide here.
|
|
157
160
|
*/
|
|
158
161
|
if (_injected_files && _injected_files.length > 0) {
|
|
159
162
|
postData.files = _injected_files;
|
|
@@ -186,8 +189,16 @@ function createCodeExecutionTool(
|
|
|
186
189
|
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
187
190
|
|
|
188
191
|
return {
|
|
189
|
-
session_id,
|
|
192
|
+
storage_session_id: session_id,
|
|
193
|
+
/* `/files` fallback returns code-output files belonging
|
|
194
|
+
* to the user; tag them user-private. */
|
|
195
|
+
kind: 'user' as const,
|
|
190
196
|
id,
|
|
197
|
+
/* `resource_id` is informational for `kind: 'user'`
|
|
198
|
+
* (codeapi derives sessionKey from auth context); use
|
|
199
|
+
* the same value as `id` since the `/files` fallback
|
|
200
|
+
* doesn't carry separate provenance. */
|
|
201
|
+
resource_id: id,
|
|
191
202
|
name: file.metadata['original-filename'],
|
|
192
203
|
};
|
|
193
204
|
});
|
|
@@ -259,11 +270,16 @@ function createCodeExecutionTool(
|
|
|
259
270
|
{
|
|
260
271
|
session_id: result.session_id,
|
|
261
272
|
files: result.files,
|
|
262
|
-
},
|
|
273
|
+
} satisfies t.CodeExecutionArtifact,
|
|
263
274
|
];
|
|
264
275
|
}
|
|
265
276
|
|
|
266
|
-
return [
|
|
277
|
+
return [
|
|
278
|
+
formattedOutput.trim(),
|
|
279
|
+
{
|
|
280
|
+
session_id: result.session_id,
|
|
281
|
+
} satisfies t.CodeExecutionArtifact,
|
|
282
|
+
];
|
|
267
283
|
} catch (error) {
|
|
268
284
|
throw new Error(
|
|
269
285
|
`Execution error:\n\n${(error as Error | undefined)?.message}`
|
|
@@ -304,8 +304,14 @@ export async function fetchSessionFiles(
|
|
|
304
304
|
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
305
305
|
|
|
306
306
|
return {
|
|
307
|
-
|
|
307
|
+
storage_session_id: sessionId,
|
|
308
|
+
/* `/files` fallback returns code-output files belonging to
|
|
309
|
+
* the user; tag them user-private. */
|
|
310
|
+
kind: 'user' as const,
|
|
308
311
|
id,
|
|
312
|
+
/* `resource_id` informational for `kind: 'user'` —
|
|
313
|
+
* codeapi derives sessionKey from auth context. */
|
|
314
|
+
resource_id: id,
|
|
309
315
|
name: (file.metadata as Record<string, unknown>)[
|
|
310
316
|
'original-filename'
|
|
311
317
|
] as string,
|
|
@@ -588,7 +594,7 @@ export function formatCompletedResponse(
|
|
|
588
594
|
{
|
|
589
595
|
session_id: response.session_id,
|
|
590
596
|
files: response.files,
|
|
591
|
-
},
|
|
597
|
+
} satisfies t.ProgrammaticExecutionArtifact,
|
|
592
598
|
];
|
|
593
599
|
}
|
|
594
600
|
|
|
@@ -629,13 +635,13 @@ export function createProgrammaticToolCallingTool(
|
|
|
629
635
|
const params = rawParams as { code: string; timeout?: number };
|
|
630
636
|
const { code, timeout = DEFAULT_TIMEOUT } = params;
|
|
631
637
|
|
|
632
|
-
// Extra params injected by ToolNode (follows web_search pattern)
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
638
|
+
// Extra params injected by ToolNode (follows web_search pattern).
|
|
639
|
+
const toolCall = (config.toolCall ?? {}) as ToolCall &
|
|
640
|
+
Partial<t.ProgrammaticCache> & {
|
|
641
|
+
session_id?: string;
|
|
642
|
+
_injected_files?: t.CodeEnvFile[];
|
|
643
|
+
};
|
|
644
|
+
const { toolMap, toolDefs, session_id, _injected_files } = toolCall;
|
|
639
645
|
|
|
640
646
|
if (toolMap == null || toolMap.size === 0) {
|
|
641
647
|
throw new Error(
|
|
@@ -671,7 +677,8 @@ export function createProgrammaticToolCallingTool(
|
|
|
671
677
|
/**
|
|
672
678
|
* File injection priority:
|
|
673
679
|
* 1. Use _injected_files from ToolNode (avoids /files endpoint race condition)
|
|
674
|
-
* 2. Fall back to fetching from /files endpoint if session_id
|
|
680
|
+
* 2. Fall back to fetching from /files endpoint if session_id
|
|
681
|
+
* provided but no injected files.
|
|
675
682
|
*/
|
|
676
683
|
let files: t.CodeEnvFile[] | undefined;
|
|
677
684
|
if (_injected_files && _injected_files.length > 0) {
|