@librechat/agents 3.1.83 → 3.1.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +26 -3
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +1 -0
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/events.cjs +2 -1
  6. package/dist/cjs/events.cjs.map +1 -1
  7. package/dist/cjs/graphs/Graph.cjs +5 -1
  8. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs +3 -2
  10. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  11. package/dist/cjs/main.cjs +4 -0
  12. package/dist/cjs/main.cjs.map +1 -1
  13. package/dist/cjs/tools/BashExecutor.cjs +5 -2
  14. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  15. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +26 -24
  16. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  17. package/dist/cjs/tools/CodeExecutor.cjs +28 -2
  18. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  19. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +130 -56
  20. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  21. package/dist/cjs/tools/ToolNode.cjs +7 -5
  22. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  23. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +52 -13
  24. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  25. package/dist/cjs/tools/ptcTimeout.cjs +56 -0
  26. package/dist/cjs/tools/ptcTimeout.cjs.map +1 -0
  27. package/dist/esm/agents/AgentContext.mjs +27 -4
  28. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  29. package/dist/esm/common/enum.mjs +1 -0
  30. package/dist/esm/common/enum.mjs.map +1 -1
  31. package/dist/esm/events.mjs +2 -1
  32. package/dist/esm/events.mjs.map +1 -1
  33. package/dist/esm/graphs/Graph.mjs +5 -1
  34. package/dist/esm/graphs/Graph.mjs.map +1 -1
  35. package/dist/esm/graphs/MultiAgentGraph.mjs +3 -2
  36. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  37. package/dist/esm/main.mjs +3 -3
  38. package/dist/esm/tools/BashExecutor.mjs +6 -3
  39. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  40. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +26 -25
  41. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  42. package/dist/esm/tools/CodeExecutor.mjs +27 -3
  43. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  44. package/dist/esm/tools/ProgrammaticToolCalling.mjs +131 -58
  45. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  46. package/dist/esm/tools/ToolNode.mjs +7 -5
  47. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  48. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +54 -15
  49. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  50. package/dist/esm/tools/ptcTimeout.mjs +50 -0
  51. package/dist/esm/tools/ptcTimeout.mjs.map +1 -0
  52. package/dist/types/agents/AgentContext.d.ts +3 -1
  53. package/dist/types/common/enum.d.ts +2 -1
  54. package/dist/types/tools/BashProgrammaticToolCalling.d.ts +4 -36
  55. package/dist/types/tools/CodeExecutor.d.ts +5 -0
  56. package/dist/types/tools/ProgrammaticToolCalling.d.ts +18 -39
  57. package/dist/types/tools/ptcTimeout.d.ts +25 -0
  58. package/dist/types/types/tools.d.ts +8 -0
  59. package/package.json +1 -1
  60. package/src/agents/AgentContext.ts +32 -3
  61. package/src/agents/__tests__/AgentContext.test.ts +36 -3
  62. package/src/common/enum.ts +1 -0
  63. package/src/events.ts +4 -1
  64. package/src/graphs/MultiAgentGraph.ts +3 -2
  65. package/src/graphs/__tests__/composition.smoke.test.ts +84 -2
  66. package/src/tools/BashExecutor.ts +14 -3
  67. package/src/tools/BashProgrammaticToolCalling.ts +37 -25
  68. package/src/tools/CodeExecutor.ts +36 -2
  69. package/src/tools/ProgrammaticToolCalling.ts +206 -53
  70. package/src/tools/ToolNode.ts +3 -4
  71. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +424 -0
  72. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +31 -1
  73. package/src/tools/local/LocalProgrammaticToolCalling.ts +94 -13
  74. package/src/tools/ptcTimeout.ts +89 -0
  75. package/src/types/tools.ts +12 -0
@@ -13,6 +13,7 @@ import {
13
13
  ANTHROPIC_TOOL_TOKEN_MULTIPLIER,
14
14
  DEFAULT_TOOL_TOKEN_MULTIPLIER,
15
15
  ContentTypes,
16
+ Constants,
16
17
  Providers,
17
18
  } from '@/common';
18
19
  import { createSchemaOnlyTools } from '@/tools/schema';
@@ -389,7 +390,7 @@ export class AgentContext {
389
390
  /**
390
391
  * Builds instructions text for tools that are ONLY callable via programmatic code execution.
391
392
  * These tools cannot be called directly by the LLM but are available through the
392
- * run_tools_with_code tool.
393
+ * configured programmatic tool.
393
394
  *
394
395
  * Includes:
395
396
  * - Code_execution-only tools that are NOT deferred
@@ -416,6 +417,7 @@ export class AgentContext {
416
417
 
417
418
  if (programmaticOnlyTools.length === 0) return '';
418
419
 
420
+ const programmaticTool = this.getProgrammaticToolInstructionTarget();
419
421
  const toolDescriptions = programmaticOnlyTools
420
422
  .map((tool) => {
421
423
  let desc = `- **${tool.name}**`;
@@ -431,12 +433,39 @@ export class AgentContext {
431
433
 
432
434
  return (
433
435
  '\n\n## Programmatic-Only Tools\n\n' +
434
- 'The following tools are available exclusively through the `run_tools_with_code` tool. ' +
435
- 'You cannot call these tools directly; instead, use `run_tools_with_code` with Python code that invokes them.\n\n' +
436
+ `The following tools are available exclusively through the \`${programmaticTool.name}\` tool. ` +
437
+ `You cannot call these tools directly; instead, use \`${programmaticTool.name}\` with ${programmaticTool.language} code that invokes them.\n\n` +
436
438
  toolDescriptions
437
439
  );
438
440
  }
439
441
 
442
+ private getProgrammaticToolInstructionTarget(): {
443
+ name: string;
444
+ language: 'bash' | 'Python';
445
+ } {
446
+ if (this.hasAvailableTool(Constants.BASH_PROGRAMMATIC_TOOL_CALLING)) {
447
+ return {
448
+ name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,
449
+ language: 'bash',
450
+ };
451
+ }
452
+
453
+ if (this.hasAvailableTool(Constants.PROGRAMMATIC_TOOL_CALLING)) {
454
+ return { name: Constants.PROGRAMMATIC_TOOL_CALLING, language: 'Python' };
455
+ }
456
+
457
+ return { name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING, language: 'bash' };
458
+ }
459
+
460
+ private hasAvailableTool(name: string): boolean {
461
+ if (this.toolDefinitions?.some((tool) => tool.name === name)) return true;
462
+ if (this.tools?.some((tool) => 'name' in tool && tool.name === name)) {
463
+ return true;
464
+ }
465
+ if (this.toolMap?.has(name)) return true;
466
+ return this.toolRegistry?.has(name) === true;
467
+ }
468
+
440
469
  /**
441
470
  * Gets the system runnable, creating it lazily if needed.
442
471
  * Includes stable instructions, dynamic additional instructions, and
@@ -1,7 +1,7 @@
1
1
  // src/agents/__tests__/AgentContext.test.ts
2
2
  import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
3
3
  import { AgentContext } from '../AgentContext';
4
- import { Providers } from '@/common';
4
+ import { Constants, Providers } from '@/common';
5
5
  import { addBedrockCacheControl } from '@/messages/cache';
6
6
  import type * as t from '@/types';
7
7
 
@@ -593,7 +593,7 @@ describe('AgentContext', () => {
593
593
  });
594
594
 
595
595
  describe('buildProgrammaticOnlyToolsInstructions', () => {
596
- it('includes code_execution-only tools in system message', () => {
596
+ it('includes code_execution-only tools in system message', async () => {
597
597
  const toolRegistry: t.LCToolRegistry = new Map([
598
598
  [
599
599
  'programmatic_tool',
@@ -606,11 +606,44 @@ describe('AgentContext', () => {
606
606
  ]);
607
607
 
608
608
  const ctx = createBasicContext({
609
- agentConfig: { instructions: 'Base', toolRegistry },
609
+ agentConfig: {
610
+ instructions: 'Base',
611
+ toolDefinitions: [{ name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING }],
612
+ toolRegistry,
613
+ },
610
614
  });
611
615
 
612
616
  const runnable = ctx.systemRunnable;
613
617
  expect(runnable).toBeDefined();
618
+ const result = await runnable!.invoke([]);
619
+ expect(result[0].content).toContain('run_tools_with_bash');
620
+ expect(result[0].content).not.toContain('run_tools_with_code');
621
+ });
622
+
623
+ it('uses Python PTC guidance when only run_tools_with_code is available', async () => {
624
+ const toolRegistry: t.LCToolRegistry = new Map([
625
+ [
626
+ 'programmatic_tool',
627
+ {
628
+ name: 'programmatic_tool',
629
+ description: 'Only callable via code execution',
630
+ allowed_callers: ['code_execution'],
631
+ },
632
+ ],
633
+ ]);
634
+
635
+ const ctx = createBasicContext({
636
+ agentConfig: {
637
+ instructions: 'Base',
638
+ toolDefinitions: [{ name: Constants.PROGRAMMATIC_TOOL_CALLING }],
639
+ toolRegistry,
640
+ },
641
+ });
642
+
643
+ const result = await ctx.systemRunnable!.invoke([]);
644
+ expect(result[0].content).toContain('run_tools_with_code');
645
+ expect(result[0].content).toContain('Python code');
646
+ expect(result[0].content).not.toContain('run_tools_with_bash');
614
647
  });
615
648
 
616
649
  it('excludes direct-callable tools from programmatic section', () => {
@@ -261,4 +261,5 @@ export enum TitleMethod {
261
261
 
262
262
  export enum EnvVar {
263
263
  CODE_BASEURL = 'LIBRECHAT_CODE_BASEURL',
264
+ CODE_API_RUN_TIMEOUT_MS = 'CODE_API_RUN_TIMEOUT_MS',
264
265
  }
package/src/events.ts CHANGED
@@ -90,7 +90,10 @@ export class ToolEndHandler implements t.EventHandler {
90
90
  return;
91
91
  }
92
92
 
93
- if (metadata[Constants.PROGRAMMATIC_TOOL_CALLING] === true) {
93
+ if (
94
+ metadata[Constants.PROGRAMMATIC_TOOL_CALLING] === true ||
95
+ metadata[Constants.BASH_PROGRAMMATIC_TOOL_CALLING] === true
96
+ ) {
94
97
  return;
95
98
  }
96
99
 
@@ -1074,10 +1074,11 @@ export class MultiAgentGraph extends StandardGraph {
1074
1074
  * to pass filtered messages + prompt to the destination agent
1075
1075
  */
1076
1076
  const filteredMessages = state.messages.slice(0, this.startIndex);
1077
+ const promptMessage = new HumanMessage(promptText);
1077
1078
  return {
1078
- messages: [new HumanMessage(promptText)],
1079
+ messages: [promptMessage],
1079
1080
  agentMessages: messagesStateReducer(filteredMessages, [
1080
- new HumanMessage(promptText),
1081
+ promptMessage,
1081
1082
  ]),
1082
1083
  };
1083
1084
  }
@@ -1,11 +1,17 @@
1
- import { HumanMessage } from '@langchain/core/messages';
2
- import type { ToolCall } from '@langchain/core/messages/tool';
1
+ import { HumanMessage, getBufferString } from '@langchain/core/messages';
2
+ import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
3
3
  import type { RunnableConfig } from '@langchain/core/runnables';
4
+ import type { ChatGenerationChunk } from '@langchain/core/outputs';
5
+ import type { ToolCall } from '@langchain/core/messages/tool';
6
+ import type { BaseMessage } from '@langchain/core/messages';
4
7
  import type * as t from '@/types';
5
8
  import { MultiAgentGraph } from '../MultiAgentGraph';
6
9
  import { Constants, Providers } from '@/common';
10
+ import { FakeChatModel } from '@/llm/fake';
7
11
  import { StandardGraph } from '../Graph';
8
12
 
13
+ const CHAIN_PROMPT_PREFIX = 'Previous context:\n';
14
+
9
15
  const makeAgent = (agentId: string): t.AgentInputs => ({
10
16
  agentId,
11
17
  provider: Providers.OPENAI,
@@ -29,6 +35,36 @@ const getAiContents = (messages: t.BaseGraphState['messages']): string[] =>
29
35
  .map((message) => message.content)
30
36
  .filter((content): content is string => typeof content === 'string');
31
37
 
38
+ const getChainPromptContent = (messages: BaseMessage[]): string => {
39
+ const promptMessage = messages.find(
40
+ (message) =>
41
+ message.getType() === 'human' &&
42
+ typeof message.content === 'string' &&
43
+ message.content.startsWith(CHAIN_PROMPT_PREFIX)
44
+ );
45
+ if (promptMessage == null || typeof promptMessage.content !== 'string') {
46
+ throw new Error('Expected chain prompt message');
47
+ }
48
+ return promptMessage.content;
49
+ };
50
+
51
+ class CapturingChatModel extends FakeChatModel {
52
+ readonly invocations: BaseMessage[][] = [];
53
+
54
+ constructor(responses: string[]) {
55
+ super({ responses });
56
+ }
57
+
58
+ override async *_streamResponseChunks(
59
+ messages: BaseMessage[],
60
+ options: this['ParsedCallOptions'],
61
+ runManager?: CallbackManagerForLLMRun
62
+ ): AsyncGenerator<ChatGenerationChunk> {
63
+ this.invocations.push(messages);
64
+ yield* super._streamResponseChunks(messages, options, runManager);
65
+ }
66
+ }
67
+
32
68
  const expectCompiledWorkflow = (
33
69
  workflow: t.CompiledWorkflow | t.CompiledMultiAgentWorkflow
34
70
  ): void => {
@@ -119,6 +155,52 @@ describe('LangGraph composition smoke tests', () => {
119
155
  expect(getAiContents(result.messages)).toEqual(['from A', 'from B']);
120
156
  });
121
157
 
158
+ it('does not duplicate excludeResults chain prompt history for downstream agents', async () => {
159
+ const model = new CapturingChatModel(['from A', 'from B', 'from C']);
160
+ const prompt = (messages: BaseMessage[], startIndex: number): string =>
161
+ `${CHAIN_PROMPT_PREFIX}${getBufferString(messages.slice(startIndex))}`;
162
+ const graph = new MultiAgentGraph({
163
+ runId: 'exclude-results-chain-smoke',
164
+ agents: [makeAgent('A'), makeAgent('B'), makeAgent('C')],
165
+ edges: [
166
+ {
167
+ from: 'A',
168
+ to: 'B',
169
+ edgeType: 'direct',
170
+ prompt,
171
+ excludeResults: true,
172
+ },
173
+ {
174
+ from: 'B',
175
+ to: 'C',
176
+ edgeType: 'direct',
177
+ prompt,
178
+ excludeResults: true,
179
+ },
180
+ ],
181
+ });
182
+ graph.overrideModel = model;
183
+
184
+ const result = await graph
185
+ .createWorkflow()
186
+ .invoke(
187
+ { messages: [new HumanMessage('start')] },
188
+ makeConfig('exclude-results-chain-smoke')
189
+ );
190
+
191
+ expect(getAiContents(result.messages)).toEqual([
192
+ 'from A',
193
+ 'from B',
194
+ 'from C',
195
+ ]);
196
+ expect(model.invocations).toHaveLength(3);
197
+
198
+ const downstreamPrompt = getChainPromptContent(model.invocations[2]);
199
+ const previousPromptCount =
200
+ downstreamPrompt.match(/Human: Previous context:/g)?.length ?? 0;
201
+ expect(previousPromptCount).toBe(1);
202
+ });
203
+
122
204
  it('compiles and invokes a handoff edge using graph-managed transfer tools', async () => {
123
205
  const transferToolCall: ToolCall = {
124
206
  id: 'call_transfer_to_B',
@@ -3,7 +3,12 @@ import fetch, { RequestInit } from 'node-fetch';
3
3
  import { HttpsProxyAgent } from 'https-proxy-agent';
4
4
  import { tool, DynamicStructuredTool } from '@langchain/core/tools';
5
5
  import type * as t from '@/types';
6
- import { emptyOutputMessage, getCodeBaseURL } from './CodeExecutor';
6
+ import {
7
+ emptyOutputMessage,
8
+ buildCodeApiHttpErrorMessage,
9
+ getCodeBaseURL,
10
+ resolveCodeApiAuthHeaders,
11
+ } from './CodeExecutor';
7
12
  import { Constants } from '@/common';
8
13
 
9
14
  config();
@@ -104,6 +109,7 @@ function createBashExecutionTool(
104
109
  ): DynamicStructuredTool {
105
110
  return tool(
106
111
  async (rawInput, config) => {
112
+ const { authHeaders, ...executionParams } = params ?? {};
107
113
  const { command, ...rest } = rawInput as {
108
114
  command: string;
109
115
  args?: string[];
@@ -117,7 +123,7 @@ function createBashExecutionTool(
117
123
  lang: 'bash',
118
124
  code: command,
119
125
  ...rest,
120
- ...params,
126
+ ...executionParams,
121
127
  };
122
128
 
123
129
  /* See `CodeExecutor.ts` for the rationale — `/files/<session_id>`
@@ -137,11 +143,14 @@ function createBashExecutionTool(
137
143
  }
138
144
 
139
145
  try {
146
+ const resolvedAuthHeaders =
147
+ await resolveCodeApiAuthHeaders(authHeaders);
140
148
  const fetchOptions: RequestInit = {
141
149
  method: 'POST',
142
150
  headers: {
143
151
  'Content-Type': 'application/json',
144
152
  'User-Agent': 'LibreChat/1.0',
153
+ ...resolvedAuthHeaders,
145
154
  },
146
155
  body: JSON.stringify(postData),
147
156
  };
@@ -151,7 +160,9 @@ function createBashExecutionTool(
151
160
  }
152
161
  const response = await fetch(EXEC_ENDPOINT, fetchOptions);
153
162
  if (!response.ok) {
154
- throw new Error(`HTTP error! status: ${response.status}`);
163
+ throw new Error(
164
+ await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)
165
+ );
155
166
  }
156
167
 
157
168
  const result: t.ExecuteResult = await response.json();
@@ -1,6 +1,7 @@
1
1
  import { config } from 'dotenv';
2
2
  import { tool, DynamicStructuredTool } from '@langchain/core/tools';
3
3
  import type { ToolCall } from '@langchain/core/messages/tool';
4
+ import type { ProgrammaticToolCallingJsonSchema } from './ptcTimeout';
4
5
  import type * as t from '@/types';
5
6
  import {
6
7
  makeRequest,
@@ -8,6 +9,11 @@ import {
8
9
  formatCompletedResponse,
9
10
  } from './ProgrammaticToolCalling';
10
11
  import { getCodeBaseURL } from './CodeExecutor';
12
+ import {
13
+ clampCodeApiRunTimeoutMs,
14
+ createCodeApiRunTimeoutSchema,
15
+ resolveCodeApiRunTimeoutMs,
16
+ } from './ptcTimeout';
11
17
  import { Constants } from '@/common';
12
18
 
13
19
  config();
@@ -17,7 +23,7 @@ config();
17
23
  // ============================================================================
18
24
 
19
25
  const DEFAULT_MAX_ROUND_TRIPS = 20;
20
- const DEFAULT_TIMEOUT = 60000;
26
+ const DEFAULT_RUN_TIMEOUT_MS = resolveCodeApiRunTimeoutMs();
21
27
 
22
28
  /** Bash reserved words that get `_tool` suffix when used as function names */
23
29
  const BASH_RESERVED = new Set([
@@ -60,7 +66,8 @@ const CORE_RULES = `Rules:
60
66
  - Tools are pre-defined as bash functions—DO NOT redefine them
61
67
  - Each tool function accepts a JSON string argument
62
68
  - Only echo/printf output returns to the model
63
- - Generated files are automatically available in /mnt/data/ for subsequent executions`;
69
+ - Generated files are automatically available in /mnt/data/ for subsequent executions
70
+ - timeout caps one sandbox run/replay iteration, not the total multi-round-trip workflow`;
64
71
 
65
72
  const ADDITIONAL_RULES =
66
73
  '- Tool names normalized: hyphens→underscores, reserved words get `_tool` suffix';
@@ -92,25 +99,25 @@ ${CORE_RULES}`;
92
99
  // Schema
93
100
  // ============================================================================
94
101
 
95
- export const BashProgrammaticToolCallingSchema = {
96
- type: 'object',
97
- properties: {
98
- code: {
99
- type: 'string',
100
- minLength: 1,
101
- description: CODE_PARAM_DESCRIPTION,
102
- },
103
- timeout: {
104
- type: 'integer',
105
- minimum: 1000,
106
- maximum: 300000,
107
- default: DEFAULT_TIMEOUT,
108
- description:
109
- 'Maximum execution time in milliseconds. Default: 60 seconds. Max: 5 minutes.',
102
+ export function createBashProgrammaticToolCallingSchema(
103
+ maxRunTimeoutMs = DEFAULT_RUN_TIMEOUT_MS
104
+ ): ProgrammaticToolCallingJsonSchema {
105
+ return {
106
+ type: 'object',
107
+ properties: {
108
+ code: {
109
+ type: 'string',
110
+ minLength: 1,
111
+ description: CODE_PARAM_DESCRIPTION,
112
+ },
113
+ timeout: createCodeApiRunTimeoutSchema(maxRunTimeoutMs),
110
114
  },
111
- },
112
- required: ['code'],
113
- } as const;
115
+ required: ['code'],
116
+ } as const;
117
+ }
118
+
119
+ export const BashProgrammaticToolCallingSchema =
120
+ createBashProgrammaticToolCallingSchema();
114
121
 
115
122
  export const BashProgrammaticToolCallingName =
116
123
  Constants.BASH_PROGRAMMATIC_TOOL_CALLING;
@@ -242,6 +249,7 @@ export function createBashProgrammaticToolCallingTool(
242
249
  ): DynamicStructuredTool {
243
250
  const baseUrl = initParams.baseUrl ?? getCodeBaseURL();
244
251
  const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;
252
+ const maxRunTimeoutMs = resolveCodeApiRunTimeoutMs(initParams.runTimeoutMs);
245
253
  const proxy = initParams.proxy ?? process.env.PROXY;
246
254
  const debug = initParams.debug ?? process.env.BASH_PTC_DEBUG === 'true';
247
255
  const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;
@@ -249,7 +257,8 @@ export function createBashProgrammaticToolCallingTool(
249
257
  return tool(
250
258
  async (rawParams, config) => {
251
259
  const params = rawParams as { code: string; timeout?: number };
252
- const { code, timeout = DEFAULT_TIMEOUT } = params;
260
+ const { code } = params;
261
+ const timeout = clampCodeApiRunTimeoutMs(params.timeout, maxRunTimeoutMs);
253
262
 
254
263
  const toolCall = (config.toolCall ?? {}) as ToolCall &
255
264
  Partial<t.ProgrammaticCache> & {
@@ -312,7 +321,8 @@ export function createBashProgrammaticToolCallingTool(
312
321
  timeout,
313
322
  ...(files && files.length > 0 ? { files } : {}),
314
323
  },
315
- proxy
324
+ proxy,
325
+ initParams.authHeaders
316
326
  );
317
327
 
318
328
  // ====================================================================
@@ -339,7 +349,8 @@ export function createBashProgrammaticToolCallingTool(
339
349
 
340
350
  const toolResults = await executeTools(
341
351
  response.tool_calls ?? [],
342
- toolMap
352
+ toolMap,
353
+ Constants.BASH_PROGRAMMATIC_TOOL_CALLING
343
354
  );
344
355
 
345
356
  response = await makeRequest(
@@ -348,7 +359,8 @@ export function createBashProgrammaticToolCallingTool(
348
359
  continuation_token: response.continuation_token,
349
360
  tool_results: toolResults,
350
361
  },
351
- proxy
362
+ proxy,
363
+ initParams.authHeaders
352
364
  );
353
365
  }
354
366
 
@@ -379,7 +391,7 @@ export function createBashProgrammaticToolCallingTool(
379
391
  {
380
392
  name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,
381
393
  description: BashProgrammaticToolCallingDescription,
382
- schema: BashProgrammaticToolCallingSchema,
394
+ schema: createBashProgrammaticToolCallingSchema(maxRunTimeoutMs),
383
395
  responseFormat: Constants.CONTENT_AND_ARTIFACT,
384
396
  }
385
397
  );
@@ -70,6 +70,34 @@ const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
70
70
 
71
71
  type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
72
72
 
73
+ export async function resolveCodeApiAuthHeaders(
74
+ authHeaders?: t.CodeApiAuthHeaders
75
+ ): Promise<t.CodeApiAuthHeaderMap> {
76
+ if (authHeaders == null) {
77
+ return {};
78
+ }
79
+ if (typeof authHeaders === 'function') {
80
+ return authHeaders();
81
+ }
82
+ return authHeaders;
83
+ }
84
+
85
+ export async function buildCodeApiHttpErrorMessage(
86
+ method: string,
87
+ endpoint: string,
88
+ response: { status: number; text: () => Promise<string> }
89
+ ): Promise<string> {
90
+ let responseBody = '';
91
+ try {
92
+ responseBody = await response.text();
93
+ } catch {
94
+ responseBody = '';
95
+ }
96
+ const body = responseBody.trim();
97
+ const bodySuffix = body === '' ? '' : `, body: ${body.slice(0, 1000)}`;
98
+ return `CodeAPI request failed: ${method} ${endpoint} returned ${response.status}${bodySuffix}`;
99
+ }
100
+
73
101
  export const CodeExecutionToolDescription = `
74
102
  Runs code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.
75
103
 
@@ -92,6 +120,7 @@ function createCodeExecutionTool(
92
120
  ): DynamicStructuredTool {
93
121
  return tool(
94
122
  async (rawInput, config) => {
123
+ const { authHeaders, ...executionParams } = params ?? {};
95
124
  const { lang, code, ...rest } = rawInput as {
96
125
  lang: SupportedLanguage;
97
126
  code: string;
@@ -111,7 +140,7 @@ function createCodeExecutionTool(
111
140
  lang,
112
141
  code,
113
142
  ...rest,
114
- ...params,
143
+ ...executionParams,
115
144
  };
116
145
 
117
146
  /* File injection: `_injected_files` from ToolNode (set when host
@@ -135,11 +164,14 @@ function createCodeExecutionTool(
135
164
  }
136
165
 
137
166
  try {
167
+ const resolvedAuthHeaders =
168
+ await resolveCodeApiAuthHeaders(authHeaders);
138
169
  const fetchOptions: RequestInit = {
139
170
  method: 'POST',
140
171
  headers: {
141
172
  'Content-Type': 'application/json',
142
173
  'User-Agent': 'LibreChat/1.0',
174
+ ...resolvedAuthHeaders,
143
175
  },
144
176
  body: JSON.stringify(postData),
145
177
  };
@@ -149,7 +181,9 @@ function createCodeExecutionTool(
149
181
  }
150
182
  const response = await fetch(EXEC_ENDPOINT, fetchOptions);
151
183
  if (!response.ok) {
152
- throw new Error(`HTTP error! status: ${response.status}`);
184
+ throw new Error(
185
+ await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)
186
+ );
153
187
  }
154
188
 
155
189
  const result: t.ExecuteResult = await response.json();