@librechat/agents 3.6.9 → 3.6.11

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 (146) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +99 -39
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/agents/projection.cjs +2 -2
  4. package/dist/cjs/agents/projection.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +32 -21
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +87 -16
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/llm/openai/index.cjs +10 -0
  10. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  11. package/dist/cjs/main.cjs +16 -0
  12. package/dist/cjs/messages/alternation.cjs +125 -20
  13. package/dist/cjs/messages/alternation.cjs.map +1 -1
  14. package/dist/cjs/messages/core.cjs +195 -18
  15. package/dist/cjs/messages/core.cjs.map +1 -1
  16. package/dist/cjs/messages/format.cjs +488 -118
  17. package/dist/cjs/messages/format.cjs.map +1 -1
  18. package/dist/cjs/messages/handoffCue.cjs +3 -2
  19. package/dist/cjs/messages/handoffCue.cjs.map +1 -1
  20. package/dist/cjs/messages/index.cjs +1 -0
  21. package/dist/cjs/messages/injected.cjs +5 -2
  22. package/dist/cjs/messages/injected.cjs.map +1 -1
  23. package/dist/cjs/messages/provenance.cjs +411 -0
  24. package/dist/cjs/messages/provenance.cjs.map +1 -0
  25. package/dist/cjs/messages/prune.cjs +18 -13
  26. package/dist/cjs/messages/prune.cjs.map +1 -1
  27. package/dist/cjs/messages/toolResultTypes.cjs +663 -0
  28. package/dist/cjs/messages/toolResultTypes.cjs.map +1 -0
  29. package/dist/cjs/run.cjs +7 -4
  30. package/dist/cjs/run.cjs.map +1 -1
  31. package/dist/cjs/stream.cjs +1 -0
  32. package/dist/cjs/stream.cjs.map +1 -1
  33. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +21 -4
  34. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  35. package/dist/cjs/tools/CallerCapabilities.cjs +79 -0
  36. package/dist/cjs/tools/CallerCapabilities.cjs.map +1 -0
  37. package/dist/cjs/tools/ProgrammaticCallerPolicy.cjs +34 -0
  38. package/dist/cjs/tools/ProgrammaticCallerPolicy.cjs.map +1 -0
  39. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +21 -4
  40. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  41. package/dist/cjs/tools/ToolNode.cjs +39 -21
  42. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  43. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +12 -4
  44. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
  45. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +13 -4
  46. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  47. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +73 -13
  48. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
  49. package/dist/cjs/tools/ptcTimeout.cjs.map +1 -1
  50. package/dist/cjs/utils/toolContent.cjs +2 -2
  51. package/dist/cjs/utils/toolContent.cjs.map +1 -1
  52. package/dist/esm/agents/AgentContext.mjs +99 -39
  53. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  54. package/dist/esm/agents/projection.mjs +2 -2
  55. package/dist/esm/agents/projection.mjs.map +1 -1
  56. package/dist/esm/graphs/Graph.mjs +34 -23
  57. package/dist/esm/graphs/Graph.mjs.map +1 -1
  58. package/dist/esm/graphs/MultiAgentGraph.mjs +87 -16
  59. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  60. package/dist/esm/llm/openai/index.mjs +10 -0
  61. package/dist/esm/llm/openai/index.mjs.map +1 -1
  62. package/dist/esm/main.mjs +4 -3
  63. package/dist/esm/messages/alternation.mjs +125 -20
  64. package/dist/esm/messages/alternation.mjs.map +1 -1
  65. package/dist/esm/messages/core.mjs +196 -19
  66. package/dist/esm/messages/core.mjs.map +1 -1
  67. package/dist/esm/messages/format.mjs +488 -119
  68. package/dist/esm/messages/format.mjs.map +1 -1
  69. package/dist/esm/messages/handoffCue.mjs +3 -2
  70. package/dist/esm/messages/handoffCue.mjs.map +1 -1
  71. package/dist/esm/messages/index.mjs +1 -0
  72. package/dist/esm/messages/injected.mjs +5 -2
  73. package/dist/esm/messages/injected.mjs.map +1 -1
  74. package/dist/esm/messages/provenance.mjs +401 -0
  75. package/dist/esm/messages/provenance.mjs.map +1 -0
  76. package/dist/esm/messages/prune.mjs +18 -13
  77. package/dist/esm/messages/prune.mjs.map +1 -1
  78. package/dist/esm/messages/toolResultTypes.mjs +657 -0
  79. package/dist/esm/messages/toolResultTypes.mjs.map +1 -0
  80. package/dist/esm/run.mjs +7 -4
  81. package/dist/esm/run.mjs.map +1 -1
  82. package/dist/esm/stream.mjs +1 -0
  83. package/dist/esm/stream.mjs.map +1 -1
  84. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +21 -4
  85. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  86. package/dist/esm/tools/CallerCapabilities.mjs +73 -0
  87. package/dist/esm/tools/CallerCapabilities.mjs.map +1 -0
  88. package/dist/esm/tools/ProgrammaticCallerPolicy.mjs +32 -0
  89. package/dist/esm/tools/ProgrammaticCallerPolicy.mjs.map +1 -0
  90. package/dist/esm/tools/ProgrammaticToolCalling.mjs +21 -4
  91. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  92. package/dist/esm/tools/ToolNode.mjs +39 -21
  93. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  94. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +14 -6
  95. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
  96. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +15 -6
  97. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  98. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +72 -15
  99. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
  100. package/dist/esm/tools/ptcTimeout.mjs.map +1 -1
  101. package/dist/esm/utils/toolContent.mjs +2 -2
  102. package/dist/esm/utils/toolContent.mjs.map +1 -1
  103. package/dist/types/agents/AgentContext.d.ts +19 -13
  104. package/dist/types/agents/projection.d.ts +3 -1
  105. package/dist/types/graphs/Graph.d.ts +1 -1
  106. package/dist/types/llm/openai/index.d.ts +20 -0
  107. package/dist/types/messages/format.d.ts +5 -0
  108. package/dist/types/messages/index.d.ts +1 -0
  109. package/dist/types/messages/provenance.d.ts +85 -0
  110. package/dist/types/messages/toolResultTypes.d.ts +33 -0
  111. package/dist/types/tools/CallerCapabilities.d.ts +25 -0
  112. package/dist/types/tools/ProgrammaticCallerPolicy.d.ts +20 -0
  113. package/dist/types/tools/ToolNode.d.ts +12 -7
  114. package/dist/types/tools/local/resolveLocalExecutionTools.d.ts +10 -0
  115. package/dist/types/tools/ptcTimeout.d.ts +8 -0
  116. package/dist/types/types/graph.d.ts +3 -1
  117. package/dist/types/types/tools.d.ts +30 -2
  118. package/package.json +1 -1
  119. package/src/agents/AgentContext.ts +253 -69
  120. package/src/agents/projection.ts +9 -1
  121. package/src/graphs/Graph.ts +62 -26
  122. package/src/graphs/MultiAgentGraph.ts +175 -33
  123. package/src/llm/openai/index.ts +43 -0
  124. package/src/messages/alternation.ts +217 -34
  125. package/src/messages/core.ts +358 -38
  126. package/src/messages/format.ts +958 -137
  127. package/src/messages/handoffCue.ts +7 -4
  128. package/src/messages/index.ts +1 -0
  129. package/src/messages/injected.ts +9 -6
  130. package/src/messages/provenance.ts +897 -0
  131. package/src/messages/prune.ts +28 -10
  132. package/src/messages/toolResultTypes.ts +1174 -0
  133. package/src/run.ts +9 -4
  134. package/src/stream.ts +6 -0
  135. package/src/tools/BashProgrammaticToolCalling.ts +46 -12
  136. package/src/tools/CallerCapabilities.ts +146 -0
  137. package/src/tools/ProgrammaticCallerPolicy.ts +97 -0
  138. package/src/tools/ProgrammaticToolCalling.ts +41 -7
  139. package/src/tools/ToolNode.ts +111 -50
  140. package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +24 -26
  141. package/src/tools/local/LocalProgrammaticToolCalling.ts +28 -23
  142. package/src/tools/local/resolveLocalExecutionTools.ts +209 -16
  143. package/src/tools/ptcTimeout.ts +6 -0
  144. package/src/types/graph.ts +3 -0
  145. package/src/types/tools.ts +32 -3
  146. package/src/utils/toolContent.ts +2 -2
package/src/run.ts CHANGED
@@ -93,6 +93,7 @@ import {
93
93
  import { applyGraphRuntimeConfig } from '@/graphs/applyGraphRuntimeConfig';
94
94
  import { LANGFUSE_OPERATION_METADATA_KEY } from '@/langfuseOperation';
95
95
  import { createTokenCounter, encodingForModel } from '@/utils/tokens';
96
+ import { stampSyntheticProviderMessage } from '@/messages/provenance';
96
97
  import { initializeLangfuseTracing } from './instrumentation';
97
98
  import { seedRunInitialSessions } from '@/utils/toolSessions';
98
99
  import { getTraceIdSeed } from '@/langfuseRuntimeContext';
@@ -469,6 +470,7 @@ export class Run<_T extends t.BaseGraphState> {
469
470
  subagentTasks: this.subagentTasks,
470
471
  preemption: this.preemption,
471
472
  streamLimits: this.streamLimits,
473
+ toolExecution: this.toolExecution,
472
474
  },
473
475
  });
474
476
  /** Propagate compile options from graph config */
@@ -508,6 +510,7 @@ export class Run<_T extends t.BaseGraphState> {
508
510
  subagentTasks: this.subagentTasks,
509
511
  preemption: this.preemption,
510
512
  streamLimits: this.streamLimits,
513
+ toolExecution: this.toolExecution,
511
514
  },
512
515
  });
513
516
 
@@ -699,10 +702,12 @@ export class Run<_T extends t.BaseGraphState> {
699
702
  * it into the agent's `instructions` config instead.
700
703
  */
701
704
  stateInputs.messages.push(
702
- new HumanMessage({
703
- content: preStreamContexts.join('\n\n'),
704
- additional_kwargs: { role: 'system', source: 'hook' },
705
- })
705
+ stampSyntheticProviderMessage(
706
+ new HumanMessage({
707
+ content: preStreamContexts.join('\n\n'),
708
+ additional_kwargs: { role: 'system', source: 'hook' },
709
+ })
710
+ )
706
711
  );
707
712
  }
708
713
 
package/src/stream.ts CHANGED
@@ -820,6 +820,12 @@ function startEagerToolExecutions(args: {
820
820
  toolCalls: entries.map((entry) => entry.request),
821
821
  userId: graph.config?.configurable?.user_id as string | undefined,
822
822
  agentId: agentContext?.agentId,
823
+ callerCapabilityProjection:
824
+ (
825
+ agentContext as
826
+ | Partial<Pick<AgentContext, 'getCallerCapabilityProjectionSnapshot'>>
827
+ | undefined
828
+ )?.getCallerCapabilityProjectionSnapshot?.(),
823
829
  configurable: graph.config?.configurable as
824
830
  | Record<string, unknown>
825
831
  | undefined,
@@ -13,16 +13,22 @@ import {
13
13
  getCodeBaseURL,
14
14
  selectRuntimeSessionHint,
15
15
  } from './CodeExecutor';
16
- import {
17
- clampCodeApiRunTimeoutMs,
18
- createCodeApiRunTimeoutSchema,
19
- resolveCodeApiRunTimeoutMs,
20
- } from './ptcTimeout';
21
16
  import {
22
17
  makeRequest,
23
18
  executeTools,
24
19
  formatCompletedResponse,
25
20
  } from './ProgrammaticToolCalling';
21
+ import {
22
+ projectProgrammaticToolMap,
23
+ resolveProgrammaticToolDefinitions,
24
+ selectProgrammaticTools,
25
+ type ProgrammaticInvocationParams,
26
+ } from './ProgrammaticCallerPolicy';
27
+ import {
28
+ clampCodeApiRunTimeoutMs,
29
+ createCodeApiRunTimeoutSchema,
30
+ resolveCodeApiRunTimeoutMs,
31
+ } from './ptcTimeout';
26
32
  import { INTENT_PROPERTY } from '@/tools/intentArg';
27
33
  import { Constants } from '@/common';
28
34
 
@@ -108,6 +114,10 @@ ${EXAMPLES}
108
114
 
109
115
  ${CORE_RULES}`;
110
116
 
117
+ const TOOL_MANIFEST_DESCRIPTION =
118
+ 'Exact registered tool names used by the code. Required when direct-only tools are configured; ' +
119
+ 'validated before execution starts.';
120
+
111
121
  // ============================================================================
112
122
  // Schema
113
123
  // ============================================================================
@@ -124,6 +134,12 @@ export function createBashProgrammaticToolCallingSchema(
124
134
  minLength: 1,
125
135
  description: CODE_PARAM_DESCRIPTION,
126
136
  },
137
+ tool_manifest: {
138
+ type: 'array',
139
+ items: { type: 'string' },
140
+ uniqueItems: true,
141
+ description: TOOL_MANIFEST_DESCRIPTION,
142
+ },
127
143
  timeout: createCodeApiRunTimeoutSchema(maxRunTimeoutMs),
128
144
  },
129
145
  required: ['code'],
@@ -302,7 +318,7 @@ export function createBashProgrammaticToolCallingTool(
302
318
 
303
319
  return tool(
304
320
  async (rawParams, config) => {
305
- const params = rawParams as { code: string; timeout?: number };
321
+ const params = rawParams as ProgrammaticInvocationParams;
306
322
  const { code } = params;
307
323
  const timeout = clampCodeApiRunTimeoutMs(params.timeout, maxRunTimeoutMs);
308
324
 
@@ -314,11 +330,26 @@ export function createBashProgrammaticToolCallingTool(
314
330
  };
315
331
  const {
316
332
  toolMap,
317
- toolDefs,
333
+ disallowedToolDefs,
318
334
  session_id,
319
335
  _injected_files,
320
336
  _runtime_session_hint,
321
337
  } = toolCall;
338
+ const toolDefs = resolveProgrammaticToolDefinitions(
339
+ toolCall as typeof toolCall & { tools?: t.LCTool[] }
340
+ );
341
+
342
+ const programmaticToolName =
343
+ toolCall.programmaticToolName ??
344
+ (typeof toolCall.name === 'string' && toolCall.name !== ''
345
+ ? toolCall.name
346
+ : Constants.BASH_PROGRAMMATIC_TOOL_CALLING);
347
+ const effectiveTools = selectProgrammaticTools({
348
+ requestedToolNames: params.tool_manifest,
349
+ allowedToolDefs: toolDefs,
350
+ disallowedToolDefs,
351
+ programmaticToolName,
352
+ });
322
353
 
323
354
  if (toolMap == null || toolMap.size === 0) {
324
355
  throw new Error(
@@ -334,20 +365,23 @@ export function createBashProgrammaticToolCallingTool(
334
365
  );
335
366
  }
336
367
 
368
+ const effectiveToolMap = projectProgrammaticToolMap(
369
+ toolMap,
370
+ effectiveTools
371
+ );
372
+
337
373
  let roundTrip = 0;
338
374
 
339
375
  try {
340
376
  // ====================================================================
341
- // Phase 1: Filter tools and make initial request
377
+ // Phase 1: Send the validated tool manifest with the initial request
342
378
  // ====================================================================
343
379
 
344
- const effectiveTools = filterBashToolsByUsage(toolDefs, code, debug);
345
-
346
380
  if (debug) {
347
381
  // eslint-disable-next-line no-console
348
382
  console.log(
349
383
  `[BashPTC Debug] Sending ${effectiveTools.length} tools to API ` +
350
- `(filtered from ${toolDefs.length})`
384
+ `(selected from ${toolDefs.length})`
351
385
  );
352
386
  }
353
387
 
@@ -422,7 +456,7 @@ export function createBashProgrammaticToolCallingTool(
422
456
  const toolResults = normalizeBashToolResultsForReplay(
423
457
  await executeTools(
424
458
  response.tool_calls ?? [],
425
- toolMap,
459
+ effectiveToolMap,
426
460
  Constants.BASH_PROGRAMMATIC_TOOL_CALLING
427
461
  )
428
462
  );
@@ -0,0 +1,146 @@
1
+ import type * as t from '@/types';
2
+ import { Constants } from '@/common';
3
+
4
+ const PROGRAMMATIC_CONTROL_TOOL_NAMES = new Set<string>([
5
+ Constants.PROGRAMMATIC_TOOL_CALLING,
6
+ Constants.BASH_PROGRAMMATIC_TOOL_CALLING,
7
+ Constants.TOOL_SEARCH,
8
+ ]);
9
+
10
+ export type CallerCapabilityProjection = {
11
+ directTools: t.LCTool[];
12
+ codeExecutionTools: t.LCTool[];
13
+ directOnlyTools: t.LCTool[];
14
+ codeExecutionOnlyTools: t.LCTool[];
15
+ };
16
+
17
+ /**
18
+ * Combines event schemas with runtime capability metadata. Matching runtime
19
+ * entries override caller/defer policy without replacing the event schema;
20
+ * runtime-only definitions are appended intact.
21
+ */
22
+ export function mergeCallerCapabilityDefinitions(
23
+ toolDefs: Iterable<t.LCTool> | null | undefined,
24
+ overrides: Iterable<t.LCTool> | null | undefined
25
+ ): t.LCTool[] {
26
+ const runtimeDefinitions = Array.from(overrides ?? []);
27
+ const merged = new Map(
28
+ applyCallerCapabilityDefinitionOverrides(
29
+ toolDefs ?? [],
30
+ runtimeDefinitions
31
+ ).map((toolDef) => [toolDef.name, toolDef])
32
+ );
33
+ for (const override of runtimeDefinitions) {
34
+ if (!merged.has(override.name)) {
35
+ merged.set(override.name, override);
36
+ }
37
+ }
38
+ return Array.from(merged.values());
39
+ }
40
+
41
+ /**
42
+ * Applies runtime caller metadata to schema-only event definitions without
43
+ * adding registry-only tools or replacing their model-facing schemas.
44
+ */
45
+ export function applyCallerCapabilityDefinitionOverrides(
46
+ toolDefs: Iterable<t.LCTool>,
47
+ overrides: Iterable<t.LCTool> | null | undefined
48
+ ): t.LCTool[] {
49
+ const overridesByName = new Map<string, t.LCTool>();
50
+ for (const override of overrides ?? []) {
51
+ overridesByName.set(override.name, override);
52
+ }
53
+ return Array.from(toolDefs, (toolDef) => {
54
+ const override = overridesByName.get(toolDef.name);
55
+ if (override == null) {
56
+ return toolDef;
57
+ }
58
+ return {
59
+ ...toolDef,
60
+ allowed_callers: override.allowed_callers,
61
+ defer_loading: override.defer_loading,
62
+ };
63
+ });
64
+ }
65
+
66
+ /** Converts the live projection into the versioned event transport shape. */
67
+ export function createCallerCapabilityProjectionSnapshot(
68
+ projection: CallerCapabilityProjection
69
+ ): t.CallerCapabilityProjectionSnapshot {
70
+ return {
71
+ version: 1,
72
+ directToolNames: projection.directTools.map((toolDef) => toolDef.name),
73
+ codeExecutionToolNames: projection.codeExecutionTools.map(
74
+ (toolDef) => toolDef.name
75
+ ),
76
+ directOnlyToolNames: projection.directOnlyTools.map(
77
+ (toolDef) => toolDef.name
78
+ ),
79
+ codeExecutionOnlyToolNames: projection.codeExecutionOnlyTools.map(
80
+ (toolDef) => toolDef.name
81
+ ),
82
+ };
83
+ }
84
+
85
+ export function getAllowedCallers(
86
+ toolDef: t.LCTool
87
+ ): readonly t.AllowedCaller[] {
88
+ return toolDef.allowed_callers ?? ['direct'];
89
+ }
90
+
91
+ export function allowsToolCaller(
92
+ toolDef: t.LCTool,
93
+ caller: t.AllowedCaller
94
+ ): boolean {
95
+ return getAllowedCallers(toolDef).includes(caller);
96
+ }
97
+
98
+ export function isProgrammaticControlTool(name: string): boolean {
99
+ return PROGRAMMATIC_CONTROL_TOOL_NAMES.has(name);
100
+ }
101
+
102
+ export function isToolDefinitionActive(
103
+ toolDef: t.LCTool,
104
+ discoveredToolNames: ReadonlySet<string>
105
+ ): boolean {
106
+ return (
107
+ toolDef.defer_loading !== true || discoveredToolNames.has(toolDef.name)
108
+ );
109
+ }
110
+
111
+ export function resolveCallerCapabilityProjection(
112
+ toolDefs: Iterable<t.LCTool>,
113
+ isActive: (toolDef: t.LCTool) => boolean = () => true
114
+ ): CallerCapabilityProjection {
115
+ const directTools: t.LCTool[] = [];
116
+ const codeExecutionTools: t.LCTool[] = [];
117
+ const directOnlyTools: t.LCTool[] = [];
118
+ const codeExecutionOnlyTools: t.LCTool[] = [];
119
+
120
+ for (const toolDef of toolDefs) {
121
+ if (!isActive(toolDef)) {
122
+ continue;
123
+ }
124
+ const allowsDirect = allowsToolCaller(toolDef, 'direct');
125
+ const allowsCodeExecution = allowsToolCaller(toolDef, 'code_execution');
126
+ if (allowsDirect) {
127
+ directTools.push(toolDef);
128
+ }
129
+ if (allowsCodeExecution) {
130
+ codeExecutionTools.push(toolDef);
131
+ }
132
+ if (allowsDirect && !allowsCodeExecution) {
133
+ directOnlyTools.push(toolDef);
134
+ }
135
+ if (allowsCodeExecution && !allowsDirect) {
136
+ codeExecutionOnlyTools.push(toolDef);
137
+ }
138
+ }
139
+
140
+ return {
141
+ directTools,
142
+ codeExecutionTools,
143
+ directOnlyTools,
144
+ codeExecutionOnlyTools,
145
+ };
146
+ }
@@ -0,0 +1,97 @@
1
+ import type * as t from '@/types';
2
+
3
+ export type ProgrammaticInvocationParams = {
4
+ code: string;
5
+ tool_manifest?: string[];
6
+ timeout?: number;
7
+ lang?: string;
8
+ runtime?: string;
9
+ language?: string;
10
+ };
11
+
12
+ export function resolveProgrammaticToolDefinitions(
13
+ context: Partial<t.ProgrammaticCache> & { tools?: t.LCTool[] }
14
+ ): t.LCTool[] | undefined {
15
+ return context.toolDefs ?? context.tools;
16
+ }
17
+
18
+ export function projectProgrammaticToolMap(
19
+ toolMap: t.ToolMap,
20
+ selectedToolDefs: readonly t.LCTool[]
21
+ ): t.ToolMap {
22
+ const selectedNames = new Set(selectedToolDefs.map((toolDef) => toolDef.name));
23
+ return new Map(
24
+ [...toolMap].filter(([name]) => selectedNames.has(name))
25
+ );
26
+ }
27
+
28
+ export function assertDisallowedToolUsage(
29
+ disallowedNames: ReadonlySet<string>,
30
+ programmaticToolName: string
31
+ ): void {
32
+ if (disallowedNames.size === 0) {
33
+ return;
34
+ }
35
+
36
+ const names = [...disallowedNames];
37
+ throw new Error(
38
+ `Tool${names.length === 1 ? '' : 's'} ${names
39
+ .map((name) => `"${name}"`)
40
+ .join(', ')} cannot be called from "${programmaticToolName}" because ` +
41
+ `the${names.length === 1 ? ' tool is' : 'se tools are'} not marked for code_execution. ` +
42
+ `Call ${names.length === 1 ? 'it' : 'them'} directly instead.`
43
+ );
44
+ }
45
+
46
+ export function selectProgrammaticTools(args: {
47
+ requestedToolNames?: string[];
48
+ allowedToolDefs?: t.LCTool[];
49
+ disallowedToolDefs?: t.LCTool[];
50
+ programmaticToolName: string;
51
+ }): t.LCTool[] {
52
+ const allowedToolDefs = args.allowedToolDefs ?? [];
53
+ const disallowedToolDefs = args.disallowedToolDefs ?? [];
54
+ const requestedToolNames = args.requestedToolNames;
55
+
56
+ if (requestedToolNames == null) {
57
+ if (disallowedToolDefs.length > 0) {
58
+ throw new Error(
59
+ `"${args.programmaticToolName}" requires a tool_manifest when direct-only tools are configured. ` +
60
+ 'List every registered tool name used by the submitted code in the tool_manifest field.'
61
+ );
62
+ }
63
+ return allowedToolDefs;
64
+ }
65
+
66
+ const disallowedNames = new Set(
67
+ disallowedToolDefs.map((toolDef) => toolDef.name)
68
+ );
69
+ const requestedDisallowedNames = new Set(
70
+ requestedToolNames.filter((name) => disallowedNames.has(name))
71
+ );
72
+ assertDisallowedToolUsage(
73
+ requestedDisallowedNames,
74
+ args.programmaticToolName
75
+ );
76
+
77
+ const allowedByName = new Map(
78
+ allowedToolDefs.map((toolDef) => [toolDef.name, toolDef])
79
+ );
80
+ const unknownNames = new Set(
81
+ requestedToolNames.filter((name) => !allowedByName.has(name))
82
+ );
83
+ if (unknownNames.size > 0) {
84
+ throw new Error(
85
+ `Tool${unknownNames.size === 1 ? '' : 's'} ${[...unknownNames]
86
+ .map((name) => `"${name}"`)
87
+ .join(
88
+ ', '
89
+ )} cannot be used by "${args.programmaticToolName}" because ` +
90
+ `${unknownNames.size === 1 ? 'it is' : 'they are'} not available for code_execution.`
91
+ );
92
+ }
93
+
94
+ return [...new Set(requestedToolNames)].map(
95
+ (name) => allowedByName.get(name) as t.LCTool
96
+ );
97
+ }
@@ -26,6 +26,12 @@ import {
26
26
  createCodeApiRunTimeoutSchema,
27
27
  resolveCodeApiRunTimeoutMs,
28
28
  } from './ptcTimeout';
29
+ import {
30
+ projectProgrammaticToolMap,
31
+ resolveProgrammaticToolDefinitions,
32
+ selectProgrammaticTools,
33
+ type ProgrammaticInvocationParams,
34
+ } from './ProgrammaticCallerPolicy';
29
35
  import { resolveFetchProxyAgent } from '@/utils/proxy';
30
36
  import { INTENT_PROPERTY } from '@/tools/intentArg';
31
37
  import { Constants } from '@/common';
@@ -87,6 +93,10 @@ ${EXAMPLES}
87
93
 
88
94
  ${CORE_RULES}`;
89
95
 
96
+ const TOOL_MANIFEST_DESCRIPTION =
97
+ 'Exact registered tool names used by the code. Required when direct-only tools are configured; ' +
98
+ 'validated before execution starts.';
99
+
90
100
  export function createProgrammaticToolCallingSchema(
91
101
  maxRunTimeoutMs = DEFAULT_RUN_TIMEOUT_MS
92
102
  ): ProgrammaticToolCallingJsonSchema {
@@ -99,6 +109,12 @@ export function createProgrammaticToolCallingSchema(
99
109
  minLength: 1,
100
110
  description: CODE_PARAM_DESCRIPTION,
101
111
  },
112
+ tool_manifest: {
113
+ type: 'array',
114
+ items: { type: 'string' },
115
+ uniqueItems: true,
116
+ description: TOOL_MANIFEST_DESCRIPTION,
117
+ },
102
118
  timeout: createCodeApiRunTimeoutSchema(maxRunTimeoutMs),
103
119
  },
104
120
  required: ['code'],
@@ -893,7 +909,7 @@ export function createProgrammaticToolCallingTool(
893
909
 
894
910
  return tool(
895
911
  async (rawParams, config) => {
896
- const params = rawParams as { code: string; timeout?: number };
912
+ const params = rawParams as ProgrammaticInvocationParams;
897
913
  const { code } = params;
898
914
  const timeout = clampCodeApiRunTimeoutMs(params.timeout, maxRunTimeoutMs);
899
915
 
@@ -906,11 +922,26 @@ export function createProgrammaticToolCallingTool(
906
922
  };
907
923
  const {
908
924
  toolMap,
909
- toolDefs,
925
+ disallowedToolDefs,
910
926
  session_id,
911
927
  _injected_files,
912
928
  _runtime_session_hint,
913
929
  } = toolCall;
930
+ const toolDefs = resolveProgrammaticToolDefinitions(
931
+ toolCall as typeof toolCall & { tools?: t.LCTool[] }
932
+ );
933
+
934
+ const programmaticToolName =
935
+ toolCall.programmaticToolName ??
936
+ (typeof toolCall.name === 'string' && toolCall.name !== ''
937
+ ? toolCall.name
938
+ : Constants.PROGRAMMATIC_TOOL_CALLING);
939
+ const effectiveTools = selectProgrammaticTools({
940
+ requestedToolNames: params.tool_manifest,
941
+ allowedToolDefs: toolDefs,
942
+ disallowedToolDefs,
943
+ programmaticToolName,
944
+ });
914
945
 
915
946
  if (toolMap == null || toolMap.size === 0) {
916
947
  throw new Error(
@@ -926,20 +957,23 @@ export function createProgrammaticToolCallingTool(
926
957
  );
927
958
  }
928
959
 
960
+ const effectiveToolMap = projectProgrammaticToolMap(
961
+ toolMap,
962
+ effectiveTools
963
+ );
964
+
929
965
  let roundTrip = 0;
930
966
 
931
967
  try {
932
968
  // ====================================================================
933
- // Phase 1: Filter tools and make initial request
969
+ // Phase 1: Send the validated tool manifest with the initial request
934
970
  // ====================================================================
935
971
 
936
- const effectiveTools = filterToolsByUsage(toolDefs, code, debug);
937
-
938
972
  if (debug) {
939
973
  // eslint-disable-next-line no-console
940
974
  console.log(
941
975
  `[PTC Debug] Sending ${effectiveTools.length} tools to API ` +
942
- `(filtered from ${toolDefs.length})`
976
+ `(selected from ${toolDefs.length})`
943
977
  );
944
978
  }
945
979
 
@@ -1015,7 +1049,7 @@ export function createProgrammaticToolCallingTool(
1015
1049
 
1016
1050
  const toolResults = await executeTools(
1017
1051
  response.tool_calls ?? [],
1018
- toolMap
1052
+ effectiveToolMap
1019
1053
  );
1020
1054
 
1021
1055
  response = await makeRequest(