@librechat/agents 3.1.84 → 3.1.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +7 -2
  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/graphs/Graph.cjs +5 -1
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +3 -2
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/main.cjs +2 -0
  10. package/dist/cjs/main.cjs.map +1 -1
  11. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +23 -21
  12. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  13. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +23 -22
  14. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  15. package/dist/cjs/tools/ToolNode.cjs +4 -1
  16. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  17. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +52 -13
  18. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  19. package/dist/cjs/tools/ptcTimeout.cjs +56 -0
  20. package/dist/cjs/tools/ptcTimeout.cjs.map +1 -0
  21. package/dist/esm/agents/AgentContext.mjs +7 -2
  22. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  23. package/dist/esm/common/enum.mjs +1 -0
  24. package/dist/esm/common/enum.mjs.map +1 -1
  25. package/dist/esm/graphs/Graph.mjs +5 -1
  26. package/dist/esm/graphs/Graph.mjs.map +1 -1
  27. package/dist/esm/graphs/MultiAgentGraph.mjs +3 -2
  28. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  29. package/dist/esm/main.mjs +2 -2
  30. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +23 -22
  31. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  32. package/dist/esm/tools/ProgrammaticToolCalling.mjs +23 -23
  33. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  34. package/dist/esm/tools/ToolNode.mjs +4 -1
  35. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  36. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +54 -15
  37. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  38. package/dist/esm/tools/ptcTimeout.mjs +50 -0
  39. package/dist/esm/tools/ptcTimeout.mjs.map +1 -0
  40. package/dist/types/common/enum.d.ts +2 -1
  41. package/dist/types/tools/BashProgrammaticToolCalling.d.ts +4 -36
  42. package/dist/types/tools/ProgrammaticToolCalling.d.ts +4 -36
  43. package/dist/types/tools/ptcTimeout.d.ts +25 -0
  44. package/dist/types/types/tools.d.ts +2 -0
  45. package/package.json +1 -1
  46. package/src/agents/AgentContext.ts +7 -2
  47. package/src/agents/__tests__/AgentContext.test.ts +254 -5
  48. package/src/common/enum.ts +1 -0
  49. package/src/graphs/MultiAgentGraph.ts +3 -2
  50. package/src/graphs/__tests__/composition.smoke.test.ts +84 -2
  51. package/src/tools/BashProgrammaticToolCalling.ts +31 -22
  52. package/src/tools/ProgrammaticToolCalling.ts +31 -23
  53. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +103 -0
  54. package/src/tools/local/LocalProgrammaticToolCalling.ts +94 -13
  55. package/src/tools/ptcTimeout.ts +89 -0
  56. package/src/types/tools.ts +2 -0
@@ -13,6 +13,14 @@ import {
13
13
  makeRequest,
14
14
  } from '../ProgrammaticToolCalling';
15
15
  import { createBashProgrammaticToolCallingTool } from '../BashProgrammaticToolCalling';
16
+ import {
17
+ clampCodeApiRunTimeoutMs,
18
+ createCodeApiRunTimeoutSchema,
19
+ } from '../ptcTimeout';
20
+ import {
21
+ createLocalProgrammaticToolCallingTool,
22
+ createLocalBashProgrammaticToolCallingTool,
23
+ } from '../local/LocalProgrammaticToolCalling';
16
24
 
17
25
  jest.mock('node-fetch', () => ({
18
26
  __esModule: true,
@@ -23,8 +31,33 @@ type FetchMock = jest.MockedFunction<
23
31
  (url: unknown, init?: unknown) => Promise<unknown>
24
32
  >;
25
33
 
34
+ type CodeApiRequestBody = {
35
+ timeout?: number;
36
+ };
37
+
38
+ type TimeoutSchemaForTest = {
39
+ default: number;
40
+ maximum: number;
41
+ description: string;
42
+ };
43
+
44
+ type ToolSchemaForTest = {
45
+ properties: {
46
+ timeout: TimeoutSchemaForTest;
47
+ };
48
+ };
49
+
26
50
  const fetchMock = fetch as unknown as FetchMock;
27
51
 
52
+ function requestBodyAt(callIndex: number): CodeApiRequestBody {
53
+ const init = fetchMock.mock.calls[callIndex]?.[1] as RequestInit;
54
+ return JSON.parse(init.body as string) as CodeApiRequestBody;
55
+ }
56
+
57
+ function timeoutSchemaForTest(toolSchema: unknown): TimeoutSchemaForTest {
58
+ return (toolSchema as ToolSchemaForTest).properties.timeout;
59
+ }
60
+
28
61
  function jsonResponse(body: unknown): unknown {
29
62
  return {
30
63
  ok: true,
@@ -203,6 +236,76 @@ describe('CodeAPI auth header injection', () => {
203
236
  }
204
237
  });
205
238
 
239
+ it('defaults programmatic timeout to the configured CodeAPI run cap', async () => {
240
+ const tool = createProgrammaticToolCallingTool({
241
+ runTimeoutMs: 15000,
242
+ });
243
+
244
+ await tool.invoke(
245
+ { code: 'result = await lookup_user()\nprint(result)' },
246
+ {
247
+ toolCall: {
248
+ name: 'programmatic_code_execution',
249
+ args: {},
250
+ toolMap: toolMap(),
251
+ toolDefs,
252
+ },
253
+ }
254
+ );
255
+
256
+ expect(requestBodyAt(0).timeout).toBe(15000);
257
+ });
258
+
259
+ it('defaults bash programmatic timeout to the configured CodeAPI run cap', async () => {
260
+ const tool = createBashProgrammaticToolCallingTool({
261
+ runTimeoutMs: 15000,
262
+ });
263
+
264
+ await tool.invoke(
265
+ { code: 'lookup_user "{}"' },
266
+ {
267
+ toolCall: {
268
+ name: 'bash_programmatic_code_execution',
269
+ args: {},
270
+ toolMap: toolMap(),
271
+ toolDefs,
272
+ },
273
+ }
274
+ );
275
+
276
+ expect(requestBodyAt(0).timeout).toBe(15000);
277
+ });
278
+
279
+ it('describes the PTC timeout as a single sandbox run cap', () => {
280
+ const schema = createCodeApiRunTimeoutSchema(15000);
281
+
282
+ expect(clampCodeApiRunTimeoutMs(60000, 15000)).toBe(15000);
283
+ expect(schema.default).toBe(15000);
284
+ expect(schema.maximum).toBe(15000);
285
+ expect(schema.description).toContain('one sandbox run');
286
+ expect(schema.description).toContain('not the total multi-round-trip');
287
+ });
288
+
289
+ it('keeps local programmatic timeout schemas aligned with local execution defaults', () => {
290
+ const pythonTimeout = timeoutSchemaForTest(
291
+ createLocalProgrammaticToolCallingTool().schema
292
+ );
293
+ const bashTimeout = timeoutSchemaForTest(
294
+ createLocalBashProgrammaticToolCallingTool().schema
295
+ );
296
+ const configuredTimeout = timeoutSchemaForTest(
297
+ createLocalProgrammaticToolCallingTool({ timeoutMs: 120000 }).schema
298
+ );
299
+
300
+ expect(pythonTimeout.default).toBe(60000);
301
+ expect(pythonTimeout.maximum).toBe(300000);
302
+ expect(pythonTimeout.description).toContain('local execution time');
303
+ expect(bashTimeout.default).toBe(60000);
304
+ expect(bashTimeout.maximum).toBe(300000);
305
+ expect(configuredTimeout.default).toBe(120000);
306
+ expect(configuredTimeout.maximum).toBe(300000);
307
+ });
308
+
206
309
  it('forwards Authorization for bash programmatic requests', async () => {
207
310
  const tool = createBashProgrammaticToolCallingTool({
208
311
  authHeaders: { Authorization: 'Bearer bash-ptc-token' },
@@ -30,19 +30,100 @@ import {
30
30
  import { Constants } from '@/common';
31
31
 
32
32
  const DEFAULT_TIMEOUT = 60000;
33
- const LocalProgrammaticToolCallingSchema = {
34
- ...ProgrammaticToolCallingSchema,
35
- properties: {
36
- ...ProgrammaticToolCallingSchema.properties,
33
+ const LOCAL_MIN_TIMEOUT = 1000;
34
+ const LOCAL_MAX_TIMEOUT = 300000;
35
+
36
+ type LocalTimeoutSchema = {
37
+ type: 'integer';
38
+ minimum: number;
39
+ maximum: number;
40
+ default: number;
41
+ description: string;
42
+ };
43
+
44
+ type LocalProgrammaticToolCallingJsonSchema = {
45
+ type: 'object';
46
+ properties: typeof ProgrammaticToolCallingSchema.properties & {
47
+ timeout: LocalTimeoutSchema;
37
48
  lang: {
38
- type: 'string',
39
- enum: ['py', 'python', 'bash', 'sh'],
40
- default: 'bash',
41
- description:
42
- 'Local engine runtime for orchestration code. Defaults to bash; use py/python for Python orchestration.',
49
+ type: 'string';
50
+ enum: readonly ['py', 'python', 'bash', 'sh'];
51
+ default: 'bash';
52
+ description: string;
53
+ };
54
+ };
55
+ required: readonly ['code'];
56
+ };
57
+
58
+ type LocalBashProgrammaticToolCallingJsonSchema = {
59
+ type: 'object';
60
+ properties: typeof BashProgrammaticToolCallingSchema.properties & {
61
+ timeout: LocalTimeoutSchema;
62
+ };
63
+ required: readonly ['code'];
64
+ };
65
+
66
+ function normalizeLocalTimeout(timeoutMs: number | undefined): number {
67
+ if (timeoutMs == null || !Number.isFinite(timeoutMs)) {
68
+ return DEFAULT_TIMEOUT;
69
+ }
70
+
71
+ return Math.max(LOCAL_MIN_TIMEOUT, Math.floor(timeoutMs));
72
+ }
73
+
74
+ function formatLocalTimeout(timeoutMs: number): string {
75
+ return timeoutMs % 1000 === 0
76
+ ? `${timeoutMs / 1000} seconds`
77
+ : `${timeoutMs} milliseconds`;
78
+ }
79
+
80
+ function createLocalTimeoutSchema(timeoutMs?: number): LocalTimeoutSchema {
81
+ const defaultTimeout = normalizeLocalTimeout(timeoutMs);
82
+ const maxTimeout = Math.max(LOCAL_MAX_TIMEOUT, defaultTimeout);
83
+ const formattedDefault = formatLocalTimeout(defaultTimeout);
84
+ const formattedMax = formatLocalTimeout(maxTimeout);
85
+
86
+ return {
87
+ type: 'integer',
88
+ minimum: LOCAL_MIN_TIMEOUT,
89
+ maximum: maxTimeout,
90
+ default: defaultTimeout,
91
+ description:
92
+ 'Maximum local execution time in milliseconds. ' +
93
+ `Default: ${formattedDefault}. Max: ${formattedMax}.`,
94
+ };
95
+ }
96
+
97
+ function createLocalProgrammaticToolCallingSchema(
98
+ localConfig: t.LocalExecutionConfig = {}
99
+ ): LocalProgrammaticToolCallingJsonSchema {
100
+ return {
101
+ ...ProgrammaticToolCallingSchema,
102
+ properties: {
103
+ ...ProgrammaticToolCallingSchema.properties,
104
+ timeout: createLocalTimeoutSchema(localConfig.timeoutMs),
105
+ lang: {
106
+ type: 'string',
107
+ enum: ['py', 'python', 'bash', 'sh'],
108
+ default: 'bash',
109
+ description:
110
+ 'Local engine runtime for orchestration code. Defaults to bash; use py/python for Python orchestration.',
111
+ },
43
112
  },
44
- },
45
- } as const;
113
+ } as const;
114
+ }
115
+
116
+ function createLocalBashProgrammaticToolCallingSchema(
117
+ localConfig: t.LocalExecutionConfig = {}
118
+ ): LocalBashProgrammaticToolCallingJsonSchema {
119
+ return {
120
+ ...BashProgrammaticToolCallingSchema,
121
+ properties: {
122
+ ...BashProgrammaticToolCallingSchema.properties,
123
+ timeout: createLocalTimeoutSchema(localConfig.timeoutMs),
124
+ },
125
+ } as const;
126
+ }
46
127
 
47
128
  type ToolBridge = {
48
129
  url: string;
@@ -582,7 +663,7 @@ export function createLocalProgrammaticToolCallingTool(
582
663
  {
583
664
  name: ProgrammaticToolCallingName,
584
665
  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.`,
585
- schema: LocalProgrammaticToolCallingSchema,
666
+ schema: createLocalProgrammaticToolCallingSchema(localConfig),
586
667
  responseFormat: Constants.CONTENT_AND_ARTIFACT,
587
668
  }
588
669
  );
@@ -604,7 +685,7 @@ export function createLocalBashProgrammaticToolCallingTool(
604
685
  {
605
686
  name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,
606
687
  description: `${BashProgrammaticToolCallingDescription}\n\nLocal engine: runs this bash orchestration code on the host machine and calls tools through an in-process localhost bridge.`,
607
- schema: BashProgrammaticToolCallingSchema,
688
+ schema: createLocalBashProgrammaticToolCallingSchema(localConfig),
608
689
  responseFormat: Constants.CONTENT_AND_ARTIFACT,
609
690
  }
610
691
  );
@@ -0,0 +1,89 @@
1
+ import { EnvVar } from '@/common';
2
+
3
+ export const DEFAULT_CODE_API_RUN_TIMEOUT_MS = 15_000;
4
+ export const MIN_CODE_API_RUN_TIMEOUT_MS = 1_000;
5
+
6
+ type TimeoutSchema = {
7
+ type: 'integer';
8
+ minimum: number;
9
+ maximum: number;
10
+ default: number;
11
+ description: string;
12
+ };
13
+
14
+ export type ProgrammaticToolCallingJsonSchema = {
15
+ type: 'object';
16
+ properties: {
17
+ code: {
18
+ type: 'string';
19
+ minLength: number;
20
+ description: string;
21
+ };
22
+ timeout: TimeoutSchema;
23
+ };
24
+ required: readonly ['code'];
25
+ };
26
+
27
+ function normalizeTimeoutMs(value: number | undefined): number | undefined {
28
+ if (value == null || !Number.isFinite(value)) {
29
+ return undefined;
30
+ }
31
+
32
+ return Math.max(MIN_CODE_API_RUN_TIMEOUT_MS, Math.floor(value));
33
+ }
34
+
35
+ function parseTimeoutMs(value: string | undefined): number | undefined {
36
+ if (value == null || value.trim() === '') {
37
+ return undefined;
38
+ }
39
+
40
+ return normalizeTimeoutMs(Number(value));
41
+ }
42
+
43
+ function formatTimeout(timeoutMs: number): string {
44
+ return timeoutMs % 1000 === 0
45
+ ? `${timeoutMs / 1000} seconds`
46
+ : `${timeoutMs} milliseconds`;
47
+ }
48
+
49
+ export function resolveCodeApiRunTimeoutMs(override?: number): number {
50
+ return (
51
+ normalizeTimeoutMs(override) ??
52
+ parseTimeoutMs(process.env[EnvVar.CODE_API_RUN_TIMEOUT_MS]) ??
53
+ DEFAULT_CODE_API_RUN_TIMEOUT_MS
54
+ );
55
+ }
56
+
57
+ export function clampCodeApiRunTimeoutMs(
58
+ timeoutMs: number | undefined,
59
+ maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()
60
+ ): number {
61
+ const normalizedMaxRunTimeoutMs =
62
+ normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;
63
+ const normalizedTimeoutMs = normalizeTimeoutMs(timeoutMs);
64
+
65
+ if (normalizedTimeoutMs == null) {
66
+ return normalizedMaxRunTimeoutMs;
67
+ }
68
+
69
+ return Math.min(normalizedTimeoutMs, normalizedMaxRunTimeoutMs);
70
+ }
71
+
72
+ export function createCodeApiRunTimeoutSchema(
73
+ maxRunTimeoutMs = resolveCodeApiRunTimeoutMs()
74
+ ): TimeoutSchema {
75
+ const normalizedMaxRunTimeoutMs =
76
+ normalizeTimeoutMs(maxRunTimeoutMs) ?? DEFAULT_CODE_API_RUN_TIMEOUT_MS;
77
+ const formattedTimeout = formatTimeout(normalizedMaxRunTimeoutMs);
78
+
79
+ return {
80
+ type: 'integer',
81
+ minimum: MIN_CODE_API_RUN_TIMEOUT_MS,
82
+ maximum: normalizedMaxRunTimeoutMs,
83
+ default: normalizedMaxRunTimeoutMs,
84
+ description:
85
+ 'Maximum wall-clock time in milliseconds for one sandbox run or replay iteration. ' +
86
+ 'This is not the total multi-round-trip task budget. ' +
87
+ `Default: ${formattedTimeout}. Max: ${formattedTimeout}.`,
88
+ };
89
+ }
@@ -900,6 +900,8 @@ export type ProgrammaticToolCallingParams = {
900
900
  baseUrl?: string;
901
901
  /** Safety limit for round-trips (default: 20) */
902
902
  maxRoundTrips?: number;
903
+ /** Maximum per-sandbox-run timeout for PTC's legacy `timeout` field. */
904
+ runTimeoutMs?: number;
903
905
  /** HTTP proxy URL */
904
906
  proxy?: string;
905
907
  /** Enable debug logging (or set PTC_DEBUG=true env var) */