@librechat/agents 3.1.77 → 3.1.78

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 (185) hide show
  1. package/dist/cjs/common/enum.cjs +54 -0
  2. package/dist/cjs/common/enum.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +155 -4
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +291 -0
  6. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -0
  7. package/dist/cjs/main.cjs +90 -0
  8. package/dist/cjs/main.cjs.map +1 -1
  9. package/dist/cjs/messages/anthropicToolCache.cjs +102 -0
  10. package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -0
  11. package/dist/cjs/messages/prune.cjs +27 -0
  12. package/dist/cjs/messages/prune.cjs.map +1 -1
  13. package/dist/cjs/messages/recency.cjs +99 -0
  14. package/dist/cjs/messages/recency.cjs.map +1 -0
  15. package/dist/cjs/run.cjs +30 -0
  16. package/dist/cjs/run.cjs.map +1 -1
  17. package/dist/cjs/summarization/node.cjs +100 -6
  18. package/dist/cjs/summarization/node.cjs.map +1 -1
  19. package/dist/cjs/tools/ToolNode.cjs +635 -23
  20. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  21. package/dist/cjs/tools/local/CompileCheckTool.cjs +227 -0
  22. package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -0
  23. package/dist/cjs/tools/local/FileCheckpointer.cjs +90 -0
  24. package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -0
  25. package/dist/cjs/tools/local/LocalCodingTools.cjs +1098 -0
  26. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -0
  27. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +1042 -0
  28. package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -0
  29. package/dist/cjs/tools/local/LocalExecutionTools.cjs +122 -0
  30. package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -0
  31. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +453 -0
  32. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -0
  33. package/dist/cjs/tools/local/attachments.cjs +183 -0
  34. package/dist/cjs/tools/local/attachments.cjs.map +1 -0
  35. package/dist/cjs/tools/local/bashAst.cjs +129 -0
  36. package/dist/cjs/tools/local/bashAst.cjs.map +1 -0
  37. package/dist/cjs/tools/local/editStrategies.cjs +188 -0
  38. package/dist/cjs/tools/local/editStrategies.cjs.map +1 -0
  39. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +141 -0
  40. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -0
  41. package/dist/cjs/tools/local/syntaxCheck.cjs +182 -0
  42. package/dist/cjs/tools/local/syntaxCheck.cjs.map +1 -0
  43. package/dist/cjs/tools/local/textEncoding.cjs +30 -0
  44. package/dist/cjs/tools/local/textEncoding.cjs.map +1 -0
  45. package/dist/cjs/tools/local/workspaceFS.cjs +51 -0
  46. package/dist/cjs/tools/local/workspaceFS.cjs.map +1 -0
  47. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +31 -0
  48. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  49. package/dist/esm/common/enum.mjs +53 -1
  50. package/dist/esm/common/enum.mjs.map +1 -1
  51. package/dist/esm/graphs/Graph.mjs +156 -5
  52. package/dist/esm/graphs/Graph.mjs.map +1 -1
  53. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +289 -0
  54. package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -0
  55. package/dist/esm/main.mjs +17 -2
  56. package/dist/esm/main.mjs.map +1 -1
  57. package/dist/esm/messages/anthropicToolCache.mjs +99 -0
  58. package/dist/esm/messages/anthropicToolCache.mjs.map +1 -0
  59. package/dist/esm/messages/prune.mjs +26 -1
  60. package/dist/esm/messages/prune.mjs.map +1 -1
  61. package/dist/esm/messages/recency.mjs +97 -0
  62. package/dist/esm/messages/recency.mjs.map +1 -0
  63. package/dist/esm/run.mjs +30 -0
  64. package/dist/esm/run.mjs.map +1 -1
  65. package/dist/esm/summarization/node.mjs +100 -6
  66. package/dist/esm/summarization/node.mjs.map +1 -1
  67. package/dist/esm/tools/ToolNode.mjs +635 -23
  68. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  69. package/dist/esm/tools/local/CompileCheckTool.mjs +223 -0
  70. package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -0
  71. package/dist/esm/tools/local/FileCheckpointer.mjs +87 -0
  72. package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -0
  73. package/dist/esm/tools/local/LocalCodingTools.mjs +1075 -0
  74. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -0
  75. package/dist/esm/tools/local/LocalExecutionEngine.mjs +1022 -0
  76. package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -0
  77. package/dist/esm/tools/local/LocalExecutionTools.mjs +117 -0
  78. package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -0
  79. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +448 -0
  80. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -0
  81. package/dist/esm/tools/local/attachments.mjs +180 -0
  82. package/dist/esm/tools/local/attachments.mjs.map +1 -0
  83. package/dist/esm/tools/local/bashAst.mjs +126 -0
  84. package/dist/esm/tools/local/bashAst.mjs.map +1 -0
  85. package/dist/esm/tools/local/editStrategies.mjs +185 -0
  86. package/dist/esm/tools/local/editStrategies.mjs.map +1 -0
  87. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +137 -0
  88. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -0
  89. package/dist/esm/tools/local/syntaxCheck.mjs +179 -0
  90. package/dist/esm/tools/local/syntaxCheck.mjs.map +1 -0
  91. package/dist/esm/tools/local/textEncoding.mjs +27 -0
  92. package/dist/esm/tools/local/textEncoding.mjs.map +1 -0
  93. package/dist/esm/tools/local/workspaceFS.mjs +49 -0
  94. package/dist/esm/tools/local/workspaceFS.mjs.map +1 -0
  95. package/dist/esm/tools/subagent/SubagentExecutor.mjs +31 -0
  96. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  97. package/dist/types/common/enum.d.ts +39 -1
  98. package/dist/types/graphs/Graph.d.ts +34 -0
  99. package/dist/types/hooks/createWorkspacePolicyHook.d.ts +95 -0
  100. package/dist/types/hooks/index.d.ts +2 -0
  101. package/dist/types/index.d.ts +1 -0
  102. package/dist/types/messages/anthropicToolCache.d.ts +51 -0
  103. package/dist/types/messages/index.d.ts +2 -0
  104. package/dist/types/messages/prune.d.ts +11 -0
  105. package/dist/types/messages/recency.d.ts +64 -0
  106. package/dist/types/run.d.ts +21 -0
  107. package/dist/types/tools/ToolNode.d.ts +145 -2
  108. package/dist/types/tools/local/CompileCheckTool.d.ts +31 -0
  109. package/dist/types/tools/local/FileCheckpointer.d.ts +39 -0
  110. package/dist/types/tools/local/LocalCodingTools.d.ts +57 -0
  111. package/dist/types/tools/local/LocalExecutionEngine.d.ts +149 -0
  112. package/dist/types/tools/local/LocalExecutionTools.d.ts +9 -0
  113. package/dist/types/tools/local/LocalProgrammaticToolCalling.d.ts +21 -0
  114. package/dist/types/tools/local/attachments.d.ts +84 -0
  115. package/dist/types/tools/local/bashAst.d.ts +11 -0
  116. package/dist/types/tools/local/editStrategies.d.ts +28 -0
  117. package/dist/types/tools/local/index.d.ts +12 -0
  118. package/dist/types/tools/local/resolveLocalExecutionTools.d.ts +38 -0
  119. package/dist/types/tools/local/syntaxCheck.d.ts +42 -0
  120. package/dist/types/tools/local/textEncoding.d.ts +21 -0
  121. package/dist/types/tools/local/workspaceFS.d.ts +49 -0
  122. package/dist/types/tools/subagent/SubagentExecutor.d.ts +29 -0
  123. package/dist/types/types/hitl.d.ts +56 -27
  124. package/dist/types/types/run.d.ts +8 -1
  125. package/dist/types/types/summarize.d.ts +30 -0
  126. package/dist/types/types/tools.d.ts +341 -6
  127. package/package.json +21 -2
  128. package/src/common/enum.ts +54 -0
  129. package/src/graphs/Graph.ts +173 -6
  130. package/src/hooks/__tests__/compactHooks.test.ts +38 -2
  131. package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +393 -0
  132. package/src/hooks/createWorkspacePolicyHook.ts +355 -0
  133. package/src/hooks/index.ts +6 -0
  134. package/src/index.ts +1 -0
  135. package/src/messages/__tests__/anthropicToolCache.test.ts +125 -0
  136. package/src/messages/__tests__/recency.test.ts +267 -0
  137. package/src/messages/anthropicToolCache.ts +116 -0
  138. package/src/messages/index.ts +2 -0
  139. package/src/messages/prune.ts +27 -1
  140. package/src/messages/recency.ts +155 -0
  141. package/src/run.ts +31 -0
  142. package/src/scripts/compare_pi_vs_ours.ts +840 -0
  143. package/src/scripts/local_engine.ts +166 -0
  144. package/src/scripts/local_engine_checkpointer.ts +205 -0
  145. package/src/scripts/local_engine_compile.ts +263 -0
  146. package/src/scripts/local_engine_hooks.ts +226 -0
  147. package/src/scripts/local_engine_image.ts +201 -0
  148. package/src/scripts/local_engine_ptc.ts +151 -0
  149. package/src/scripts/local_engine_workspace.ts +258 -0
  150. package/src/scripts/subagent-configurable-inheritance.ts +252 -0
  151. package/src/scripts/summarization-recency.ts +462 -0
  152. package/src/specs/prune.test.ts +39 -0
  153. package/src/summarization/__tests__/node.test.ts +499 -3
  154. package/src/summarization/node.ts +124 -7
  155. package/src/tools/ToolNode.ts +769 -20
  156. package/src/tools/__tests__/LocalExecutionTools.test.ts +2647 -0
  157. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +175 -0
  158. package/src/tools/__tests__/SubagentExecutor.test.ts +148 -0
  159. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +114 -0
  160. package/src/tools/__tests__/ToolNode.session.test.ts +84 -0
  161. package/src/tools/__tests__/directToolHITLResumeScope.test.ts +467 -0
  162. package/src/tools/__tests__/directToolHooks.test.ts +411 -0
  163. package/src/tools/__tests__/localToolNames.test.ts +73 -0
  164. package/src/tools/__tests__/workspaceSeam.test.ts +134 -0
  165. package/src/tools/local/CompileCheckTool.ts +278 -0
  166. package/src/tools/local/FileCheckpointer.ts +93 -0
  167. package/src/tools/local/LocalCodingTools.ts +1342 -0
  168. package/src/tools/local/LocalExecutionEngine.ts +1329 -0
  169. package/src/tools/local/LocalExecutionTools.ts +167 -0
  170. package/src/tools/local/LocalProgrammaticToolCalling.ts +594 -0
  171. package/src/tools/local/__tests__/FileCheckpointer.test.ts +120 -0
  172. package/src/tools/local/__tests__/editStrategies.test.ts +134 -0
  173. package/src/tools/local/attachments.ts +251 -0
  174. package/src/tools/local/bashAst.ts +151 -0
  175. package/src/tools/local/editStrategies.ts +188 -0
  176. package/src/tools/local/index.ts +12 -0
  177. package/src/tools/local/resolveLocalExecutionTools.ts +208 -0
  178. package/src/tools/local/syntaxCheck.ts +243 -0
  179. package/src/tools/local/textEncoding.ts +37 -0
  180. package/src/tools/local/workspaceFS.ts +89 -0
  181. package/src/tools/subagent/SubagentExecutor.ts +60 -0
  182. package/src/types/hitl.ts +56 -27
  183. package/src/types/run.ts +12 -1
  184. package/src/types/summarize.ts +31 -0
  185. package/src/types/tools.ts +359 -7
@@ -20,10 +20,13 @@ import {
20
20
  extractToolDiscoveries,
21
21
  addBedrockCacheControl,
22
22
  formatArtifactPayload,
23
+ enforceOriginalContentCap,
23
24
  formatContentStrings,
24
25
  createPruneMessages,
25
26
  addCacheControl,
26
27
  getMessageId,
28
+ makeIsDeferred,
29
+ partitionAndMarkAnthropicToolCache,
27
30
  } from '@/messages';
28
31
  import {
29
32
  GraphNodeKeys,
@@ -53,6 +56,8 @@ import { createSchemaOnlyTools } from '@/tools/schema';
53
56
  import { AgentContext } from '@/agents/AgentContext';
54
57
  import { createFakeStreamingLLM } from '@/llm/fake';
55
58
  import { handleToolCalls } from '@/tools/handlers';
59
+ import { resolveLocalToolsForBinding } from '@/tools/local';
60
+ import { createLocalCodingToolBundle } from '@/tools/local/LocalCodingTools';
56
61
  import { isThinkingEnabled } from '@/llm/request';
57
62
  import { initializeModel } from '@/llm/init';
58
63
  import { HandlerRegistry } from '@/events';
@@ -142,6 +147,11 @@ export abstract class Graph<
142
147
  * graph compiles.
143
148
  */
144
149
  toolOutputReferences: t.ToolOutputReferencesConfig | undefined;
150
+ /**
151
+ * Run-scoped execution backend for built-in code tools. Defaults to the
152
+ * remote Code API sandbox when unset.
153
+ */
154
+ toolExecution: t.ToolExecutionConfig | undefined;
145
155
  /**
146
156
  * Shared registry instance used by every ToolNode compiled from this
147
157
  * graph. Lazily constructed on first access so multi-agent graphs
@@ -176,6 +186,7 @@ export abstract class Graph<
176
186
  this.hookRegistry = undefined;
177
187
  this.humanInTheLoop = undefined;
178
188
  this.toolOutputReferences = undefined;
189
+ this.toolExecution = undefined;
179
190
  /**
180
191
  * ToolNodes compiled from this graph captured the registry
181
192
  * instance at construction time, so simply dropping the Graph's
@@ -186,9 +197,38 @@ export abstract class Graph<
186
197
  */
187
198
  this._toolOutputRegistry?.clear();
188
199
  this._toolOutputRegistry = undefined;
200
+ // NB: `_fileCheckpointer` is intentionally NOT cleared here.
201
+ // `Run.processStream()` calls `clearHeavyState()` in its
202
+ // finally block on natural-completion / error paths — exactly
203
+ // when the host is most likely to want `Run.rewindFiles()` (for
204
+ // rollback after a failed batch). Per-Run isolation is already
205
+ // automatic because each `Run.create()` constructs a brand-new
206
+ // Graph instance, so the next Run gets its own checkpointer
207
+ // without us needing to reset this field. Codex P1 #32: pre-fix
208
+ // the checkpointer was nulled before the caller could reach it.
209
+ // Flush each compiled ToolNode's direct-path turn cache so it
210
+ // doesn't leak across Runs (Codex P2 #33). The cache survives
211
+ // `run()` re-entry by design (resume-stable), but end-of-Run
212
+ // is the right point to reset it.
213
+ for (const node of this._compiledToolNodes) {
214
+ node.clearDirectPathTurns();
215
+ }
216
+ this._compiledToolNodes.clear();
189
217
  this.sessions.clear();
190
218
  }
191
219
 
220
+ /**
221
+ * Subclass hook to register a freshly compiled ToolNode so
222
+ * `clearHeavyState` can flush its per-Run direct-path turn cache
223
+ * at end-of-Run. Internal — called from `initializeTools` in the
224
+ * concrete graph subclasses.
225
+ */
226
+ protected registerCompiledToolNode(node: {
227
+ clearDirectPathTurns(): void;
228
+ }): void {
229
+ this._compiledToolNodes.add(node);
230
+ }
231
+
192
232
  /**
193
233
  * Returns the shared `ToolOutputReferenceRegistry` for this run,
194
234
  * constructing it on first access. Returns `undefined` when the
@@ -216,6 +256,56 @@ export abstract class Graph<
216
256
  }
217
257
  return this._toolOutputRegistry;
218
258
  }
259
+
260
+ /**
261
+ * Single per-Run file checkpointer shared across every ToolNode the
262
+ * graph compiles. Lazily constructed when
263
+ * `toolExecution.local.fileCheckpointing === true` so multi-agent
264
+ * graphs see ONE snapshot store, not one-per-agent. Returns
265
+ * undefined when checkpointing is disabled or the local engine
266
+ * isn't selected. Exposed via `Run.getFileCheckpointer()` /
267
+ * `Run.rewindFiles()`.
268
+ */
269
+ private _fileCheckpointer?: t.LocalFileCheckpointer;
270
+ /**
271
+ * ToolNodes compiled into this Graph's workflow. Tracked so
272
+ * `clearHeavyState()` can flush their per-Run direct-path turn
273
+ * cache (`directPathTurns`) at end-of-Run — that map intentionally
274
+ * survives `run()` re-entry (resume-stable per Codex P2 #30) but
275
+ * would otherwise grow linearly with tool calls and could collide
276
+ * across Runs if a provider reuses call ids (Codex P2 #33).
277
+ */
278
+ private _compiledToolNodes: Set<{
279
+ clearDirectPathTurns(): void;
280
+ }> = new Set();
281
+ public getOrCreateFileCheckpointer():
282
+ | t.LocalFileCheckpointer
283
+ | undefined {
284
+ // Return the cached instance unconditionally if one exists. The
285
+ // toolExecution check below decides whether to *create* a new
286
+ // one — `clearHeavyState` nulls `this.toolExecution` at end-of-
287
+ // Run, but we want post-Run `Run.rewindFiles()` to still resolve
288
+ // to the checkpointer that captured the writes. Codex P1 #32.
289
+ if (this._fileCheckpointer != null) {
290
+ return this._fileCheckpointer;
291
+ }
292
+ if (
293
+ this.toolExecution?.engine !== 'local' ||
294
+ this.toolExecution.local?.fileCheckpointing !== true
295
+ ) {
296
+ return undefined;
297
+ }
298
+ // Eagerly create via the bundle factory so the construction path
299
+ // matches the bundle-only callers (and future bundle-internal
300
+ // cleanup hooks fire). The bundle factory itself accepts a pre-
301
+ // supplied checkpointer when present, so re-injecting this one
302
+ // into every ToolNode is idempotent.
303
+ const bundle = createLocalCodingToolBundle(
304
+ this.toolExecution.local ?? {}
305
+ );
306
+ this._fileCheckpointer = bundle.checkpointer;
307
+ return this._fileCheckpointer;
308
+ }
219
309
  }
220
310
 
221
311
  export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
@@ -577,7 +667,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
577
667
  }
578
668
  }
579
669
 
580
- return new CustomToolNode<t.BaseGraphState>({
670
+ const node = new CustomToolNode<t.BaseGraphState>({
581
671
  tools: allTools,
582
672
  toolMap: allToolMap,
583
673
  eventDrivenMode: true,
@@ -588,13 +678,17 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
588
678
  toolRegistry: agentContext?.toolRegistry,
589
679
  hookRegistry: this.hookRegistry,
590
680
  humanInTheLoop: this.humanInTheLoop,
681
+ toolExecution: this.toolExecution,
591
682
  directToolNames: directToolNames.size > 0 ? directToolNames : undefined,
592
683
  maxContextTokens: agentContext?.maxContextTokens,
593
684
  maxToolResultChars: agentContext?.maxToolResultChars,
594
685
  toolOutputRegistry: this.getOrCreateToolOutputRegistry(),
595
- errorHandler: (data, metadata) =>
686
+ fileCheckpointer: this.getOrCreateFileCheckpointer(),
687
+ errorHandler: (data, metadata): Promise<void> =>
596
688
  StandardGraph.handleToolCallErrorStatic(this, data, metadata),
597
689
  });
690
+ this.registerCompiledToolNode(node);
691
+ return node;
598
692
  }
599
693
 
600
694
  const graphTools = agentContext?.graphTools as t.GenericTool[] | undefined;
@@ -613,18 +707,24 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
613
707
  ])
614
708
  : currentToolMap;
615
709
 
616
- return new CustomToolNode<t.BaseGraphState>({
710
+ const node = new CustomToolNode<t.BaseGraphState>({
617
711
  tools: allTraditionalTools,
618
712
  toolMap: traditionalToolMap,
619
713
  toolCallStepIds: this.toolCallStepIds,
620
- errorHandler: (data, metadata) =>
714
+ errorHandler: (data, metadata): Promise<void> =>
621
715
  StandardGraph.handleToolCallErrorStatic(this, data, metadata),
622
716
  toolRegistry: agentContext?.toolRegistry,
623
717
  sessions: this.sessions,
718
+ toolExecution: this.toolExecution,
719
+ hookRegistry: this.hookRegistry,
720
+ humanInTheLoop: this.humanInTheLoop,
624
721
  maxContextTokens: agentContext?.maxContextTokens,
625
722
  maxToolResultChars: agentContext?.maxToolResultChars,
626
723
  toolOutputRegistry: this.getOrCreateToolOutputRegistry(),
724
+ fileCheckpointer: this.getOrCreateFileCheckpointer(),
627
725
  });
726
+ this.registerCompiledToolNode(node);
727
+ return node;
628
728
  }
629
729
 
630
730
  overrideTestModel(
@@ -688,7 +788,37 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
688
788
  agentContext.markToolsAsDiscovered(discoveredNames);
689
789
  }
690
790
 
691
- const toolsForBinding = agentContext.getToolsForBinding();
791
+ const rawToolsForBinding = resolveLocalToolsForBinding({
792
+ tools: agentContext.getToolsForBinding(),
793
+ toolExecution: this.toolExecution,
794
+ });
795
+
796
+ /**
797
+ * Anthropic prompt-cache breakpoint on the tool definitions.
798
+ *
799
+ * Without this, the (often static) tool inventory shows up as
800
+ * fresh input on every turn — measured at ~28k tokens/turn for
801
+ * the local engine's coding-tool bundle, dominating per-turn
802
+ * cost even when message-level caching is on.
803
+ *
804
+ * Strategy: partition tools into [static, deferred] and stamp
805
+ * `cache_control: ephemeral` on the last static tool.
806
+ * Discovered deferred tools that arrive across turns sit *after*
807
+ * the breakpoint and don't invalidate the prefix.
808
+ */
809
+ let toolsForBinding = rawToolsForBinding;
810
+ if (
811
+ agentContext.provider === Providers.ANTHROPIC &&
812
+ (agentContext.clientOptions as t.AnthropicClientOptions | undefined)
813
+ ?.promptCache === true
814
+ ) {
815
+ toolsForBinding =
816
+ partitionAndMarkAnthropicToolCache(
817
+ rawToolsForBinding,
818
+ makeIsDeferred(agentContext.toolDefinitions)
819
+ ) ?? rawToolsForBinding;
820
+ }
821
+
692
822
  let model =
693
823
  this.overrideModel ??
694
824
  initializeModel({
@@ -804,7 +934,35 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
804
934
 
805
935
  if (triggerResult) {
806
936
  if (originalToolContent != null && originalToolContent.size > 0) {
807
- agentContext.pendingOriginalToolContent = originalToolContent;
937
+ /**
938
+ * Merge — never overwrite — the pruner's masking record
939
+ * into pendingOriginalToolContent. Carry-over entries
940
+ * from a prior summarize (preserved by the recency
941
+ * window for masked tool messages still in the tail) and
942
+ * the current pruner's new entries are both keyed by
943
+ * indices in the current `state.messages`, so a key-wise
944
+ * union is correct. Overwriting would discard the
945
+ * carry-over and reduce summary fidelity when those
946
+ * masked tail messages eventually move into the head.
947
+ */
948
+ if (agentContext.pendingOriginalToolContent == null) {
949
+ agentContext.pendingOriginalToolContent = originalToolContent;
950
+ } else {
951
+ for (const [idx, content] of originalToolContent) {
952
+ agentContext.pendingOriginalToolContent.set(idx, content);
953
+ }
954
+ /**
955
+ * Re-apply the per-store char cap after the union. The
956
+ * pruner enforces ORIGINAL_CONTENT_MAX_CHARS inside its
957
+ * own map via the onContentStored callback, but a
958
+ * key-wise merge with recency carry-over bypasses that
959
+ * accounting and could let the merged map grow without
960
+ * bound across long sessions.
961
+ */
962
+ enforceOriginalContentCap(
963
+ agentContext.pendingOriginalToolContent
964
+ );
965
+ }
808
966
  }
809
967
 
810
968
  emitAgentLog(
@@ -1313,6 +1471,15 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1313
1471
  subagentType,
1314
1472
  threadId,
1315
1473
  parentToolCallId,
1474
+ /**
1475
+ * Forward the parent's `configurable` so host-set fields
1476
+ * (`requestBody`, `user`, etc.) propagate into the child
1477
+ * workflow. The executor scrubs run-identity fields before
1478
+ * forwarding — see `SubagentExecuteParams.parentConfigurable`.
1479
+ */
1480
+ parentConfigurable: config.configurable as
1481
+ | Record<string, unknown>
1482
+ | undefined,
1316
1483
  });
1317
1484
  return result.content;
1318
1485
  }, buildSubagentToolParams(resolvedConfigs));
@@ -58,7 +58,14 @@ function buildConversation(): t.IState {
58
58
  async function createCompactingRun(
59
59
  tokenCounter: t.TokenCounter,
60
60
  hooks?: HookRegistry,
61
- runId = 'compact-run'
61
+ runId = 'compact-run',
62
+ /**
63
+ * Recency-window setting. Defaults to `{ turns: 0 }` for these tests so
64
+ * the post-compaction state is the legacy "remove-all only" shape, which
65
+ * the original assertions were written against. Tests that want to
66
+ * exercise the recency-window path should pass an explicit value.
67
+ */
68
+ retainRecent: { turns: number } = { turns: 0 }
62
69
  ): Promise<Run<t.IState>> {
63
70
  const conversation = buildConversation();
64
71
  const indexTokenCountMap: Record<string, number> = {};
@@ -79,6 +86,7 @@ async function createCompactingRun(
79
86
  summarizationEnabled: true,
80
87
  summarizationConfig: {
81
88
  provider: Providers.OPENAI,
89
+ retainRecent,
82
90
  },
83
91
  },
84
92
  returnContent: true,
@@ -138,7 +146,7 @@ describe('Compaction hook integration', () => {
138
146
  });
139
147
 
140
148
  describe('PostCompact', () => {
141
- it('fires with summary text after compaction', async () => {
149
+ it('fires with summary text after compaction (legacy retainRecent.turns=0 shape)', async () => {
142
150
  const registry = new HookRegistry();
143
151
  let captured: PostCompactHookInput | undefined;
144
152
  const hook: HookCallback<'PostCompact'> = async (
@@ -161,6 +169,34 @@ describe('Compaction hook integration', () => {
161
169
  expect(captured!.messagesAfterCount).toBe(0);
162
170
  expect(captured!.agentId).toBeDefined();
163
171
  });
172
+
173
+ it('reports the recency-tail length in messagesAfterCount when retainRecent.turns > 0', async () => {
174
+ const registry = new HookRegistry();
175
+ let captured: PostCompactHookInput | undefined;
176
+ const hook: HookCallback<'PostCompact'> = async (
177
+ input
178
+ ): Promise<PostCompactHookOutput> => {
179
+ captured = input;
180
+ return {};
181
+ };
182
+ registry.register('PostCompact', { hooks: [hook] });
183
+
184
+ const run = await createCompactingRun(
185
+ tokenCounter,
186
+ registry,
187
+ 'compact-recency-run',
188
+ { turns: 2 }
189
+ );
190
+ run.Graph!.overrideTestModel(['Final answer after compaction.']);
191
+ const inputs = buildConversation();
192
+ await run.processStream(inputs, callerConfig);
193
+
194
+ expect(captured).toBeDefined();
195
+ expect(captured!.hook_event_name).toBe('PostCompact');
196
+ // buildConversation produces 20 user-led turns of [user, ai]. With
197
+ // retainRecent.turns=2, the tail is the last 2 turns = 4 messages.
198
+ expect(captured!.messagesAfterCount).toBe(4);
199
+ });
164
200
  });
165
201
 
166
202
  describe('error resilience', () => {