@librechat/agents 3.1.68 → 3.1.71-dev.0

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 (192) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +23 -3
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +16 -1
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +136 -0
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/hooks/HookRegistry.cjs +162 -0
  8. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -0
  9. package/dist/cjs/hooks/executeHooks.cjs +276 -0
  10. package/dist/cjs/hooks/executeHooks.cjs.map +1 -0
  11. package/dist/cjs/hooks/matchers.cjs +256 -0
  12. package/dist/cjs/hooks/matchers.cjs.map +1 -0
  13. package/dist/cjs/hooks/types.cjs +27 -0
  14. package/dist/cjs/hooks/types.cjs.map +1 -0
  15. package/dist/cjs/main.cjs +57 -0
  16. package/dist/cjs/main.cjs.map +1 -1
  17. package/dist/cjs/messages/format.cjs +74 -12
  18. package/dist/cjs/messages/format.cjs.map +1 -1
  19. package/dist/cjs/messages/prune.cjs +9 -2
  20. package/dist/cjs/messages/prune.cjs.map +1 -1
  21. package/dist/cjs/run.cjs +115 -0
  22. package/dist/cjs/run.cjs.map +1 -1
  23. package/dist/cjs/summarization/node.cjs +44 -0
  24. package/dist/cjs/summarization/node.cjs.map +1 -1
  25. package/dist/cjs/tools/BashExecutor.cjs +208 -0
  26. package/dist/cjs/tools/BashExecutor.cjs.map +1 -0
  27. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +287 -0
  28. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -0
  29. package/dist/cjs/tools/CodeExecutor.cjs +0 -9
  30. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  31. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +7 -23
  32. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  33. package/dist/cjs/tools/ReadFile.cjs +43 -0
  34. package/dist/cjs/tools/ReadFile.cjs.map +1 -0
  35. package/dist/cjs/tools/SkillTool.cjs +50 -0
  36. package/dist/cjs/tools/SkillTool.cjs.map +1 -0
  37. package/dist/cjs/tools/SubagentTool.cjs +92 -0
  38. package/dist/cjs/tools/SubagentTool.cjs.map +1 -0
  39. package/dist/cjs/tools/ToolNode.cjs +746 -174
  40. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  41. package/dist/cjs/tools/ToolSearch.cjs +2 -13
  42. package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
  43. package/dist/cjs/tools/skillCatalog.cjs +84 -0
  44. package/dist/cjs/tools/skillCatalog.cjs.map +1 -0
  45. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +511 -0
  46. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -0
  47. package/dist/cjs/tools/toolOutputReferences.cjs +475 -0
  48. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -0
  49. package/dist/cjs/utils/truncation.cjs +28 -0
  50. package/dist/cjs/utils/truncation.cjs.map +1 -1
  51. package/dist/esm/agents/AgentContext.mjs +23 -3
  52. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  53. package/dist/esm/common/enum.mjs +15 -2
  54. package/dist/esm/common/enum.mjs.map +1 -1
  55. package/dist/esm/graphs/Graph.mjs +136 -0
  56. package/dist/esm/graphs/Graph.mjs.map +1 -1
  57. package/dist/esm/hooks/HookRegistry.mjs +160 -0
  58. package/dist/esm/hooks/HookRegistry.mjs.map +1 -0
  59. package/dist/esm/hooks/executeHooks.mjs +273 -0
  60. package/dist/esm/hooks/executeHooks.mjs.map +1 -0
  61. package/dist/esm/hooks/matchers.mjs +251 -0
  62. package/dist/esm/hooks/matchers.mjs.map +1 -0
  63. package/dist/esm/hooks/types.mjs +25 -0
  64. package/dist/esm/hooks/types.mjs.map +1 -0
  65. package/dist/esm/main.mjs +13 -2
  66. package/dist/esm/main.mjs.map +1 -1
  67. package/dist/esm/messages/format.mjs +66 -4
  68. package/dist/esm/messages/format.mjs.map +1 -1
  69. package/dist/esm/messages/prune.mjs +9 -2
  70. package/dist/esm/messages/prune.mjs.map +1 -1
  71. package/dist/esm/run.mjs +115 -0
  72. package/dist/esm/run.mjs.map +1 -1
  73. package/dist/esm/summarization/node.mjs +44 -0
  74. package/dist/esm/summarization/node.mjs.map +1 -1
  75. package/dist/esm/tools/BashExecutor.mjs +200 -0
  76. package/dist/esm/tools/BashExecutor.mjs.map +1 -0
  77. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +278 -0
  78. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -0
  79. package/dist/esm/tools/CodeExecutor.mjs +0 -9
  80. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  81. package/dist/esm/tools/ProgrammaticToolCalling.mjs +8 -24
  82. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  83. package/dist/esm/tools/ReadFile.mjs +38 -0
  84. package/dist/esm/tools/ReadFile.mjs.map +1 -0
  85. package/dist/esm/tools/SkillTool.mjs +45 -0
  86. package/dist/esm/tools/SkillTool.mjs.map +1 -0
  87. package/dist/esm/tools/SubagentTool.mjs +85 -0
  88. package/dist/esm/tools/SubagentTool.mjs.map +1 -0
  89. package/dist/esm/tools/ToolNode.mjs +748 -176
  90. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  91. package/dist/esm/tools/ToolSearch.mjs +3 -14
  92. package/dist/esm/tools/ToolSearch.mjs.map +1 -1
  93. package/dist/esm/tools/skillCatalog.mjs +82 -0
  94. package/dist/esm/tools/skillCatalog.mjs.map +1 -0
  95. package/dist/esm/tools/subagent/SubagentExecutor.mjs +505 -0
  96. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -0
  97. package/dist/esm/tools/toolOutputReferences.mjs +468 -0
  98. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -0
  99. package/dist/esm/utils/truncation.mjs +27 -1
  100. package/dist/esm/utils/truncation.mjs.map +1 -1
  101. package/dist/types/agents/AgentContext.d.ts +6 -0
  102. package/dist/types/common/enum.d.ts +10 -2
  103. package/dist/types/graphs/Graph.d.ts +23 -0
  104. package/dist/types/hooks/HookRegistry.d.ts +56 -0
  105. package/dist/types/hooks/executeHooks.d.ts +79 -0
  106. package/dist/types/hooks/index.d.ts +6 -0
  107. package/dist/types/hooks/matchers.d.ts +95 -0
  108. package/dist/types/hooks/types.d.ts +320 -0
  109. package/dist/types/index.d.ts +8 -0
  110. package/dist/types/messages/format.d.ts +2 -1
  111. package/dist/types/run.d.ts +2 -0
  112. package/dist/types/summarization/node.d.ts +2 -0
  113. package/dist/types/tools/BashExecutor.d.ts +76 -0
  114. package/dist/types/tools/BashProgrammaticToolCalling.d.ts +72 -0
  115. package/dist/types/tools/ProgrammaticToolCalling.d.ts +4 -9
  116. package/dist/types/tools/ReadFile.d.ts +28 -0
  117. package/dist/types/tools/SkillTool.d.ts +40 -0
  118. package/dist/types/tools/SubagentTool.d.ts +36 -0
  119. package/dist/types/tools/ToolNode.d.ts +109 -4
  120. package/dist/types/tools/ToolSearch.d.ts +2 -2
  121. package/dist/types/tools/skillCatalog.d.ts +19 -0
  122. package/dist/types/tools/subagent/SubagentExecutor.d.ts +137 -0
  123. package/dist/types/tools/subagent/index.d.ts +2 -0
  124. package/dist/types/tools/toolOutputReferences.d.ts +205 -0
  125. package/dist/types/types/graph.d.ts +61 -2
  126. package/dist/types/types/index.d.ts +1 -0
  127. package/dist/types/types/run.d.ts +28 -0
  128. package/dist/types/types/skill.d.ts +9 -0
  129. package/dist/types/types/tools.d.ts +108 -10
  130. package/dist/types/utils/truncation.d.ts +21 -0
  131. package/package.json +5 -1
  132. package/src/agents/AgentContext.ts +26 -2
  133. package/src/common/enum.ts +15 -1
  134. package/src/graphs/Graph.ts +161 -0
  135. package/src/hooks/HookRegistry.ts +208 -0
  136. package/src/hooks/__tests__/HookRegistry.test.ts +190 -0
  137. package/src/hooks/__tests__/compactHooks.test.ts +214 -0
  138. package/src/hooks/__tests__/executeHooks.test.ts +1013 -0
  139. package/src/hooks/__tests__/integration.test.ts +337 -0
  140. package/src/hooks/__tests__/matchers.test.ts +238 -0
  141. package/src/hooks/__tests__/toolHooks.test.ts +669 -0
  142. package/src/hooks/executeHooks.ts +375 -0
  143. package/src/hooks/index.ts +57 -0
  144. package/src/hooks/matchers.ts +280 -0
  145. package/src/hooks/types.ts +404 -0
  146. package/src/index.ts +10 -0
  147. package/src/messages/format.ts +74 -4
  148. package/src/messages/formatAgentMessages.skills.test.ts +334 -0
  149. package/src/messages/prune.ts +9 -2
  150. package/src/run.ts +130 -0
  151. package/src/scripts/multi-agent-subagent.ts +246 -0
  152. package/src/scripts/programmatic_exec.ts +1 -10
  153. package/src/scripts/subagent-event-driven-debug.ts +190 -0
  154. package/src/scripts/subagent-tools-debug.ts +160 -0
  155. package/src/scripts/test_code_api.ts +0 -7
  156. package/src/scripts/tool_search.ts +1 -10
  157. package/src/specs/prune.test.ts +413 -0
  158. package/src/specs/subagent.test.ts +305 -0
  159. package/src/summarization/node.ts +53 -0
  160. package/src/tools/BashExecutor.ts +238 -0
  161. package/src/tools/BashProgrammaticToolCalling.ts +381 -0
  162. package/src/tools/CodeExecutor.ts +0 -11
  163. package/src/tools/ProgrammaticToolCalling.ts +4 -29
  164. package/src/tools/ReadFile.ts +39 -0
  165. package/src/tools/SkillTool.ts +46 -0
  166. package/src/tools/SubagentTool.ts +100 -0
  167. package/src/tools/ToolNode.ts +999 -214
  168. package/src/tools/ToolSearch.ts +3 -19
  169. package/src/tools/__tests__/BashExecutor.test.ts +36 -0
  170. package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.ts +7 -8
  171. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -1
  172. package/src/tools/__tests__/ReadFile.test.ts +44 -0
  173. package/src/tools/__tests__/SkillTool.test.ts +442 -0
  174. package/src/tools/__tests__/SubagentExecutor.test.ts +1148 -0
  175. package/src/tools/__tests__/SubagentTool.test.ts +149 -0
  176. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +1395 -0
  177. package/src/tools/__tests__/ToolNode.session.test.ts +12 -12
  178. package/src/tools/__tests__/ToolSearch.integration.test.ts +7 -8
  179. package/src/tools/__tests__/skillCatalog.test.ts +161 -0
  180. package/src/tools/__tests__/subagentHooks.test.ts +215 -0
  181. package/src/tools/__tests__/toolOutputReferences.test.ts +415 -0
  182. package/src/tools/skillCatalog.ts +126 -0
  183. package/src/tools/subagent/SubagentExecutor.ts +676 -0
  184. package/src/tools/subagent/index.ts +13 -0
  185. package/src/tools/toolOutputReferences.ts +590 -0
  186. package/src/types/graph.ts +80 -1
  187. package/src/types/index.ts +1 -0
  188. package/src/types/run.ts +28 -0
  189. package/src/types/skill.ts +11 -0
  190. package/src/types/tools.ts +112 -10
  191. package/src/utils/__tests__/truncation.test.ts +66 -0
  192. package/src/utils/truncation.ts +30 -0
package/src/types/run.ts CHANGED
@@ -11,6 +11,8 @@ import type * as s from '@/types/stream';
11
11
  import type * as e from '@/common/enum';
12
12
  import type * as g from '@/types/graph';
13
13
  import type * as l from '@/types/llm';
14
+ import type { ToolSessionMap, ToolOutputReferencesConfig } from '@/types/tools';
15
+ import type { HookRegistry } from '@/hooks';
14
16
 
15
17
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
18
  export type ZodObjectAny = z.ZodObject<any, any, any, any>;
@@ -112,6 +114,18 @@ export type RunConfig = {
112
114
  runId: string;
113
115
  graphConfig: LegacyGraphConfig | StandardGraphConfig | MultiAgentGraphConfig;
114
116
  customHandlers?: Record<string, g.EventHandler>;
117
+ /**
118
+ * Pre-constructed hook registry for this run. Hooks fire at lifecycle
119
+ * points in `processStream` (RunStart, UserPromptSubmit, Stop,
120
+ * StopFailure) and around tool calls (PreToolUse, PostToolUse,
121
+ * PostToolUseFailure, PermissionDenied).
122
+ *
123
+ * Pass `undefined` (the default) to skip all hook dispatch. When a
124
+ * registry is provided, the run attaches it to the `Graph` so internal
125
+ * nodes can fire hooks too, and clears the session in the `finally`
126
+ * block to prevent leaks.
127
+ */
128
+ hooks?: HookRegistry;
115
129
  returnContent?: boolean;
116
130
  tokenCounter?: TokenCounter;
117
131
  indexTokenCountMap?: Record<string, number>;
@@ -126,6 +140,20 @@ export type RunConfig = {
126
140
  calibrationRatio?: number;
127
141
  /** Skip post-stream cleanup (clearHeavyState) — useful for tests that inspect graph state after processStream */
128
142
  skipCleanup?: boolean;
143
+ /**
144
+ * Initial session state to seed the Graph's ToolSessionMap.
145
+ * Used to carry over code environment sessions from skill file priming
146
+ * at run start, so ToolNode can inject session_id + files into tool calls.
147
+ */
148
+ initialSessions?: ToolSessionMap;
149
+ /**
150
+ * Run-scoped tool output reference configuration. When `enabled` is
151
+ * `true`, tool outputs are registered under stable keys
152
+ * (`tool<idx>turn<turn>`) and subsequent tool calls can pipe previous
153
+ * outputs into their arguments via `{{tool<idx>turn<turn>}}`
154
+ * placeholders. Disabled by default so existing runs are unaffected.
155
+ */
156
+ toolOutputReferences?: ToolOutputReferencesConfig;
129
157
  };
130
158
 
131
159
  export type ProvidedCallbacks =
@@ -0,0 +1,11 @@
1
+ // src/types/skill.ts
2
+
3
+ /** Minimal skill metadata for catalog assembly. The host provides these from its own data layer. */
4
+ export type SkillCatalogEntry = {
5
+ /** Kebab-case identifier (what the model passes to SkillTool) */
6
+ name: string;
7
+ /** One-line description for the catalog listing */
8
+ description: string;
9
+ /** Optional human-readable label (UI only, not shown to model) */
10
+ displayTitle?: string;
11
+ };
@@ -2,8 +2,9 @@
2
2
  import type { StructuredToolInterface } from '@langchain/core/tools';
3
3
  import type { RunnableToolLike } from '@langchain/core/runnables';
4
4
  import type { ToolCall } from '@langchain/core/messages/tool';
5
- import type { ToolErrorData } from './stream';
6
- import { EnvVar } from '@/common';
5
+ import type { HookRegistry } from '@/hooks';
6
+ import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
7
+ import type { MessageContentComplex, ToolErrorData } from './stream';
7
8
 
8
9
  /** Replacement type for `import type { ToolCall } from '@langchain/core/messages/tool'` in order to have stringified args typed */
9
10
  export type CustomToolCall = {
@@ -49,6 +50,12 @@ export type ToolNodeOptions = {
49
50
  agentId?: string;
50
51
  /** Tool names that must be executed directly (via runTool) even in event-driven mode (e.g., graph-managed handoff tools) */
51
52
  directToolNames?: Set<string>;
53
+ /**
54
+ * Hook registry for PreToolUse/PostToolUse lifecycle hooks.
55
+ * Only fires for event-driven tool calls (`dispatchToolEvents`). Tools
56
+ * routed through `directToolNames` bypass hook dispatch entirely.
57
+ */
58
+ hookRegistry?: HookRegistry;
52
59
  /** Max context tokens for the agent — used to compute tool result truncation limits. */
53
60
  maxContextTokens?: number;
54
61
  /**
@@ -56,6 +63,22 @@ export type ToolNodeOptions = {
56
63
  * When provided, takes precedence over the value computed from maxContextTokens.
57
64
  */
58
65
  maxToolResultChars?: number;
66
+ /**
67
+ * Run-scoped tool output reference configuration. When `enabled` is
68
+ * `true`, ToolNode registers successful outputs and substitutes
69
+ * `{{tool<idx>turn<turn>}}` placeholders found in string args.
70
+ *
71
+ * Ignored when `toolOutputRegistry` is also provided (host-supplied
72
+ * registry wins).
73
+ */
74
+ toolOutputReferences?: ToolOutputReferencesConfig;
75
+ /**
76
+ * Pre-constructed registry instance shared across ToolNodes for the
77
+ * run. Graphs pass the same registry to every ToolNode they compile
78
+ * so cross-agent `{{tool<i>turn<n>}}` substitutions resolve. Takes
79
+ * precedence over `toolOutputReferences` when both are set.
80
+ */
81
+ toolOutputRegistry?: ToolOutputReferenceRegistry;
59
82
  };
60
83
 
61
84
  export type ToolNodeConstructorParams = ToolRefs & ToolNodeOptions;
@@ -81,9 +104,7 @@ export type CodeExecutionToolParams =
81
104
  | {
82
105
  session_id?: string;
83
106
  user_id?: string;
84
- apiKey?: string;
85
107
  files?: CodeEnvFile[];
86
- [EnvVar.CODE_API_KEY]?: string;
87
108
  };
88
109
 
89
110
  export type FileRef = {
@@ -186,6 +207,26 @@ export type ToolExecuteBatchRequest = {
186
207
  reject: (error: Error) => void;
187
208
  };
188
209
 
210
+ /**
211
+ * A message injected into graph state by any tool execution handler.
212
+ * Generic mechanism: any tool returning `injectedMessages` in its `ToolExecuteResult`
213
+ * will have these appended to state after the ToolMessage for this call.
214
+ */
215
+ export type InjectedMessage = {
216
+ /** 'user' for skill body injection, 'system' for context hints.
217
+ * Both are converted to HumanMessage at runtime; the original role
218
+ * is preserved in additional_kwargs.role. */
219
+ role: 'user' | 'system';
220
+ /** Message content: string for simple text, array for complex multi-part content */
221
+ content: string | MessageContentComplex[];
222
+ /** When true, the message is framework-internal: not shown in UI, not counted as a user turn */
223
+ isMeta?: boolean;
224
+ /** Origin tag for downstream consumers (UI, pruner, compaction) */
225
+ source?: 'skill' | 'hook' | 'system';
226
+ /** Only set when source is 'skill', for compaction preservation */
227
+ skillName?: string;
228
+ };
229
+
189
230
  /** Result for a single tool call in event-driven execution */
190
231
  export type ToolExecuteResult = {
191
232
  /** Matches ToolCallRequest.id */
@@ -198,11 +239,72 @@ export type ToolExecuteResult = {
198
239
  status: 'success' | 'error';
199
240
  /** Error message if status is 'error' */
200
241
  errorMessage?: string;
242
+ /**
243
+ * Messages to inject into graph state after the ToolMessage for this call.
244
+ * Placed after tool results to respect provider message ordering (tool_call -> tool_result adjacency).
245
+ * The host's message formatter may merge injected user messages with the preceding tool_result turn.
246
+ * Generic mechanism: any tool execution handler can use this.
247
+ */
248
+ injectedMessages?: InjectedMessage[];
201
249
  };
202
250
 
203
251
  /** Map of tool names to tool definitions */
204
252
  export type LCToolRegistry = Map<string, LCTool>;
205
253
 
254
+ /**
255
+ * Run-scoped configuration for tool output references.
256
+ *
257
+ * When enabled, each successful tool result is registered under a stable
258
+ * key (`tool<idx>turn<turn>`). Later tool calls can pipe a previous
259
+ * output into their arguments by including the literal placeholder
260
+ * `{{tool<idx>turn<turn>}}` anywhere in a string argument; ToolNode
261
+ * substitutes it with the stored output immediately before invoking
262
+ * the tool.
263
+ *
264
+ * The registry stores the *raw, untruncated* tool output (subject to
265
+ * its own size caps) so a later substitution can pipe the full payload
266
+ * into the next tool even when the LLM only saw a head+tail-truncated
267
+ * preview in `ToolMessage.content`. Size limits are decoupled from the
268
+ * LLM-visible truncation budget and default to 5 MB total.
269
+ *
270
+ * Known limitations:
271
+ * - Tools that return a `ToolMessage` with array-type content
272
+ * (multi-part content blocks such as text + image) are not
273
+ * registered and cannot be cited via `{{tool<i>turn<n>}}`. A
274
+ * warning is logged so the missing reference is visible.
275
+ * - When a `PostToolUse` hook replaces `ToolMessage.content`, the
276
+ * *post-hook* content is what gets stored in the registry (and
277
+ * what the model sees), so `{{…}}` substitutions deliver the
278
+ * hooked output rather than the raw tool return. This matches the
279
+ * hook's "authoritative" role for output shaping.
280
+ */
281
+ export type ToolOutputReferencesConfig = {
282
+ /** Enable the registry and placeholder substitution. Defaults to `false`. */
283
+ enabled?: boolean;
284
+ /**
285
+ * Maximum characters stored (and substituted) per registered output.
286
+ * Applied to the *raw* output before storage. Defaults to
287
+ * `HARD_MAX_TOOL_RESULT_CHARS` (~400 KB) — matching the
288
+ * LLM-visible tool-result truncation budget, which is also a safe
289
+ * payload size for shell `ARG_MAX` limits when a `{{…}}` expansion
290
+ * gets piped into a bash `command`. Hosts that want to preserve
291
+ * fuller fidelity (for example for non-bash API consumers) can
292
+ * raise this up to `maxTotalSize` (defaults to 5 MB) — be aware
293
+ * that large single-output substitutions may exceed shell
294
+ * argument-size limits on typical Linux/macOS.
295
+ */
296
+ maxOutputSize?: number;
297
+ /**
298
+ * Hard cap on total characters retained across all registered outputs
299
+ * for the run. When exceeded, the oldest entries are evicted FIFO
300
+ * until the total fits. The effective per-output cap is
301
+ * `min(maxOutputSize, maxTotalSize)` so a single stored output can
302
+ * never exceed the aggregate bound. Defaults to
303
+ * `calculateMaxTotalToolOutputSize(maxOutputSize)` (5 MB).
304
+ */
305
+ maxTotalSize?: number;
306
+ };
307
+
206
308
  export type ProgrammaticCache = { toolMap: ToolMap; toolDefs: LCTool[] };
207
309
 
208
310
  /** Search mode: code_interpreter uses external sandbox, local uses safe substring matching */
@@ -213,7 +315,6 @@ export type McpNameFormat = 'full' | 'base';
213
315
 
214
316
  /** Parameters for creating a Tool Search tool */
215
317
  export type ToolSearchParams = {
216
- apiKey?: string;
217
318
  toolRegistry?: LCToolRegistry;
218
319
  onlyDeferred?: boolean;
219
320
  baseUrl?: string;
@@ -223,7 +324,6 @@ export type ToolSearchParams = {
223
324
  mcpServer?: string | string[];
224
325
  /** Format for MCP tool names: 'full' (tool_mcp_server) or 'base' (tool only). Default: 'full' */
225
326
  mcpNameFormat?: McpNameFormat;
226
- [key: string]: unknown;
227
327
  };
228
328
 
229
329
  /** Simplified tool metadata for search purposes */
@@ -318,12 +418,16 @@ export type ProgrammaticExecutionArtifact = {
318
418
  files?: FileRefs;
319
419
  };
320
420
 
421
+ /** Parameters for creating a bash execution tool (same API as CodeExecutor, bash-only) */
422
+ export type BashExecutionToolParams = CodeExecutionToolParams;
423
+
424
+ /** Parameters for creating a bash programmatic tool calling tool (same API as PTC, bash-only) */
425
+ export type BashProgrammaticToolCallingParams = ProgrammaticToolCallingParams;
426
+
321
427
  /**
322
428
  * Initialization parameters for the PTC tool
323
429
  */
324
430
  export type ProgrammaticToolCallingParams = {
325
- /** Code API key (or use CODE_API_KEY env var) */
326
- apiKey?: string;
327
431
  /** Code API base URL (or use CODE_BASEURL env var) */
328
432
  baseUrl?: string;
329
433
  /** Safety limit for round-trips (default: 20) */
@@ -332,8 +436,6 @@ export type ProgrammaticToolCallingParams = {
332
436
  proxy?: string;
333
437
  /** Enable debug logging (or set PTC_DEBUG=true env var) */
334
438
  debug?: boolean;
335
- /** Environment variable key for API key */
336
- [key: string]: unknown;
337
439
  };
338
440
 
339
441
  // ============================================================================
@@ -0,0 +1,66 @@
1
+ import { describe, it, expect } from '@jest/globals';
2
+ import {
3
+ HARD_MAX_TOOL_RESULT_CHARS,
4
+ HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE,
5
+ calculateMaxToolResultChars,
6
+ calculateMaxTotalToolOutputSize,
7
+ } from '@/utils/truncation';
8
+
9
+ describe('truncation helpers', () => {
10
+ describe('calculateMaxToolResultChars', () => {
11
+ it('returns the hard cap when context tokens are missing', () => {
12
+ expect(calculateMaxToolResultChars()).toBe(HARD_MAX_TOOL_RESULT_CHARS);
13
+ expect(calculateMaxToolResultChars(undefined)).toBe(
14
+ HARD_MAX_TOOL_RESULT_CHARS
15
+ );
16
+ expect(calculateMaxToolResultChars(0)).toBe(HARD_MAX_TOOL_RESULT_CHARS);
17
+ expect(calculateMaxToolResultChars(-100)).toBe(
18
+ HARD_MAX_TOOL_RESULT_CHARS
19
+ );
20
+ });
21
+
22
+ it('computes 30% of context-window characters for normal inputs', () => {
23
+ // 100k tokens * 0.3 = 30k tokens * 4 chars/token = 120k chars
24
+ expect(calculateMaxToolResultChars(100_000)).toBe(120_000);
25
+ });
26
+
27
+ it('clamps to the hard cap for large context windows', () => {
28
+ // 1M tokens * 0.3 * 4 = 1.2M chars, exceeds 400k cap
29
+ expect(calculateMaxToolResultChars(1_000_000)).toBe(
30
+ HARD_MAX_TOOL_RESULT_CHARS
31
+ );
32
+ });
33
+ });
34
+
35
+ describe('calculateMaxTotalToolOutputSize', () => {
36
+ it('returns the absolute hard cap when no per-output is provided', () => {
37
+ expect(calculateMaxTotalToolOutputSize()).toBe(
38
+ HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE
39
+ );
40
+ expect(calculateMaxTotalToolOutputSize(0)).toBe(
41
+ HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE
42
+ );
43
+ expect(calculateMaxTotalToolOutputSize(-1)).toBe(
44
+ HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE
45
+ );
46
+ });
47
+
48
+ it('doubles the per-output cap by default', () => {
49
+ expect(calculateMaxTotalToolOutputSize(100_000)).toBe(200_000);
50
+ expect(calculateMaxTotalToolOutputSize(1)).toBe(2);
51
+ });
52
+
53
+ it('clamps the doubled value to HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE', () => {
54
+ // 4M * 2 = 8M, exceeds 5M
55
+ expect(calculateMaxTotalToolOutputSize(4_000_000)).toBe(
56
+ HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE
57
+ );
58
+ // Right at the boundary: 2.5M * 2 = 5M (no clamp).
59
+ expect(calculateMaxTotalToolOutputSize(2_500_000)).toBe(5_000_000);
60
+ // Just past it: 2_500_001 * 2 = 5_000_002 -> clamped.
61
+ expect(calculateMaxTotalToolOutputSize(2_500_001)).toBe(
62
+ HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE
63
+ );
64
+ });
65
+ });
66
+ });
@@ -12,6 +12,16 @@
12
12
  */
13
13
  export const HARD_MAX_TOOL_RESULT_CHARS = 400_000;
14
14
 
15
+ /**
16
+ * Absolute hard cap on the aggregate size (characters) of all registered
17
+ * tool outputs kept for `{{tool<i>turn<n>}}` substitution. Set at 5 MB
18
+ * because the registry stores *raw, untruncated* tool output — full
19
+ * fidelity for piping into downstream bash/jq — so the budget needs
20
+ * enough headroom to keep a handful of large responses without
21
+ * ballooning unbounded.
22
+ */
23
+ export const HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE = 5_000_000;
24
+
15
25
  /**
16
26
  * Computes the dynamic max tool result size based on the model's context window.
17
27
  * Uses 30% of the context window (in estimated characters, ~4 chars/token)
@@ -32,6 +42,26 @@ export function calculateMaxToolResultChars(
32
42
  );
33
43
  }
34
44
 
45
+ /**
46
+ * Computes the default aggregate size (characters) for the tool output
47
+ * reference registry based on the per-output budget. Mirrors
48
+ * `calculateMaxToolResultChars`'s shape: a multiple of the per-output
49
+ * cap, clamped to `HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE`.
50
+ *
51
+ * @param maxOutputSize - Per-output maximum characters (e.g., the
52
+ * ToolNode's `maxToolResultChars`). When omitted or non-positive,
53
+ * falls back to the absolute total cap.
54
+ * @returns Maximum total characters retained across the registry.
55
+ */
56
+ export function calculateMaxTotalToolOutputSize(
57
+ maxOutputSize?: number
58
+ ): number {
59
+ if (maxOutputSize == null || maxOutputSize <= 0) {
60
+ return HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE;
61
+ }
62
+ return Math.min(maxOutputSize * 2, HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE);
63
+ }
64
+
35
65
  /**
36
66
  * Truncates a tool-call input (the arguments/payload of a tool_use block)
37
67
  * using head+tail strategy. Returns an object with `_truncated` (the