@librechat/agents 3.1.83 → 3.1.84

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 (47) 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/events.cjs +2 -1
  4. package/dist/cjs/events.cjs.map +1 -1
  5. package/dist/cjs/main.cjs +2 -0
  6. package/dist/cjs/main.cjs.map +1 -1
  7. package/dist/cjs/tools/BashExecutor.cjs +5 -2
  8. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  9. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +3 -3
  10. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  11. package/dist/cjs/tools/CodeExecutor.cjs +28 -2
  12. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  13. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +107 -34
  14. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  15. package/dist/cjs/tools/ToolNode.cjs +3 -4
  16. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  17. package/dist/esm/agents/AgentContext.mjs +27 -4
  18. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  19. package/dist/esm/events.mjs +2 -1
  20. package/dist/esm/events.mjs.map +1 -1
  21. package/dist/esm/main.mjs +1 -1
  22. package/dist/esm/tools/BashExecutor.mjs +6 -3
  23. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  24. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +3 -3
  25. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  26. package/dist/esm/tools/CodeExecutor.mjs +27 -3
  27. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  28. package/dist/esm/tools/ProgrammaticToolCalling.mjs +108 -35
  29. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  30. package/dist/esm/tools/ToolNode.mjs +3 -4
  31. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  32. package/dist/types/agents/AgentContext.d.ts +3 -1
  33. package/dist/types/tools/CodeExecutor.d.ts +5 -0
  34. package/dist/types/tools/ProgrammaticToolCalling.d.ts +14 -3
  35. package/dist/types/types/tools.d.ts +6 -0
  36. package/package.json +1 -1
  37. package/src/agents/AgentContext.ts +32 -3
  38. package/src/agents/__tests__/AgentContext.test.ts +36 -3
  39. package/src/events.ts +4 -1
  40. package/src/tools/BashExecutor.ts +14 -3
  41. package/src/tools/BashProgrammaticToolCalling.ts +6 -3
  42. package/src/tools/CodeExecutor.ts +36 -2
  43. package/src/tools/ProgrammaticToolCalling.ts +175 -30
  44. package/src/tools/ToolNode.ts +3 -4
  45. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +321 -0
  46. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +31 -1
  47. package/src/types/tools.ts +10 -0
@@ -186,13 +186,15 @@ export declare class AgentContext {
186
186
  /**
187
187
  * Builds instructions text for tools that are ONLY callable via programmatic code execution.
188
188
  * These tools cannot be called directly by the LLM but are available through the
189
- * run_tools_with_code tool.
189
+ * configured programmatic tool.
190
190
  *
191
191
  * Includes:
192
192
  * - Code_execution-only tools that are NOT deferred
193
193
  * - Code_execution-only tools that ARE deferred but have been discovered via tool search
194
194
  */
195
195
  private buildProgrammaticOnlyToolsInstructions;
196
+ private getProgrammaticToolInstructionTarget;
197
+ private hasAvailableTool;
196
198
  /**
197
199
  * Gets the system runnable, creating it lazily if needed.
198
200
  * Includes stable instructions, dynamic additional instructions, and
@@ -25,6 +25,11 @@ export declare const CodeExecutionToolSchema: {
25
25
  };
26
26
  readonly required: readonly ["lang", "code"];
27
27
  };
28
+ export declare function resolveCodeApiAuthHeaders(authHeaders?: t.CodeApiAuthHeaders): Promise<t.CodeApiAuthHeaderMap>;
29
+ export declare function buildCodeApiHttpErrorMessage(method: string, endpoint: string, response: {
30
+ status: number;
31
+ text: () => Promise<string>;
32
+ }): Promise<string>;
28
33
  export declare const CodeExecutionToolDescription: string;
29
34
  export declare const CodeExecutionToolName = Constants.EXECUTE_CODE;
30
35
  export declare const CodeExecutionToolDefinition: {
@@ -43,6 +43,15 @@ export declare const ProgrammaticToolCallingDefinition: {
43
43
  readonly required: readonly ["code"];
44
44
  };
45
45
  };
46
+ export type FetchSessionFilesScope = {
47
+ kind: 'skill';
48
+ id: string;
49
+ version: number;
50
+ } | {
51
+ kind: 'agent' | 'user';
52
+ id: string;
53
+ version?: never;
54
+ };
46
55
  /**
47
56
  * Normalizes a tool name to Python identifier format.
48
57
  * Must match the Code API's `normalizePythonFunctionName` exactly:
@@ -76,10 +85,12 @@ export declare function filterToolsByUsage(toolDefs: t.LCTool[], code: string, d
76
85
  * Files are returned as CodeEnvFile references to be included in the request.
77
86
  * @param baseUrl - The base URL for the Code API
78
87
  * @param sessionId - The session ID to fetch files from
88
+ * @param scope - Resource scope used by CodeAPI to authorize the session
79
89
  * @param proxy - Optional HTTP proxy URL
80
90
  * @returns Array of CodeEnvFile references, or empty array if fetch fails
81
91
  */
82
- export declare function fetchSessionFiles(baseUrl: string, sessionId: string, proxy?: string): Promise<t.CodeEnvFile[]>;
92
+ export declare function fetchSessionFiles(baseUrl: string, sessionId: string, proxy?: string, authHeaders?: t.CodeApiAuthHeaders): Promise<t.CodeEnvFile[]>;
93
+ export declare function fetchSessionFiles(baseUrl: string, sessionId: string, scope: FetchSessionFilesScope, proxyOrAuthHeaders?: string | t.CodeApiAuthHeaders, authHeaders?: t.CodeApiAuthHeaders): Promise<t.CodeEnvFile[]>;
83
94
  /**
84
95
  * Makes an HTTP request to the Code API.
85
96
  * @param endpoint - The API endpoint URL
@@ -87,7 +98,7 @@ export declare function fetchSessionFiles(baseUrl: string, sessionId: string, pr
87
98
  * @param proxy - Optional HTTP proxy URL
88
99
  * @returns The parsed API response
89
100
  */
90
- export declare function makeRequest(endpoint: string, body: Record<string, unknown>, proxy?: string): Promise<t.ProgrammaticExecutionResponse>;
101
+ export declare function makeRequest(endpoint: string, body: Record<string, unknown>, proxy?: string, authHeaders?: t.CodeApiAuthHeaders): Promise<t.ProgrammaticExecutionResponse>;
91
102
  /**
92
103
  * Unwraps tool responses that may be formatted as tuples or content blocks.
93
104
  * MCP tools return [content, artifacts], we need to extract the raw data.
@@ -104,7 +115,7 @@ export declare function unwrapToolResponse(result: unknown, isMCPTool: boolean):
104
115
  * @param toolMap - Map of tool names to executable tools
105
116
  * @returns Array of tool results
106
117
  */
107
- export declare function executeTools(toolCalls: t.PTCToolCall[], toolMap: t.ToolMap): Promise<t.PTCToolResult[]>;
118
+ export declare function executeTools(toolCalls: t.PTCToolCall[], toolMap: t.ToolMap, programmaticToolName?: Constants): Promise<t.PTCToolResult[]>;
108
119
  /**
109
120
  * Formats the completed response for the agent.
110
121
  *
@@ -205,7 +205,11 @@ export type CodeExecutionToolParams = undefined | {
205
205
  session_id?: string;
206
206
  user_id?: string;
207
207
  files?: CodeEnvFile[];
208
+ /** Optional host-supplied Code API auth headers. */
209
+ authHeaders?: CodeApiAuthHeaders;
208
210
  };
211
+ export type CodeApiAuthHeaderMap = Record<string, string>;
212
+ export type CodeApiAuthHeaders = CodeApiAuthHeaderMap | (() => CodeApiAuthHeaderMap | Promise<CodeApiAuthHeaderMap>);
209
213
  export type FileRef = {
210
214
  /**
211
215
  * Storage file id (the per-file uuid). See `CodeEnvFile.id` for
@@ -825,6 +829,8 @@ export type ProgrammaticToolCallingParams = {
825
829
  proxy?: string;
826
830
  /** Enable debug logging (or set PTC_DEBUG=true env var) */
827
831
  debug?: boolean;
832
+ /** Optional host-supplied Code API auth headers. */
833
+ authHeaders?: CodeApiAuthHeaders;
828
834
  };
829
835
  /**
830
836
  * Tracks code execution session state for automatic file persistence.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.1.83",
3
+ "version": "3.1.84",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -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', () => {
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
 
@@ -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();
@@ -312,7 +312,8 @@ export function createBashProgrammaticToolCallingTool(
312
312
  timeout,
313
313
  ...(files && files.length > 0 ? { files } : {}),
314
314
  },
315
- proxy
315
+ proxy,
316
+ initParams.authHeaders
316
317
  );
317
318
 
318
319
  // ====================================================================
@@ -339,7 +340,8 @@ export function createBashProgrammaticToolCallingTool(
339
340
 
340
341
  const toolResults = await executeTools(
341
342
  response.tool_calls ?? [],
342
- toolMap
343
+ toolMap,
344
+ Constants.BASH_PROGRAMMATIC_TOOL_CALLING
343
345
  );
344
346
 
345
347
  response = await makeRequest(
@@ -348,7 +350,8 @@ export function createBashProgrammaticToolCallingTool(
348
350
  continuation_token: response.continuation_token,
349
351
  tool_results: toolResults,
350
352
  },
351
- proxy
353
+ proxy,
354
+ initParams.authHeaders
352
355
  );
353
356
  }
354
357
 
@@ -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();