@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
@@ -62,7 +62,6 @@ import {
62
62
  SUBAGENT_PARENT_BATCH_CONFIG_KEY,
63
63
  SUBAGENT_REPLAY_CONTROLLER,
64
64
  } from '@/tools/subagent/SubagentReplay';
65
- import { attachRunStepResumeState } from '@/tools/runStepResume';
66
65
  import {
67
66
  INTENT_ARG,
68
67
  readOutcomeFields,
@@ -95,8 +94,17 @@ import {
95
94
  resolveLocalToolRegistry,
96
95
  resolveLocalExecutionTools,
97
96
  } from '@/tools/local';
97
+ import {
98
+ type CallerCapabilityProjection,
99
+ createCallerCapabilityProjectionSnapshot,
100
+ isToolDefinitionActive,
101
+ isProgrammaticControlTool,
102
+ mergeCallerCapabilityDefinitions,
103
+ resolveCallerCapabilityProjection,
104
+ } from '@/tools/CallerCapabilities';
98
105
  import { stripCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';
99
106
  import { Constants, GraphEvents, CODE_EXECUTION_TOOLS } from '@/common';
107
+ import { attachRunStepResumeState } from '@/tools/runStepResume';
100
108
 
101
109
  /** Host-facing batch requests must not carry the batch's breaker scope —
102
110
  * hosts spread `configurable` into their own run configs. */
@@ -110,6 +118,7 @@ function stripRunBreakerScope(
110
118
  return rest;
111
119
  }
112
120
  import { convertInjectedMessages } from '@/messages/injected';
121
+ import { stampSyntheticProviderMessage } from '@/messages/provenance';
113
122
  import { safeDispatchCustomEvent } from '@/utils/events';
114
123
  import { RunnableCallable, composeAbortSignals } from '@/utils';
115
124
  import {
@@ -692,8 +701,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
692
701
  >();
693
702
  /** Tool registry for filtering (lazy computation of programmatic maps) */
694
703
  private toolRegistry?: t.LCToolRegistry;
695
- /** Cached programmatic tools (computed once on first PTC call) */
696
- private programmaticCache?: t.ProgrammaticCache;
704
+ /** Schema-only definitions used when event mode has no runtime registry. */
705
+ private toolDefinitions?: t.LCToolRegistry;
706
+ /** Tool-map entries created or replaced by the local execution resolver. */
707
+ private localImplementationNames = new Set<string>();
708
+ /** Reads deferred-tool discovery state from the owning agent context. */
709
+ private getDiscoveredToolNames?: () => readonly string[];
697
710
  /** Reference to Graph's sessions map for automatic session injection */
698
711
  private sessions?: t.ToolSessionMap;
699
712
  /** Partition within `sessions` owned by this agent's execution profile. */
@@ -797,6 +810,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
797
810
  handleToolErrors,
798
811
  loadRuntimeTools,
799
812
  toolRegistry,
813
+ toolDefinitions,
814
+ getDiscoveredToolNames,
800
815
  sessions,
801
816
  codeSessionKey,
802
817
  eventDrivenMode,
@@ -872,6 +887,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
872
887
  toolRegistry,
873
888
  toolExecution,
874
889
  });
890
+ this.toolDefinitions = toolDefinitions;
891
+ this.getDiscoveredToolNames = getDiscoveredToolNames;
875
892
  this.sessions = sessions;
876
893
  this.codeSessionKey = codeSessionKey ?? Constants.EXECUTE_CODE;
877
894
  this.eventDrivenMode = eventDrivenMode ?? false;
@@ -986,10 +1003,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
986
1003
  const resolved = resolveLocalExecutionTools({
987
1004
  toolMap: this.toolMap,
988
1005
  toolExecution: this.toolExecution,
1006
+ toolRegistry: this.toolRegistry,
989
1007
  fileCheckpointer: this.fileCheckpointer,
990
1008
  });
991
1009
 
992
1010
  this.toolMap = resolved.toolMap;
1011
+ this.localImplementationNames = resolved.localImplementationNames;
993
1012
  if (resolved.fileCheckpointer != null) {
994
1013
  this.fileCheckpointer = resolved.fileCheckpointer;
995
1014
  }
@@ -1001,7 +1020,6 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1001
1020
  ...(this.directToolNames ?? new Set<string>()),
1002
1021
  ...resolved.directToolNames,
1003
1022
  ]);
1004
- this.programmaticCache = undefined;
1005
1023
  }
1006
1024
 
1007
1025
  /**
@@ -1097,30 +1115,61 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1097
1115
  this.settledInterruptingResults.clear();
1098
1116
  }
1099
1117
 
1100
- /**
1101
- * Returns cached programmatic tools, computing once on first access.
1102
- * Single iteration builds both toolMap and toolDefs simultaneously.
1103
- */
1104
- private getProgrammaticTools(): { toolMap: t.ToolMap; toolDefs: t.LCTool[] } {
1105
- if (this.programmaticCache) return this.programmaticCache;
1118
+ /** Returns the live caller projection used by direct and event execution. */
1119
+ private getCallerCapabilityProjection(): CallerCapabilityProjection {
1120
+ const discoveredToolNames = new Set(
1121
+ this.getDiscoveredToolNames?.() ?? []
1122
+ );
1123
+ return resolveCallerCapabilityProjection(
1124
+ mergeCallerCapabilityDefinitions(
1125
+ this.toolDefinitions?.values(),
1126
+ this.toolRegistry?.values()
1127
+ ),
1128
+ (toolDef) => isToolDefinitionActive(toolDef, discoveredToolNames)
1129
+ );
1130
+ }
1131
+
1132
+ /** Serializes the live caller projection for event-driven hosts. */
1133
+ private getCallerCapabilityProjectionSnapshot(): t.CallerCapabilityProjectionSnapshot {
1134
+ return createCallerCapabilityProjectionSnapshot(
1135
+ this.getCallerCapabilityProjection()
1136
+ );
1137
+ }
1106
1138
 
1139
+ /** Returns active tools projected by their effective caller capabilities. */
1140
+ private getProgrammaticTools(): t.ProgrammaticCache {
1107
1141
  const toolMap: t.ToolMap = new Map();
1108
1142
  const toolDefs: t.LCTool[] = [];
1109
-
1110
- if (this.toolRegistry) {
1111
- for (const [name, toolDef] of this.toolRegistry) {
1112
- if (
1113
- (toolDef.allowed_callers ?? ['direct']).includes('code_execution')
1114
- ) {
1115
- toolDefs.push(toolDef);
1116
- const tool = this.toolMap.get(name);
1117
- if (tool) toolMap.set(name, tool);
1118
- }
1143
+ const discoveredToolNames = new Set(
1144
+ this.getDiscoveredToolNames?.() ?? []
1145
+ );
1146
+ const executableCapabilities = resolveCallerCapabilityProjection(
1147
+ this.toolRegistry?.values() ?? [],
1148
+ (toolDef) => isToolDefinitionActive(toolDef, discoveredToolNames)
1149
+ );
1150
+ for (const toolDef of executableCapabilities.codeExecutionTools) {
1151
+ if (
1152
+ this.eventDrivenMode &&
1153
+ this.directToolNames?.has(toolDef.name) !== true &&
1154
+ !this.localImplementationNames.has(toolDef.name)
1155
+ ) {
1156
+ continue;
1157
+ }
1158
+ const tool = this.toolMap.get(toolDef.name);
1159
+ if (tool != null) {
1160
+ toolMap.set(toolDef.name, tool);
1161
+ toolDefs.push(toolDef);
1119
1162
  }
1120
1163
  }
1164
+ const activeCapabilities = this.getCallerCapabilityProjection();
1121
1165
 
1122
- this.programmaticCache = { toolMap, toolDefs };
1123
- return this.programmaticCache;
1166
+ return {
1167
+ toolMap,
1168
+ toolDefs,
1169
+ disallowedToolDefs: activeCapabilities.directOnlyTools
1170
+ .filter((toolDef) => !isProgrammaticControlTool(toolDef.name))
1171
+ .map((toolDef) => ({ name: toolDef.name })),
1172
+ };
1124
1173
  }
1125
1174
 
1126
1175
  /**
@@ -1331,11 +1380,14 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
1331
1380
  call.name === Constants.PROGRAMMATIC_TOOL_CALLING ||
1332
1381
  call.name === Constants.BASH_PROGRAMMATIC_TOOL_CALLING
1333
1382
  ) {
1334
- const { toolMap, toolDefs } = this.getProgrammaticTools();
1383
+ const { toolMap, toolDefs, disallowedToolDefs } =
1384
+ this.getProgrammaticTools();
1335
1385
  invokeParams = {
1336
1386
  ...invokeParams,
1337
1387
  toolMap,
1338
1388
  toolDefs,
1389
+ disallowedToolDefs,
1390
+ programmaticToolName: call.name,
1339
1391
  // Plumb the hook context into the programmatic-tool path so
1340
1392
  // inner tool calls made via the in-process bridge can run
1341
1393
  // through `PreToolUse` (deny / updatedInput) before reaching
@@ -3444,6 +3496,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3444
3496
  // the eager path sends `agentContext.agentId` — this must
3445
3497
  // match it at the top level too.
3446
3498
  agentId: this.executingAgentId,
3499
+ callerCapabilityProjection:
3500
+ this.getCallerCapabilityProjectionSnapshot(),
3447
3501
  configurable: stripRunBreakerScope(
3448
3502
  config.configurable as Record<string, unknown> | undefined
3449
3503
  ),
@@ -3927,10 +3981,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3927
3981
  * a user message; `role` is metadata only.
3928
3982
  */
3929
3983
  injected.push(
3930
- new HumanMessage({
3931
- content: batchAdditionalContexts.join('\n\n'),
3932
- additional_kwargs: { role: 'system', source: 'hook' },
3933
- })
3984
+ stampSyntheticProviderMessage(
3985
+ new HumanMessage({
3986
+ content: batchAdditionalContexts.join('\n\n'),
3987
+ additional_kwargs: { role: 'system', source: 'hook' },
3988
+ })
3989
+ )
3934
3990
  );
3935
3991
  }
3936
3992
 
@@ -4423,13 +4479,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4423
4479
  directAdditionalContexts.length > 0
4424
4480
  ? [
4425
4481
  sendOutput,
4426
- new HumanMessage({
4427
- content: directAdditionalContexts.join('\n\n'),
4428
- // Match the event-driven path's marker so hosts /
4429
- // model-side annotators treat this as system intent
4430
- // rather than ordinary user text. Codex P2 [46].
4431
- additional_kwargs: { role: 'system', source: 'hook' },
4432
- }),
4482
+ stampSyntheticProviderMessage(
4483
+ new HumanMessage({
4484
+ content: directAdditionalContexts.join('\n\n'),
4485
+ // Match the event-driven path's marker so hosts /
4486
+ // model-side annotators treat this as system intent
4487
+ // rather than ordinary user text. Codex P2 [46].
4488
+ additional_kwargs: { role: 'system', source: 'hook' },
4489
+ })
4490
+ ),
4433
4491
  ]
4434
4492
  : [sendOutput];
4435
4493
  await this.handleRunToolCompletions(
@@ -4477,7 +4535,6 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4477
4535
  this.toolMap =
4478
4536
  toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
4479
4537
  this.applyToolExecutionOverrides();
4480
- this.programmaticCache = undefined; // Invalidate cache on toolMap change
4481
4538
  }
4482
4539
 
4483
4540
  const filteredCalls =
@@ -4721,14 +4778,16 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4721
4778
  const directInjected: BaseMessage[] =
4722
4779
  directAdditionalContexts.length > 0
4723
4780
  ? [
4724
- new HumanMessage({
4725
- content: directAdditionalContexts.join('\n\n'),
4726
- // System-role metadata to match the event-driven
4727
- // path so policy/recovery guidance is treated
4728
- // consistently regardless of whether the tool ran
4729
- // direct or dispatched. Codex P2 [46].
4730
- additional_kwargs: { role: 'system', source: 'hook' },
4731
- }),
4781
+ stampSyntheticProviderMessage(
4782
+ new HumanMessage({
4783
+ content: directAdditionalContexts.join('\n\n'),
4784
+ // System-role metadata to match the event-driven
4785
+ // path so policy/recovery guidance is treated
4786
+ // consistently regardless of whether the tool ran
4787
+ // direct or dispatched. Codex P2 [46].
4788
+ additional_kwargs: { role: 'system', source: 'hook' },
4789
+ })
4790
+ ),
4732
4791
  ]
4733
4792
  : [];
4734
4793
  outputs = [
@@ -4782,13 +4841,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
4782
4841
  ...promotedPrefix,
4783
4842
  ...toolOutputs,
4784
4843
  ...invalidCallResults,
4785
- new HumanMessage({
4786
- content: directAdditionalContexts.join('\n\n'),
4787
- // Same system-role marker the event-driven path
4788
- // uses so direct vs dispatched is invisible to
4789
- // downstream consumers. Codex P2 [46].
4790
- additional_kwargs: { role: 'system', source: 'hook' },
4791
- }),
4844
+ stampSyntheticProviderMessage(
4845
+ new HumanMessage({
4846
+ content: directAdditionalContexts.join('\n\n'),
4847
+ // Same system-role marker the event-driven path
4848
+ // uses so direct vs dispatched is invisible to
4849
+ // downstream consumers. Codex P2 [46].
4850
+ additional_kwargs: { role: 'system', source: 'hook' },
4851
+ })
4852
+ ),
4792
4853
  ]
4793
4854
  : [...promotedPrefix, ...toolOutputs, ...invalidCallResults];
4794
4855
  }
@@ -9,14 +9,17 @@ import {
9
9
  ProgrammaticToolCallingDescription,
10
10
  ProgrammaticToolCallingName,
11
11
  ProgrammaticToolCallingSchema,
12
- filterToolsByUsage,
13
12
  } from '@/tools/ProgrammaticToolCalling';
14
13
  import {
15
14
  BashProgrammaticToolCallingDescription,
16
15
  BashProgrammaticToolCallingSchema,
17
- filterBashToolsByUsage,
18
16
  normalizeToBashIdentifier,
19
17
  } from '@/tools/BashProgrammaticToolCalling';
18
+ import {
19
+ resolveProgrammaticToolDefinitions,
20
+ selectProgrammaticTools,
21
+ type ProgrammaticInvocationParams,
22
+ } from '@/tools/ProgrammaticCallerPolicy';
20
23
  import { Constants } from '@/common';
21
24
  import {
22
25
  clientExecTimeoutMs,
@@ -27,13 +30,7 @@ import {
27
30
  validateCloudflareBashCommand,
28
31
  } from './CloudflareSandboxExecutionEngine';
29
32
 
30
- type ProgrammaticParams = {
31
- code: string;
32
- timeout?: number;
33
- lang?: string;
34
- runtime?: string;
35
- language?: string;
36
- };
33
+ type ProgrammaticParams = ProgrammaticInvocationParams;
37
34
 
38
35
  const DEFAULT_TIMEOUT = 60000;
39
36
  const MIN_TIMEOUT = 1000;
@@ -224,15 +221,8 @@ function resolveRuntime(params: ProgrammaticParams): 'python' | 'bash' {
224
221
  return raw === 'py' || raw === 'python' ? 'python' : 'bash';
225
222
  }
226
223
 
227
- function filterNativeTools(
228
- toolDefs: t.LCTool[],
229
- code: string,
230
- runtime: 'python' | 'bash'
231
- ): t.LCTool[] {
232
- const nativeDefs = toolDefs.filter((def) => NATIVE_TOOL_NAMES.has(def.name));
233
- const filter =
234
- runtime === 'bash' ? filterBashToolsByUsage : filterToolsByUsage;
235
- return filter(nativeDefs, code);
224
+ function filterNativeTools(toolDefs: t.LCTool[]): t.LCTool[] {
225
+ return toolDefs.filter((def) => NATIVE_TOOL_NAMES.has(def.name));
236
226
  }
237
227
 
238
228
  function indent(code: string, spaces = 4): string {
@@ -1060,14 +1050,22 @@ async function runProgrammatic(args: {
1060
1050
  cloudflareConfig: t.CloudflareSandboxExecutionConfig;
1061
1051
  runtime: 'python' | 'bash';
1062
1052
  }): Promise<[string, t.ProgrammaticExecutionArtifact]> {
1063
- const toolCall = (args.config?.toolCall ??
1064
- {}) as Partial<t.ProgrammaticCache>;
1065
- const toolDefs = toolCall.toolDefs ?? [];
1066
- const effectiveTools = filterNativeTools(
1067
- toolDefs,
1068
- args.params.code,
1069
- args.runtime
1070
- );
1053
+ const toolCall = (args.config?.toolCall ?? {}) as Partial<
1054
+ t.ProgrammaticCache
1055
+ > & { tools?: t.LCTool[] };
1056
+ const toolDefs = resolveProgrammaticToolDefinitions(toolCall);
1057
+ const programmaticToolName =
1058
+ toolCall.programmaticToolName ??
1059
+ (args.runtime === 'bash'
1060
+ ? Constants.BASH_PROGRAMMATIC_TOOL_CALLING
1061
+ : Constants.PROGRAMMATIC_TOOL_CALLING);
1062
+ const selectedTools = selectProgrammaticTools({
1063
+ requestedToolNames: args.params.tool_manifest,
1064
+ allowedToolDefs: toolDefs,
1065
+ disallowedToolDefs: toolCall.disallowedToolDefs,
1066
+ programmaticToolName,
1067
+ });
1068
+ const effectiveTools = filterNativeTools(selectedTools);
1071
1069
  const timeoutMs = clampExecutionTimeout(
1072
1070
  args.params.timeout,
1073
1071
  args.cloudflareConfig.timeoutMs
@@ -7,7 +7,6 @@ import type { AddressInfo } from 'net';
7
7
  import type * as t from '@/types';
8
8
  import {
9
9
  executeTools,
10
- filterToolsByUsage,
11
10
  formatCompletedResponse,
12
11
  normalizeToPythonIdentifier,
13
12
  ProgrammaticToolCallingName,
@@ -17,9 +16,13 @@ import {
17
16
  import {
18
17
  BashProgrammaticToolCallingSchema,
19
18
  BashProgrammaticToolCallingDescription,
20
- filterBashToolsByUsage,
21
19
  normalizeToBashIdentifier,
22
20
  } from '@/tools/BashProgrammaticToolCalling';
21
+ import {
22
+ resolveProgrammaticToolDefinitions,
23
+ selectProgrammaticTools,
24
+ type ProgrammaticInvocationParams,
25
+ } from '@/tools/ProgrammaticCallerPolicy';
23
26
  import {
24
27
  executeLocalBash,
25
28
  executeLocalCode,
@@ -150,15 +153,7 @@ function constantTimeEquals(a: string, b: string): boolean {
150
153
 
151
154
  type LocalProgrammaticRuntime = 'python' | 'bash';
152
155
 
153
- type LocalProgrammaticParams = {
154
- code: string;
155
- timeout?: number;
156
- lang?: string;
157
- runtime?: string;
158
- language?: string;
159
- };
160
-
161
- type ToolFilter = (toolDefs: t.LCTool[], code: string) => t.LCTool[];
156
+ type LocalProgrammaticParams = ProgrammaticInvocationParams;
162
157
 
163
158
  function resolveRuntime(
164
159
  params: LocalProgrammaticParams
@@ -547,17 +542,16 @@ ${code}
547
542
 
548
543
  function getProgrammaticContext(config?: {
549
544
  toolCall?: unknown;
550
- }): Partial<t.ProgrammaticCache> {
551
- return (config?.toolCall ?? {}) as Partial<t.ProgrammaticCache>;
545
+ }): Partial<t.ProgrammaticCache> & { tools?: t.LCTool[] } {
546
+ return (config?.toolCall ?? {}) as Partial<t.ProgrammaticCache> & {
547
+ tools?: t.LCTool[];
548
+ };
552
549
  }
553
550
 
554
551
  function createEffectiveToolMap(
555
552
  toolMap: t.ToolMap,
556
- toolDefs: t.LCTool[],
557
- code: string,
558
- filterTools: ToolFilter
553
+ effectiveTools: t.LCTool[]
559
554
  ): { effectiveTools: t.LCTool[]; effectiveMap: t.ToolMap } {
560
- const effectiveTools = filterTools(toolDefs, code);
561
555
  const effectiveMap = new Map<string, t.GenericTool>(
562
556
  effectiveTools
563
557
  .map((def) => {
@@ -578,9 +572,22 @@ async function runLocalProgrammaticTool(args: {
578
572
  localConfig: t.LocalExecutionConfig;
579
573
  runtime: LocalProgrammaticRuntime;
580
574
  }): Promise<[string, t.ProgrammaticExecutionArtifact]> {
581
- const { toolMap, toolDefs, hookContext } = getProgrammaticContext(
582
- args.config
583
- );
575
+ const context = getProgrammaticContext(args.config);
576
+ const { toolMap, disallowedToolDefs, programmaticToolName, hookContext } =
577
+ context;
578
+ const toolDefs = resolveProgrammaticToolDefinitions(context);
579
+
580
+ const runnerName =
581
+ programmaticToolName ??
582
+ (args.runtime === 'bash'
583
+ ? Constants.BASH_PROGRAMMATIC_TOOL_CALLING
584
+ : Constants.PROGRAMMATIC_TOOL_CALLING);
585
+ const selectedTools = selectProgrammaticTools({
586
+ requestedToolNames: args.params.tool_manifest,
587
+ allowedToolDefs: toolDefs,
588
+ disallowedToolDefs,
589
+ programmaticToolName: runnerName,
590
+ });
584
591
 
585
592
  if (toolMap == null || toolMap.size === 0) {
586
593
  throw new Error('No toolMap provided for local programmatic execution.');
@@ -593,9 +600,7 @@ async function runLocalProgrammaticTool(args: {
593
600
 
594
601
  const { effectiveTools, effectiveMap } = createEffectiveToolMap(
595
602
  toolMap,
596
- toolDefs,
597
- args.params.code,
598
- args.runtime === 'bash' ? filterBashToolsByUsage : filterToolsByUsage
603
+ selectedTools
599
604
  );
600
605
  const bridge = await createToolBridge(effectiveMap, hookContext);
601
606