@librechat/agents 3.4.7 → 3.5.1

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 (924) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +0 -356
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/agents/projection.cjs +0 -9
  4. package/dist/cjs/agents/projection.cjs.map +1 -1
  5. package/dist/cjs/common/constants.cjs +0 -26
  6. package/dist/cjs/common/constants.cjs.map +1 -1
  7. package/dist/cjs/common/enum.cjs +0 -85
  8. package/dist/cjs/common/enum.cjs.map +1 -1
  9. package/dist/cjs/events.cjs +0 -14
  10. package/dist/cjs/events.cjs.map +1 -1
  11. package/dist/cjs/graphs/Graph.cjs +1 -909
  12. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  13. package/dist/cjs/graphs/MultiAgentGraph.cjs +7 -272
  14. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  15. package/dist/cjs/graphs/applyGraphRuntimeConfig.cjs.map +1 -1
  16. package/dist/cjs/graphs/createGraph.cjs.map +1 -1
  17. package/dist/cjs/hitl/askUserQuestion.cjs +0 -53
  18. package/dist/cjs/hitl/askUserQuestion.cjs.map +1 -1
  19. package/dist/cjs/hitl/askUserQuestions.cjs +0 -20
  20. package/dist/cjs/hitl/askUserQuestions.cjs.map +1 -1
  21. package/dist/cjs/hitl/askUserQuestionsInterrupt.cjs +0 -6
  22. package/dist/cjs/hitl/askUserQuestionsInterrupt.cjs.map +1 -1
  23. package/dist/cjs/hooks/HookRegistry.cjs +0 -127
  24. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  25. package/dist/cjs/hooks/createToolPolicyHook.cjs +0 -38
  26. package/dist/cjs/hooks/createToolPolicyHook.cjs.map +1 -1
  27. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +0 -77
  28. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
  29. package/dist/cjs/hooks/executeHooks.cjs +0 -79
  30. package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
  31. package/dist/cjs/hooks/index.cjs +0 -16
  32. package/dist/cjs/hooks/index.cjs.map +1 -1
  33. package/dist/cjs/hooks/matchers.cjs +0 -104
  34. package/dist/cjs/hooks/matchers.cjs.map +1 -1
  35. package/dist/cjs/hooks/types.cjs +0 -8
  36. package/dist/cjs/hooks/types.cjs.map +1 -1
  37. package/dist/cjs/instrumentation.cjs +0 -14
  38. package/dist/cjs/instrumentation.cjs.map +1 -1
  39. package/dist/cjs/langfuse.cjs +0 -75
  40. package/dist/cjs/langfuse.cjs.map +1 -1
  41. package/dist/cjs/langfuseConfig.cjs.map +1 -1
  42. package/dist/cjs/langfuseRuntimeContext.cjs +0 -13
  43. package/dist/cjs/langfuseRuntimeContext.cjs.map +1 -1
  44. package/dist/cjs/langfuseRuntimeScope.cjs +0 -4
  45. package/dist/cjs/langfuseRuntimeScope.cjs.map +1 -1
  46. package/dist/cjs/langfuseSpanRegistry.cjs +0 -28
  47. package/dist/cjs/langfuseSpanRegistry.cjs.map +1 -1
  48. package/dist/cjs/langfuseToolOutputTracing.cjs +0 -3
  49. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  50. package/dist/cjs/langfuseTraceShaping.cjs +0 -74
  51. package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
  52. package/dist/cjs/llm/anthropic/index.cjs +0 -3
  53. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  54. package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
  55. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +1 -111
  56. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  57. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +0 -1
  58. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  59. package/dist/cjs/llm/anthropic/utils/stream_events.cjs +0 -8
  60. package/dist/cjs/llm/anthropic/utils/stream_events.cjs.map +1 -1
  61. package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -1
  62. package/dist/cjs/llm/bedrock/cachePoints.cjs.map +1 -1
  63. package/dist/cjs/llm/bedrock/index.cjs +1 -95
  64. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  65. package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
  66. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +0 -78
  67. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  68. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs +0 -24
  69. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs.map +1 -1
  70. package/dist/cjs/llm/contextOverflowRecovery.cjs +0 -62
  71. package/dist/cjs/llm/contextOverflowRecovery.cjs.map +1 -1
  72. package/dist/cjs/llm/fake.cjs.map +1 -1
  73. package/dist/cjs/llm/google/index.cjs +2 -21
  74. package/dist/cjs/llm/google/index.cjs.map +1 -1
  75. package/dist/cjs/llm/google/utils/common.cjs +0 -35
  76. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  77. package/dist/cjs/llm/init.cjs +0 -9
  78. package/dist/cjs/llm/init.cjs.map +1 -1
  79. package/dist/cjs/llm/invoke.cjs +1 -270
  80. package/dist/cjs/llm/invoke.cjs.map +1 -1
  81. package/dist/cjs/llm/mistral/index.cjs.map +1 -1
  82. package/dist/cjs/llm/openai/index.cjs +0 -99
  83. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  84. package/dist/cjs/llm/openai/streamMetadata.cjs +0 -21
  85. package/dist/cjs/llm/openai/streamMetadata.cjs.map +1 -1
  86. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  87. package/dist/cjs/llm/openrouter/index.cjs +0 -1
  88. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  89. package/dist/cjs/llm/openrouter/toolCache.cjs +0 -6
  90. package/dist/cjs/llm/openrouter/toolCache.cjs.map +1 -1
  91. package/dist/cjs/llm/preempt.cjs +0 -75
  92. package/dist/cjs/llm/preempt.cjs.map +1 -1
  93. package/dist/cjs/llm/providers.cjs.map +1 -1
  94. package/dist/cjs/llm/request.cjs +0 -10
  95. package/dist/cjs/llm/request.cjs.map +1 -1
  96. package/dist/cjs/llm/stream/chunkAdapters.cjs +0 -62
  97. package/dist/cjs/llm/stream/chunkAdapters.cjs.map +1 -1
  98. package/dist/cjs/llm/stream/smoother.cjs +0 -58
  99. package/dist/cjs/llm/stream/smoother.cjs.map +1 -1
  100. package/dist/cjs/llm/streamLimits.cjs +4 -293
  101. package/dist/cjs/llm/streamLimits.cjs.map +1 -1
  102. package/dist/cjs/llm/truncation.cjs +0 -36
  103. package/dist/cjs/llm/truncation.cjs.map +1 -1
  104. package/dist/cjs/llm/vertexai/index.cjs +0 -330
  105. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  106. package/dist/cjs/messages/alternation.cjs +0 -43
  107. package/dist/cjs/messages/alternation.cjs.map +1 -1
  108. package/dist/cjs/messages/anthropicToolCache.cjs +0 -45
  109. package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -1
  110. package/dist/cjs/messages/assistantPhase.cjs +0 -12
  111. package/dist/cjs/messages/assistantPhase.cjs.map +1 -1
  112. package/dist/cjs/messages/budget.cjs +0 -9
  113. package/dist/cjs/messages/budget.cjs.map +1 -1
  114. package/dist/cjs/messages/cache.cjs +0 -172
  115. package/dist/cjs/messages/cache.cjs.map +1 -1
  116. package/dist/cjs/messages/content.cjs +0 -9
  117. package/dist/cjs/messages/content.cjs.map +1 -1
  118. package/dist/cjs/messages/contextPruning.cjs +0 -15
  119. package/dist/cjs/messages/contextPruning.cjs.map +1 -1
  120. package/dist/cjs/messages/contextPruningSettings.cjs +0 -3
  121. package/dist/cjs/messages/contextPruningSettings.cjs.map +1 -1
  122. package/dist/cjs/messages/core.cjs +0 -48
  123. package/dist/cjs/messages/core.cjs.map +1 -1
  124. package/dist/cjs/messages/format.cjs +1 -345
  125. package/dist/cjs/messages/format.cjs.map +1 -1
  126. package/dist/cjs/messages/handoffCue.cjs +0 -36
  127. package/dist/cjs/messages/handoffCue.cjs.map +1 -1
  128. package/dist/cjs/messages/ids.cjs.map +1 -1
  129. package/dist/cjs/messages/injected.cjs +0 -33
  130. package/dist/cjs/messages/injected.cjs.map +1 -1
  131. package/dist/cjs/messages/langchain.cjs +0 -14
  132. package/dist/cjs/messages/langchain.cjs.map +1 -1
  133. package/dist/cjs/messages/prune.cjs +1 -184
  134. package/dist/cjs/messages/prune.cjs.map +1 -1
  135. package/dist/cjs/messages/recency.cjs +0 -32
  136. package/dist/cjs/messages/recency.cjs.map +1 -1
  137. package/dist/cjs/messages/reducer.cjs +0 -30
  138. package/dist/cjs/messages/reducer.cjs.map +1 -1
  139. package/dist/cjs/messages/tools.cjs +0 -14
  140. package/dist/cjs/messages/tools.cjs.map +1 -1
  141. package/dist/cjs/openai/index.cjs.map +1 -1
  142. package/dist/cjs/prompts/activityLabel.cjs +9 -79
  143. package/dist/cjs/prompts/activityLabel.cjs.map +1 -1
  144. package/dist/cjs/responses/index.cjs.map +1 -1
  145. package/dist/cjs/run.cjs +2 -538
  146. package/dist/cjs/run.cjs.map +1 -1
  147. package/dist/cjs/session/AgentSession.cjs +1 -17
  148. package/dist/cjs/session/AgentSession.cjs.map +1 -1
  149. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
  150. package/dist/cjs/session/handlers.cjs +0 -7
  151. package/dist/cjs/session/handlers.cjs.map +1 -1
  152. package/dist/cjs/session/ids.cjs.map +1 -1
  153. package/dist/cjs/session/messageSerialization.cjs +0 -4
  154. package/dist/cjs/session/messageSerialization.cjs.map +1 -1
  155. package/dist/cjs/stream.cjs +1 -93
  156. package/dist/cjs/stream.cjs.map +1 -1
  157. package/dist/cjs/summarization/index.cjs +0 -27
  158. package/dist/cjs/summarization/index.cjs.map +1 -1
  159. package/dist/cjs/summarization/node.cjs +0 -273
  160. package/dist/cjs/summarization/node.cjs.map +1 -1
  161. package/dist/cjs/tools/BashExecutor.cjs +0 -35
  162. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  163. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +0 -24
  164. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  165. package/dist/cjs/tools/Calculator.cjs.map +1 -1
  166. package/dist/cjs/tools/CodeExecutor.cjs +0 -13
  167. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  168. package/dist/cjs/tools/CodeSessionFileSummary.cjs.map +1 -1
  169. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +0 -101
  170. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  171. package/dist/cjs/tools/ReadFile.cjs.map +1 -1
  172. package/dist/cjs/tools/SkillTool.cjs +0 -5
  173. package/dist/cjs/tools/SkillTool.cjs.map +1 -1
  174. package/dist/cjs/tools/SubagentTool.cjs +0 -10
  175. package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
  176. package/dist/cjs/tools/ToolNode.cjs +6 -1038
  177. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  178. package/dist/cjs/tools/ToolSearch.cjs +0 -179
  179. package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
  180. package/dist/cjs/tools/cloudflare/CloudflareBridgeRuntime.cjs.map +1 -1
  181. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
  182. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +0 -39
  183. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -1
  184. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs.map +1 -1
  185. package/dist/cjs/tools/eagerEventExecution.cjs +0 -6
  186. package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -1
  187. package/dist/cjs/tools/handlers.cjs +1 -41
  188. package/dist/cjs/tools/handlers.cjs.map +1 -1
  189. package/dist/cjs/tools/intentArg.cjs +1 -132
  190. package/dist/cjs/tools/intentArg.cjs.map +1 -1
  191. package/dist/cjs/tools/local/CompileCheckTool.cjs +0 -25
  192. package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
  193. package/dist/cjs/tools/local/FileCheckpointer.cjs +0 -23
  194. package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -1
  195. package/dist/cjs/tools/local/LocalCodingTools.cjs +0 -80
  196. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
  197. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +0 -126
  198. package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -1
  199. package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -1
  200. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +0 -12
  201. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  202. package/dist/cjs/tools/local/attachments.cjs +0 -44
  203. package/dist/cjs/tools/local/attachments.cjs.map +1 -1
  204. package/dist/cjs/tools/local/bashAst.cjs +0 -16
  205. package/dist/cjs/tools/local/bashAst.cjs.map +1 -1
  206. package/dist/cjs/tools/local/editStrategies.cjs +0 -14
  207. package/dist/cjs/tools/local/editStrategies.cjs.map +1 -1
  208. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
  209. package/dist/cjs/tools/local/syntaxCheck.cjs +0 -31
  210. package/dist/cjs/tools/local/syntaxCheck.cjs.map +1 -1
  211. package/dist/cjs/tools/local/textEncoding.cjs +0 -11
  212. package/dist/cjs/tools/local/textEncoding.cjs.map +1 -1
  213. package/dist/cjs/tools/local/workspaceFS.cjs +0 -33
  214. package/dist/cjs/tools/local/workspaceFS.cjs.map +1 -1
  215. package/dist/cjs/tools/ptcTimeout.cjs.map +1 -1
  216. package/dist/cjs/tools/schema.cjs +0 -8
  217. package/dist/cjs/tools/schema.cjs.map +1 -1
  218. package/dist/cjs/tools/search/anthropic.cjs +0 -9
  219. package/dist/cjs/tools/search/anthropic.cjs.map +1 -1
  220. package/dist/cjs/tools/search/content.cjs.map +1 -1
  221. package/dist/cjs/tools/search/crw-scraper.cjs +0 -33
  222. package/dist/cjs/tools/search/crw-scraper.cjs.map +1 -1
  223. package/dist/cjs/tools/search/crw-search.cjs +0 -2
  224. package/dist/cjs/tools/search/crw-search.cjs.map +1 -1
  225. package/dist/cjs/tools/search/firecrawl.cjs +0 -25
  226. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  227. package/dist/cjs/tools/search/format.cjs +0 -30
  228. package/dist/cjs/tools/search/format.cjs.map +1 -1
  229. package/dist/cjs/tools/search/highlights.cjs +0 -16
  230. package/dist/cjs/tools/search/highlights.cjs.map +1 -1
  231. package/dist/cjs/tools/search/keenable-scraper.cjs +0 -8
  232. package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -1
  233. package/dist/cjs/tools/search/keenable-search.cjs +0 -7
  234. package/dist/cjs/tools/search/keenable-search.cjs.map +1 -1
  235. package/dist/cjs/tools/search/rerankers.cjs +0 -35
  236. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  237. package/dist/cjs/tools/search/schema.cjs +0 -1
  238. package/dist/cjs/tools/search/schema.cjs.map +1 -1
  239. package/dist/cjs/tools/search/search.cjs +1 -14
  240. package/dist/cjs/tools/search/search.cjs.map +1 -1
  241. package/dist/cjs/tools/search/serper-scraper.cjs +0 -46
  242. package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -1
  243. package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -1
  244. package/dist/cjs/tools/search/tavily-search.cjs.map +1 -1
  245. package/dist/cjs/tools/search/tool.cjs +0 -25
  246. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  247. package/dist/cjs/tools/search/utils.cjs +0 -8
  248. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  249. package/dist/cjs/tools/skillCatalog.cjs +0 -6
  250. package/dist/cjs/tools/skillCatalog.cjs.map +1 -1
  251. package/dist/cjs/tools/streamedToolCallSeals.cjs +0 -6
  252. package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -1
  253. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +0 -2
  254. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
  255. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +3 -213
  256. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  257. package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
  258. package/dist/cjs/tools/subagent/childGraphConfig.cjs +0 -4
  259. package/dist/cjs/tools/subagent/childGraphConfig.cjs.map +1 -1
  260. package/dist/cjs/tools/subagent/runtimeLimits.cjs.map +1 -1
  261. package/dist/cjs/tools/toolOutputReferences.cjs +1 -322
  262. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  263. package/dist/cjs/types/hitl.cjs +0 -5
  264. package/dist/cjs/types/hitl.cjs.map +1 -1
  265. package/dist/cjs/utils/callbacks.cjs.map +1 -1
  266. package/dist/cjs/utils/errors.cjs +14 -165
  267. package/dist/cjs/utils/errors.cjs.map +1 -1
  268. package/dist/cjs/utils/events.cjs +0 -10
  269. package/dist/cjs/utils/events.cjs.map +1 -1
  270. package/dist/cjs/utils/graph.cjs.map +1 -1
  271. package/dist/cjs/utils/handlers.cjs +0 -3
  272. package/dist/cjs/utils/handlers.cjs.map +1 -1
  273. package/dist/cjs/utils/llm.cjs +0 -1
  274. package/dist/cjs/utils/llm.cjs.map +1 -1
  275. package/dist/cjs/utils/misc.cjs +0 -16
  276. package/dist/cjs/utils/misc.cjs.map +1 -1
  277. package/dist/cjs/utils/proxy.cjs +0 -26
  278. package/dist/cjs/utils/proxy.cjs.map +1 -1
  279. package/dist/cjs/utils/run.cjs +0 -6
  280. package/dist/cjs/utils/run.cjs.map +1 -1
  281. package/dist/cjs/utils/schema.cjs +0 -5
  282. package/dist/cjs/utils/schema.cjs.map +1 -1
  283. package/dist/cjs/utils/title.cjs +0 -6
  284. package/dist/cjs/utils/title.cjs.map +1 -1
  285. package/dist/cjs/utils/tokens.cjs +0 -113
  286. package/dist/cjs/utils/tokens.cjs.map +1 -1
  287. package/dist/cjs/utils/toolContent.cjs +0 -41
  288. package/dist/cjs/utils/toolContent.cjs.map +1 -1
  289. package/dist/cjs/utils/truncation.cjs +0 -61
  290. package/dist/cjs/utils/truncation.cjs.map +1 -1
  291. package/dist/esm/agents/AgentContext.mjs +0 -356
  292. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  293. package/dist/esm/agents/projection.mjs +0 -9
  294. package/dist/esm/agents/projection.mjs.map +1 -1
  295. package/dist/esm/common/constants.mjs +0 -26
  296. package/dist/esm/common/constants.mjs.map +1 -1
  297. package/dist/esm/common/enum.mjs +0 -85
  298. package/dist/esm/common/enum.mjs.map +1 -1
  299. package/dist/esm/events.mjs +0 -14
  300. package/dist/esm/events.mjs.map +1 -1
  301. package/dist/esm/graphs/Graph.mjs +1 -909
  302. package/dist/esm/graphs/Graph.mjs.map +1 -1
  303. package/dist/esm/graphs/MultiAgentGraph.mjs +7 -272
  304. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  305. package/dist/esm/graphs/applyGraphRuntimeConfig.mjs.map +1 -1
  306. package/dist/esm/graphs/createGraph.mjs.map +1 -1
  307. package/dist/esm/hitl/askUserQuestion.mjs +0 -53
  308. package/dist/esm/hitl/askUserQuestion.mjs.map +1 -1
  309. package/dist/esm/hitl/askUserQuestions.mjs +0 -20
  310. package/dist/esm/hitl/askUserQuestions.mjs.map +1 -1
  311. package/dist/esm/hitl/askUserQuestionsInterrupt.mjs +0 -6
  312. package/dist/esm/hitl/askUserQuestionsInterrupt.mjs.map +1 -1
  313. package/dist/esm/hooks/HookRegistry.mjs +0 -127
  314. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  315. package/dist/esm/hooks/createToolPolicyHook.mjs +0 -38
  316. package/dist/esm/hooks/createToolPolicyHook.mjs.map +1 -1
  317. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +0 -77
  318. package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
  319. package/dist/esm/hooks/executeHooks.mjs +0 -79
  320. package/dist/esm/hooks/executeHooks.mjs.map +1 -1
  321. package/dist/esm/hooks/index.mjs +0 -16
  322. package/dist/esm/hooks/index.mjs.map +1 -1
  323. package/dist/esm/hooks/matchers.mjs +0 -104
  324. package/dist/esm/hooks/matchers.mjs.map +1 -1
  325. package/dist/esm/hooks/types.mjs +0 -8
  326. package/dist/esm/hooks/types.mjs.map +1 -1
  327. package/dist/esm/instrumentation.mjs +0 -14
  328. package/dist/esm/instrumentation.mjs.map +1 -1
  329. package/dist/esm/langfuse.mjs +0 -75
  330. package/dist/esm/langfuse.mjs.map +1 -1
  331. package/dist/esm/langfuseConfig.mjs.map +1 -1
  332. package/dist/esm/langfuseRuntimeContext.mjs +0 -13
  333. package/dist/esm/langfuseRuntimeContext.mjs.map +1 -1
  334. package/dist/esm/langfuseRuntimeScope.mjs +0 -4
  335. package/dist/esm/langfuseRuntimeScope.mjs.map +1 -1
  336. package/dist/esm/langfuseSpanRegistry.mjs +0 -28
  337. package/dist/esm/langfuseSpanRegistry.mjs.map +1 -1
  338. package/dist/esm/langfuseToolOutputTracing.mjs +0 -3
  339. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  340. package/dist/esm/langfuseTraceShaping.mjs +0 -74
  341. package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
  342. package/dist/esm/llm/anthropic/index.mjs +0 -3
  343. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  344. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  345. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +1 -111
  346. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  347. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +0 -1
  348. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  349. package/dist/esm/llm/anthropic/utils/stream_events.mjs +0 -8
  350. package/dist/esm/llm/anthropic/utils/stream_events.mjs.map +1 -1
  351. package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -1
  352. package/dist/esm/llm/bedrock/cachePoints.mjs.map +1 -1
  353. package/dist/esm/llm/bedrock/index.mjs +1 -95
  354. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  355. package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
  356. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +0 -78
  357. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  358. package/dist/esm/llm/bedrock/utils/message_outputs.mjs +0 -24
  359. package/dist/esm/llm/bedrock/utils/message_outputs.mjs.map +1 -1
  360. package/dist/esm/llm/contextOverflowRecovery.mjs +0 -62
  361. package/dist/esm/llm/contextOverflowRecovery.mjs.map +1 -1
  362. package/dist/esm/llm/fake.mjs.map +1 -1
  363. package/dist/esm/llm/google/index.mjs +2 -21
  364. package/dist/esm/llm/google/index.mjs.map +1 -1
  365. package/dist/esm/llm/google/utils/common.mjs +0 -35
  366. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  367. package/dist/esm/llm/init.mjs +0 -9
  368. package/dist/esm/llm/init.mjs.map +1 -1
  369. package/dist/esm/llm/invoke.mjs +1 -270
  370. package/dist/esm/llm/invoke.mjs.map +1 -1
  371. package/dist/esm/llm/mistral/index.mjs.map +1 -1
  372. package/dist/esm/llm/openai/index.mjs +0 -99
  373. package/dist/esm/llm/openai/index.mjs.map +1 -1
  374. package/dist/esm/llm/openai/streamMetadata.mjs +0 -21
  375. package/dist/esm/llm/openai/streamMetadata.mjs.map +1 -1
  376. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  377. package/dist/esm/llm/openrouter/index.mjs +0 -1
  378. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  379. package/dist/esm/llm/openrouter/toolCache.mjs +0 -6
  380. package/dist/esm/llm/openrouter/toolCache.mjs.map +1 -1
  381. package/dist/esm/llm/preempt.mjs +0 -75
  382. package/dist/esm/llm/preempt.mjs.map +1 -1
  383. package/dist/esm/llm/providers.mjs.map +1 -1
  384. package/dist/esm/llm/request.mjs +0 -10
  385. package/dist/esm/llm/request.mjs.map +1 -1
  386. package/dist/esm/llm/stream/chunkAdapters.mjs +0 -62
  387. package/dist/esm/llm/stream/chunkAdapters.mjs.map +1 -1
  388. package/dist/esm/llm/stream/smoother.mjs +0 -58
  389. package/dist/esm/llm/stream/smoother.mjs.map +1 -1
  390. package/dist/esm/llm/streamLimits.mjs +4 -293
  391. package/dist/esm/llm/streamLimits.mjs.map +1 -1
  392. package/dist/esm/llm/truncation.mjs +0 -36
  393. package/dist/esm/llm/truncation.mjs.map +1 -1
  394. package/dist/esm/llm/vertexai/index.mjs +0 -330
  395. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  396. package/dist/esm/messages/alternation.mjs +0 -43
  397. package/dist/esm/messages/alternation.mjs.map +1 -1
  398. package/dist/esm/messages/anthropicToolCache.mjs +0 -45
  399. package/dist/esm/messages/anthropicToolCache.mjs.map +1 -1
  400. package/dist/esm/messages/assistantPhase.mjs +0 -12
  401. package/dist/esm/messages/assistantPhase.mjs.map +1 -1
  402. package/dist/esm/messages/budget.mjs +0 -9
  403. package/dist/esm/messages/budget.mjs.map +1 -1
  404. package/dist/esm/messages/cache.mjs +0 -172
  405. package/dist/esm/messages/cache.mjs.map +1 -1
  406. package/dist/esm/messages/content.mjs +0 -9
  407. package/dist/esm/messages/content.mjs.map +1 -1
  408. package/dist/esm/messages/contextPruning.mjs +0 -15
  409. package/dist/esm/messages/contextPruning.mjs.map +1 -1
  410. package/dist/esm/messages/contextPruningSettings.mjs +0 -3
  411. package/dist/esm/messages/contextPruningSettings.mjs.map +1 -1
  412. package/dist/esm/messages/core.mjs +0 -48
  413. package/dist/esm/messages/core.mjs.map +1 -1
  414. package/dist/esm/messages/format.mjs +1 -345
  415. package/dist/esm/messages/format.mjs.map +1 -1
  416. package/dist/esm/messages/handoffCue.mjs +0 -36
  417. package/dist/esm/messages/handoffCue.mjs.map +1 -1
  418. package/dist/esm/messages/ids.mjs.map +1 -1
  419. package/dist/esm/messages/injected.mjs +0 -33
  420. package/dist/esm/messages/injected.mjs.map +1 -1
  421. package/dist/esm/messages/langchain.mjs +0 -14
  422. package/dist/esm/messages/langchain.mjs.map +1 -1
  423. package/dist/esm/messages/prune.mjs +1 -184
  424. package/dist/esm/messages/prune.mjs.map +1 -1
  425. package/dist/esm/messages/recency.mjs +0 -32
  426. package/dist/esm/messages/recency.mjs.map +1 -1
  427. package/dist/esm/messages/reducer.mjs +0 -30
  428. package/dist/esm/messages/reducer.mjs.map +1 -1
  429. package/dist/esm/messages/tools.mjs +0 -14
  430. package/dist/esm/messages/tools.mjs.map +1 -1
  431. package/dist/esm/openai/index.mjs.map +1 -1
  432. package/dist/esm/prompts/activityLabel.mjs +9 -79
  433. package/dist/esm/prompts/activityLabel.mjs.map +1 -1
  434. package/dist/esm/responses/index.mjs.map +1 -1
  435. package/dist/esm/run.mjs +2 -538
  436. package/dist/esm/run.mjs.map +1 -1
  437. package/dist/esm/session/AgentSession.mjs +1 -17
  438. package/dist/esm/session/AgentSession.mjs.map +1 -1
  439. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
  440. package/dist/esm/session/handlers.mjs +0 -7
  441. package/dist/esm/session/handlers.mjs.map +1 -1
  442. package/dist/esm/session/ids.mjs.map +1 -1
  443. package/dist/esm/session/messageSerialization.mjs +0 -4
  444. package/dist/esm/session/messageSerialization.mjs.map +1 -1
  445. package/dist/esm/stream.mjs +1 -93
  446. package/dist/esm/stream.mjs.map +1 -1
  447. package/dist/esm/summarization/index.mjs +0 -27
  448. package/dist/esm/summarization/index.mjs.map +1 -1
  449. package/dist/esm/summarization/node.mjs +0 -273
  450. package/dist/esm/summarization/node.mjs.map +1 -1
  451. package/dist/esm/tools/BashExecutor.mjs +0 -35
  452. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  453. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +0 -24
  454. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  455. package/dist/esm/tools/Calculator.mjs.map +1 -1
  456. package/dist/esm/tools/CodeExecutor.mjs +0 -13
  457. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  458. package/dist/esm/tools/CodeSessionFileSummary.mjs.map +1 -1
  459. package/dist/esm/tools/ProgrammaticToolCalling.mjs +0 -101
  460. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  461. package/dist/esm/tools/ReadFile.mjs.map +1 -1
  462. package/dist/esm/tools/SkillTool.mjs +0 -5
  463. package/dist/esm/tools/SkillTool.mjs.map +1 -1
  464. package/dist/esm/tools/SubagentTool.mjs +0 -10
  465. package/dist/esm/tools/SubagentTool.mjs.map +1 -1
  466. package/dist/esm/tools/ToolNode.mjs +6 -1038
  467. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  468. package/dist/esm/tools/ToolSearch.mjs +0 -179
  469. package/dist/esm/tools/ToolSearch.mjs.map +1 -1
  470. package/dist/esm/tools/cloudflare/CloudflareBridgeRuntime.mjs.map +1 -1
  471. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
  472. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +0 -39
  473. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -1
  474. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs.map +1 -1
  475. package/dist/esm/tools/eagerEventExecution.mjs +0 -6
  476. package/dist/esm/tools/eagerEventExecution.mjs.map +1 -1
  477. package/dist/esm/tools/handlers.mjs +1 -41
  478. package/dist/esm/tools/handlers.mjs.map +1 -1
  479. package/dist/esm/tools/intentArg.mjs +1 -132
  480. package/dist/esm/tools/intentArg.mjs.map +1 -1
  481. package/dist/esm/tools/local/CompileCheckTool.mjs +0 -25
  482. package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
  483. package/dist/esm/tools/local/FileCheckpointer.mjs +0 -23
  484. package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -1
  485. package/dist/esm/tools/local/LocalCodingTools.mjs +0 -80
  486. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
  487. package/dist/esm/tools/local/LocalExecutionEngine.mjs +0 -126
  488. package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -1
  489. package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -1
  490. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +0 -12
  491. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  492. package/dist/esm/tools/local/attachments.mjs +0 -44
  493. package/dist/esm/tools/local/attachments.mjs.map +1 -1
  494. package/dist/esm/tools/local/bashAst.mjs +0 -16
  495. package/dist/esm/tools/local/bashAst.mjs.map +1 -1
  496. package/dist/esm/tools/local/editStrategies.mjs +0 -14
  497. package/dist/esm/tools/local/editStrategies.mjs.map +1 -1
  498. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
  499. package/dist/esm/tools/local/syntaxCheck.mjs +0 -31
  500. package/dist/esm/tools/local/syntaxCheck.mjs.map +1 -1
  501. package/dist/esm/tools/local/textEncoding.mjs +0 -11
  502. package/dist/esm/tools/local/textEncoding.mjs.map +1 -1
  503. package/dist/esm/tools/local/workspaceFS.mjs +0 -33
  504. package/dist/esm/tools/local/workspaceFS.mjs.map +1 -1
  505. package/dist/esm/tools/ptcTimeout.mjs.map +1 -1
  506. package/dist/esm/tools/schema.mjs +0 -8
  507. package/dist/esm/tools/schema.mjs.map +1 -1
  508. package/dist/esm/tools/search/anthropic.mjs +0 -9
  509. package/dist/esm/tools/search/anthropic.mjs.map +1 -1
  510. package/dist/esm/tools/search/content.mjs.map +1 -1
  511. package/dist/esm/tools/search/crw-scraper.mjs +0 -33
  512. package/dist/esm/tools/search/crw-scraper.mjs.map +1 -1
  513. package/dist/esm/tools/search/crw-search.mjs +0 -2
  514. package/dist/esm/tools/search/crw-search.mjs.map +1 -1
  515. package/dist/esm/tools/search/firecrawl.mjs +0 -25
  516. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  517. package/dist/esm/tools/search/format.mjs +0 -30
  518. package/dist/esm/tools/search/format.mjs.map +1 -1
  519. package/dist/esm/tools/search/highlights.mjs +0 -16
  520. package/dist/esm/tools/search/highlights.mjs.map +1 -1
  521. package/dist/esm/tools/search/keenable-scraper.mjs +0 -8
  522. package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -1
  523. package/dist/esm/tools/search/keenable-search.mjs +0 -7
  524. package/dist/esm/tools/search/keenable-search.mjs.map +1 -1
  525. package/dist/esm/tools/search/rerankers.mjs +0 -35
  526. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  527. package/dist/esm/tools/search/schema.mjs +0 -1
  528. package/dist/esm/tools/search/schema.mjs.map +1 -1
  529. package/dist/esm/tools/search/search.mjs +1 -14
  530. package/dist/esm/tools/search/search.mjs.map +1 -1
  531. package/dist/esm/tools/search/serper-scraper.mjs +0 -46
  532. package/dist/esm/tools/search/serper-scraper.mjs.map +1 -1
  533. package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -1
  534. package/dist/esm/tools/search/tavily-search.mjs.map +1 -1
  535. package/dist/esm/tools/search/tool.mjs +0 -25
  536. package/dist/esm/tools/search/tool.mjs.map +1 -1
  537. package/dist/esm/tools/search/utils.mjs +0 -8
  538. package/dist/esm/tools/search/utils.mjs.map +1 -1
  539. package/dist/esm/tools/skillCatalog.mjs +0 -6
  540. package/dist/esm/tools/skillCatalog.mjs.map +1 -1
  541. package/dist/esm/tools/streamedToolCallSeals.mjs +0 -6
  542. package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -1
  543. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +0 -2
  544. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
  545. package/dist/esm/tools/subagent/SubagentExecutor.mjs +3 -213
  546. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  547. package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
  548. package/dist/esm/tools/subagent/childGraphConfig.mjs +0 -4
  549. package/dist/esm/tools/subagent/childGraphConfig.mjs.map +1 -1
  550. package/dist/esm/tools/subagent/runtimeLimits.mjs.map +1 -1
  551. package/dist/esm/tools/toolOutputReferences.mjs +1 -322
  552. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  553. package/dist/esm/types/hitl.mjs +0 -5
  554. package/dist/esm/types/hitl.mjs.map +1 -1
  555. package/dist/esm/utils/callbacks.mjs.map +1 -1
  556. package/dist/esm/utils/errors.mjs +14 -165
  557. package/dist/esm/utils/errors.mjs.map +1 -1
  558. package/dist/esm/utils/events.mjs +0 -10
  559. package/dist/esm/utils/events.mjs.map +1 -1
  560. package/dist/esm/utils/graph.mjs.map +1 -1
  561. package/dist/esm/utils/handlers.mjs +0 -3
  562. package/dist/esm/utils/handlers.mjs.map +1 -1
  563. package/dist/esm/utils/llm.mjs +0 -1
  564. package/dist/esm/utils/llm.mjs.map +1 -1
  565. package/dist/esm/utils/misc.mjs +0 -16
  566. package/dist/esm/utils/misc.mjs.map +1 -1
  567. package/dist/esm/utils/proxy.mjs +0 -26
  568. package/dist/esm/utils/proxy.mjs.map +1 -1
  569. package/dist/esm/utils/run.mjs +0 -6
  570. package/dist/esm/utils/run.mjs.map +1 -1
  571. package/dist/esm/utils/schema.mjs +0 -5
  572. package/dist/esm/utils/schema.mjs.map +1 -1
  573. package/dist/esm/utils/title.mjs +0 -6
  574. package/dist/esm/utils/title.mjs.map +1 -1
  575. package/dist/esm/utils/tokens.mjs +0 -113
  576. package/dist/esm/utils/tokens.mjs.map +1 -1
  577. package/dist/esm/utils/toolContent.mjs +0 -41
  578. package/dist/esm/utils/toolContent.mjs.map +1 -1
  579. package/dist/esm/utils/truncation.mjs +0 -61
  580. package/dist/esm/utils/truncation.mjs.map +1 -1
  581. package/package.json +35 -32
  582. package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +0 -52
  583. package/dist/types/test/mockTools.d.ts +0 -28
  584. package/dist/types/utils/__tests__/fixtures/contextOverflowSignatures.d.ts +0 -40
  585. package/src/__tests__/stream.eagerArgsDivergence.test.ts +0 -911
  586. package/src/__tests__/stream.eagerEventExecution.test.ts +0 -5046
  587. package/src/__tests__/stream.streamLimits.test.ts +0 -1982
  588. package/src/agents/__tests__/AgentContext.anthropic.live.test.ts +0 -448
  589. package/src/agents/__tests__/AgentContext.bedrock.live.test.ts +0 -655
  590. package/src/agents/__tests__/AgentContext.openrouter.live.test.ts +0 -128
  591. package/src/agents/__tests__/AgentContext.overflow.test.ts +0 -205
  592. package/src/agents/__tests__/AgentContext.test.ts +0 -2425
  593. package/src/agents/__tests__/projection.test.ts +0 -73
  594. package/src/agents/__tests__/promptCacheLiveHelpers.ts +0 -175
  595. package/src/aggregator.test.ts +0 -1214
  596. package/src/graphs/__tests__/Graph.breakerLifecycle.test.ts +0 -273
  597. package/src/graphs/__tests__/Graph.closeRunStep.test.ts +0 -385
  598. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +0 -1766
  599. package/src/graphs/__tests__/Graph.preemptSignal.test.ts +0 -126
  600. package/src/graphs/__tests__/Graph.reasoning.test.ts +0 -1717
  601. package/src/graphs/__tests__/Graph.subagentResumeState.test.ts +0 -80
  602. package/src/graphs/__tests__/MultiAgentGraph.test.ts +0 -177
  603. package/src/graphs/__tests__/composition.smoke.test.ts +0 -509
  604. package/src/graphs/__tests__/createGraph.test.ts +0 -206
  605. package/src/hooks/__tests__/HookRegistry.test.ts +0 -334
  606. package/src/hooks/__tests__/compactHooks.test.ts +0 -250
  607. package/src/hooks/__tests__/createToolPolicyHook.test.ts +0 -268
  608. package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +0 -405
  609. package/src/hooks/__tests__/executeHooks.test.ts +0 -1294
  610. package/src/hooks/__tests__/integration.test.ts +0 -337
  611. package/src/hooks/__tests__/matchers.test.ts +0 -238
  612. package/src/hooks/__tests__/preemptBoundary.test.ts +0 -152
  613. package/src/hooks/__tests__/toolHooks.test.ts +0 -669
  614. package/src/llm/__tests__/contextOverflowRecovery.test.ts +0 -401
  615. package/src/llm/__tests__/fallbackOverflow.test.ts +0 -287
  616. package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
  617. package/src/llm/anthropic/inherited-content-utils.spec.ts +0 -249
  618. package/src/llm/anthropic/inherited-stream-events.spec.ts +0 -1259
  619. package/src/llm/anthropic/inherited-strict.spec.ts +0 -302
  620. package/src/llm/anthropic/llm.spec.ts +0 -3394
  621. package/src/llm/anthropic/utils/cross-provider-reasoning.test.ts +0 -383
  622. package/src/llm/anthropic/utils/cross-provider-server-tools.test.ts +0 -110
  623. package/src/llm/anthropic/utils/server-tool-inputs.test.ts +0 -508
  624. package/src/llm/anthropic/utils/streaming-tool-input.test.ts +0 -361
  625. package/src/llm/anthropic/utils/stripPrefillCache.test.ts +0 -111
  626. package/src/llm/anthropic/utils/tool-id-normalization.test.ts +0 -287
  627. package/src/llm/bedrock/inherited-cache.spec.ts +0 -144
  628. package/src/llm/bedrock/inherited.spec.ts +0 -724
  629. package/src/llm/bedrock/llm.spec.ts +0 -1533
  630. package/src/llm/bedrock/streamSealDispatch.test.ts +0 -331
  631. package/src/llm/bedrock/toolCache.test.ts +0 -194
  632. package/src/llm/bedrock/utils/cross-provider-reasoning.test.ts +0 -131
  633. package/src/llm/bedrock/utils/cross-provider-server-tools.test.ts +0 -122
  634. package/src/llm/bedrock/utils/message_inputs.test.ts +0 -650
  635. package/src/llm/bedrock/utils/message_outputs.test.ts +0 -85
  636. package/src/llm/bedrock/utils/toolResultCachePoint.test.ts +0 -118
  637. package/src/llm/custom-chat-models.smoke.test.ts +0 -1966
  638. package/src/llm/google/data/gettysburg10.wav +0 -0
  639. package/src/llm/google/data/hotdog.jpg +0 -0
  640. package/src/llm/google/inherited-stream-events.spec.ts +0 -350
  641. package/src/llm/google/llm.spec.ts +0 -1388
  642. package/src/llm/google/streamSmoothing.test.ts +0 -121
  643. package/src/llm/google/utils/common.test.ts +0 -196
  644. package/src/llm/invoke.alternation.test.ts +0 -87
  645. package/src/llm/invoke.handoffCue.test.ts +0 -112
  646. package/src/llm/invoke.streamLimits.test.ts +0 -142
  647. package/src/llm/invoke.test.ts +0 -1198
  648. package/src/llm/mistral/streamSmoothing.test.ts +0 -97
  649. package/src/llm/openai/cacheWriteTokens.test.ts +0 -112
  650. package/src/llm/openai/contentBlocks.test.ts +0 -381
  651. package/src/llm/openai/deepseek.test.ts +0 -747
  652. package/src/llm/openai/inherited-deepseek.spec.ts +0 -347
  653. package/src/llm/openai/inherited-xai.spec.ts +0 -416
  654. package/src/llm/openai/llm.spec.ts +0 -1774
  655. package/src/llm/openai/managedRequests.test.ts +0 -182
  656. package/src/llm/openai/sequentialToolCallSeals.test.ts +0 -199
  657. package/src/llm/openai/streamMetadata.spec.ts +0 -86
  658. package/src/llm/openai/streamMetadataDedup.spec.ts +0 -166
  659. package/src/llm/openai/utils/isReasoningModel.test.ts +0 -90
  660. package/src/llm/openai/utils/messages.test.ts +0 -488
  661. package/src/llm/openrouter/reasoning.test.ts +0 -561
  662. package/src/llm/openrouter/toolCache.test.ts +0 -134
  663. package/src/llm/preempt.test.ts +0 -323
  664. package/src/llm/stream/chunkAdapters.test.ts +0 -202
  665. package/src/llm/stream/reassembly.test.ts +0 -241
  666. package/src/llm/stream/smoother.bench.test.ts +0 -155
  667. package/src/llm/stream/smoother.test.ts +0 -519
  668. package/src/llm/streamLimits.test.ts +0 -450
  669. package/src/llm/truncation.test.ts +0 -242
  670. package/src/llm/vertexai/fixThoughtSignatures.test.ts +0 -154
  671. package/src/llm/vertexai/inherited-stream-events.spec.ts +0 -271
  672. package/src/llm/vertexai/llm.spec.ts +0 -159
  673. package/src/llm/vertexai/repairUsageMetadata.test.ts +0 -54
  674. package/src/llm/vertexai/sealStreamedToolCalls.test.ts +0 -88
  675. package/src/llm/vertexai/streamSealDispatch.test.ts +0 -148
  676. package/src/llm/vertexai/streamSmoothing.test.ts +0 -109
  677. package/src/messages/__tests__/anthropicToolCache.test.ts +0 -339
  678. package/src/messages/__tests__/observationMasking.test.ts +0 -312
  679. package/src/messages/__tests__/recency.test.ts +0 -267
  680. package/src/messages/__tests__/tools.test.ts +0 -473
  681. package/src/messages/alternation.test.ts +0 -212
  682. package/src/messages/assistantPhase.test.ts +0 -75
  683. package/src/messages/cache.tail.test.ts +0 -533
  684. package/src/messages/cache.test.ts +0 -2166
  685. package/src/messages/content.test.ts +0 -362
  686. package/src/messages/contextPruning.test.ts +0 -184
  687. package/src/messages/ensureThinkingBlock.test.ts +0 -1427
  688. package/src/messages/foldToollessToolBlocks.test.ts +0 -596
  689. package/src/messages/formatAgentMessages.reducer.test.ts +0 -162
  690. package/src/messages/formatAgentMessages.skills.test.ts +0 -513
  691. package/src/messages/formatAgentMessages.steer.test.ts +0 -598
  692. package/src/messages/formatAgentMessages.test.ts +0 -8111
  693. package/src/messages/formatAgentMessages.tools.test.ts +0 -522
  694. package/src/messages/formatMessage.test.ts +0 -693
  695. package/src/messages/handoffCue.test.ts +0 -96
  696. package/src/messages/injected.test.ts +0 -107
  697. package/src/messages/labelContentByAgent.test.ts +0 -962
  698. package/src/messages/reducer.spec.ts +0 -32
  699. package/src/messages/shiftIndexTokenCountMap.test.ts +0 -81
  700. package/src/messages/tailCacheConversion.test.ts +0 -161
  701. package/src/openai/__tests__/openai.test.ts +0 -337
  702. package/src/responses/__tests__/responses.test.ts +0 -652
  703. package/src/scripts/abort.ts +0 -157
  704. package/src/scripts/activity-labels/captured.json +0 -56
  705. package/src/scripts/activity-labels/checks.cjs +0 -205
  706. package/src/scripts/activity-labels/corpus.cjs +0 -473
  707. package/src/scripts/activity-labels/report.cjs +0 -203
  708. package/src/scripts/activity-labels/rescore.cjs +0 -102
  709. package/src/scripts/activity-labels/run.ts +0 -705
  710. package/src/scripts/activity-labels/variants.ts +0 -71
  711. package/src/scripts/ant_web_search.ts +0 -159
  712. package/src/scripts/ant_web_search_edge_case.ts +0 -163
  713. package/src/scripts/ant_web_search_error_edge_case.ts +0 -149
  714. package/src/scripts/args.ts +0 -48
  715. package/src/scripts/bedrock-cache-debug.ts +0 -250
  716. package/src/scripts/bedrock-content-aggregation-test.ts +0 -266
  717. package/src/scripts/bedrock-merge-test.ts +0 -107
  718. package/src/scripts/bedrock-parallel-tools-test.ts +0 -204
  719. package/src/scripts/bench-prompt-cache.ts +0 -479
  720. package/src/scripts/caching.ts +0 -132
  721. package/src/scripts/cli.ts +0 -172
  722. package/src/scripts/cli2.ts +0 -133
  723. package/src/scripts/cli3.ts +0 -184
  724. package/src/scripts/cli4.ts +0 -191
  725. package/src/scripts/cli5.ts +0 -191
  726. package/src/scripts/code_exec.ts +0 -214
  727. package/src/scripts/code_exec_files.ts +0 -237
  728. package/src/scripts/code_exec_multi_session.ts +0 -237
  729. package/src/scripts/code_exec_ptc.ts +0 -335
  730. package/src/scripts/code_exec_session.ts +0 -283
  731. package/src/scripts/code_exec_simple.ts +0 -148
  732. package/src/scripts/compare_pi_vs_ours.ts +0 -1259
  733. package/src/scripts/content.ts +0 -139
  734. package/src/scripts/context-overflow-probe.ts +0 -997
  735. package/src/scripts/empty_input.ts +0 -137
  736. package/src/scripts/handoff-test.ts +0 -134
  737. package/src/scripts/local_engine.ts +0 -166
  738. package/src/scripts/local_engine_checkpointer.ts +0 -205
  739. package/src/scripts/local_engine_compile.ts +0 -263
  740. package/src/scripts/local_engine_hooks.ts +0 -226
  741. package/src/scripts/local_engine_image.ts +0 -201
  742. package/src/scripts/local_engine_ptc.ts +0 -151
  743. package/src/scripts/local_engine_workspace.ts +0 -258
  744. package/src/scripts/memory.ts +0 -107
  745. package/src/scripts/multi-agent-chain.ts +0 -332
  746. package/src/scripts/multi-agent-conditional.ts +0 -222
  747. package/src/scripts/multi-agent-document-review-chain.ts +0 -198
  748. package/src/scripts/multi-agent-hybrid-flow.ts +0 -311
  749. package/src/scripts/multi-agent-parallel-start.ts +0 -265
  750. package/src/scripts/multi-agent-parallel.ts +0 -395
  751. package/src/scripts/multi-agent-sequence.ts +0 -217
  752. package/src/scripts/multi-agent-subagent.ts +0 -246
  753. package/src/scripts/multi-agent-supervisor.ts +0 -366
  754. package/src/scripts/multi-agent-test.ts +0 -187
  755. package/src/scripts/openrouter_prompt_cache_live.ts +0 -310
  756. package/src/scripts/parallel-asymmetric-tools-test.ts +0 -275
  757. package/src/scripts/parallel-full-metadata-test.ts +0 -241
  758. package/src/scripts/parallel-tools-test.ts +0 -341
  759. package/src/scripts/preempt-probe.ts +0 -330
  760. package/src/scripts/preempt-scenarios.ts +0 -388
  761. package/src/scripts/programmatic_exec.ts +0 -387
  762. package/src/scripts/programmatic_exec_agent.ts +0 -232
  763. package/src/scripts/search.ts +0 -147
  764. package/src/scripts/sequential-full-metadata-test.ts +0 -198
  765. package/src/scripts/session_live.ts +0 -548
  766. package/src/scripts/simple.ts +0 -226
  767. package/src/scripts/single-agent-metadata-test.ts +0 -196
  768. package/src/scripts/stream.ts +0 -141
  769. package/src/scripts/subagent-configurable-inheritance.ts +0 -252
  770. package/src/scripts/subagent-event-driven-debug.ts +0 -190
  771. package/src/scripts/subagent-tools-debug.ts +0 -160
  772. package/src/scripts/subagent-usage-sink.ts +0 -176
  773. package/src/scripts/summarization-recency.ts +0 -462
  774. package/src/scripts/test-custom-prompt-key.ts +0 -145
  775. package/src/scripts/test-handoff-input.ts +0 -170
  776. package/src/scripts/test-handoff-preamble.ts +0 -278
  777. package/src/scripts/test-handoff-steering.ts +0 -430
  778. package/src/scripts/test-multi-agent-list-handoff.ts +0 -418
  779. package/src/scripts/test-parallel-agent-labeling.ts +0 -327
  780. package/src/scripts/test-parallel-handoffs.ts +0 -292
  781. package/src/scripts/test-thinking-handoff-bedrock.ts +0 -154
  782. package/src/scripts/test-thinking-handoff.ts +0 -156
  783. package/src/scripts/test-thinking-to-thinking-handoff-bedrock.ts +0 -166
  784. package/src/scripts/test-tool-before-handoff-role-order.ts +0 -276
  785. package/src/scripts/test-tools-before-handoff.ts +0 -222
  786. package/src/scripts/test_code_api.ts +0 -354
  787. package/src/scripts/thinking-bedrock.ts +0 -160
  788. package/src/scripts/thinking-vertexai.ts +0 -168
  789. package/src/scripts/thinking.ts +0 -172
  790. package/src/scripts/tool_search.ts +0 -153
  791. package/src/scripts/tools.ts +0 -163
  792. package/src/session/__tests__/JsonlSessionStore.test.ts +0 -1463
  793. package/src/session/__tests__/handlers.test.ts +0 -161
  794. package/src/specs/activity-label-observability.live.test.ts +0 -262
  795. package/src/specs/activity-label-observability.test.ts +0 -167
  796. package/src/specs/activity-label-prompt.test.ts +0 -359
  797. package/src/specs/activity-label-trace-seed.test.ts +0 -47
  798. package/src/specs/activity-phase-label.test.ts +0 -306
  799. package/src/specs/agent-handoffs.live.test.ts +0 -140
  800. package/src/specs/agent-handoffs.test.ts +0 -2465
  801. package/src/specs/anthropic.simple.test.ts +0 -453
  802. package/src/specs/ask-user-question-batch.test.ts +0 -289
  803. package/src/specs/ask-user-questions.live.test.ts +0 -185
  804. package/src/specs/ask-user-questions.test.ts +0 -293
  805. package/src/specs/azure.simple.test.ts +0 -502
  806. package/src/specs/bedrock-toolless.live.test.ts +0 -123
  807. package/src/specs/bedrock-truncation.live.test.ts +0 -191
  808. package/src/specs/cache.simple.test.ts +0 -416
  809. package/src/specs/context-accuracy.live.test.ts +0 -409
  810. package/src/specs/context-overflow-recovery.live.test.ts +0 -213
  811. package/src/specs/context-usage-event.test.ts +0 -117
  812. package/src/specs/context-usage.live.test.ts +0 -297
  813. package/src/specs/custom-event-await.test.ts +0 -304
  814. package/src/specs/deepseek.simple.test.ts +0 -290
  815. package/src/specs/deterministic-trace-id.test.ts +0 -43
  816. package/src/specs/discovered-tools.test.ts +0 -217
  817. package/src/specs/durability-checkpoint.integration.test.ts +0 -243
  818. package/src/specs/graph-subagent.live.test.ts +0 -242
  819. package/src/specs/graph-subagent.test.ts +0 -322
  820. package/src/specs/handoffCue.test.ts +0 -165
  821. package/src/specs/langfuse-callbacks.test.ts +0 -1578
  822. package/src/specs/langfuse-config.test.ts +0 -247
  823. package/src/specs/langfuse-instrumentation.test.ts +0 -393
  824. package/src/specs/langfuse-metadata.test.ts +0 -245
  825. package/src/specs/langfuse-routing.integration.test.ts +0 -844
  826. package/src/specs/langfuse-runtime-context.test.ts +0 -92
  827. package/src/specs/langfuse-span-registry.test.ts +0 -87
  828. package/src/specs/langfuse-tool-output-tracing.test.ts +0 -1858
  829. package/src/specs/langfuse-trace-shaping.test.ts +0 -591
  830. package/src/specs/moonshot.simple.test.ts +0 -367
  831. package/src/specs/multi-agent-summarization.test.ts +0 -396
  832. package/src/specs/openai.simple.test.ts +0 -315
  833. package/src/specs/openrouter.simple.test.ts +0 -275
  834. package/src/specs/preemptSeal.test.ts +0 -678
  835. package/src/specs/prune.test.ts +0 -3741
  836. package/src/specs/reasoning.test.ts +0 -201
  837. package/src/specs/run-step-timestamps.test.ts +0 -414
  838. package/src/specs/spec.utils.ts +0 -15
  839. package/src/specs/subagent.test.ts +0 -864
  840. package/src/specs/summarization-unit.test.ts +0 -973
  841. package/src/specs/summarization.test.ts +0 -3823
  842. package/src/specs/summarize-prune.test.ts +0 -376
  843. package/src/specs/thinking-handoff.test.ts +0 -621
  844. package/src/specs/thinking-prune.test.ts +0 -827
  845. package/src/specs/token-accounting-e2e.test.ts +0 -1034
  846. package/src/specs/token-accounting-pipeline.test.ts +0 -1008
  847. package/src/specs/token-distribution-edge-case.test.ts +0 -315
  848. package/src/specs/token-memoization.test.ts +0 -40
  849. package/src/specs/tokens.test.ts +0 -1000
  850. package/src/specs/tool-error-resume.test.ts +0 -194
  851. package/src/specs/tool-error.test.ts +0 -198
  852. package/src/specs/vllm-reasoning-toolcalls.test.ts +0 -340
  853. package/src/stream.dispatch.test.ts +0 -63
  854. package/src/stream.test.ts +0 -144
  855. package/src/summarization/__tests__/aggregator.test.ts +0 -236
  856. package/src/summarization/__tests__/node.test.ts +0 -1996
  857. package/src/summarization/__tests__/trigger.test.ts +0 -149
  858. package/src/summarization/chunkHandler.test.ts +0 -196
  859. package/src/test/mockTools.ts +0 -386
  860. package/src/tools/Calculator.test.ts +0 -278
  861. package/src/tools/__tests__/BashExecutor.test.ts +0 -108
  862. package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +0 -939
  863. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +0 -858
  864. package/src/tools/__tests__/CodeExecutor.stateful.test.ts +0 -124
  865. package/src/tools/__tests__/LocalExecutionRoots.test.ts +0 -8
  866. package/src/tools/__tests__/LocalExecutionTools.test.ts +0 -2733
  867. package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.ts +0 -318
  868. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -1557
  869. package/src/tools/__tests__/ReadFile.test.ts +0 -44
  870. package/src/tools/__tests__/SkillTool.test.ts +0 -442
  871. package/src/tools/__tests__/SubagentExecutor.test.ts +0 -3728
  872. package/src/tools/__tests__/SubagentReplay.test.ts +0 -329
  873. package/src/tools/__tests__/SubagentTool.test.ts +0 -149
  874. package/src/tools/__tests__/ToolNode.breakerSignal.test.ts +0 -595
  875. package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +0 -1191
  876. package/src/tools/__tests__/ToolNode.invalidToolCalls.test.ts +0 -757
  877. package/src/tools/__tests__/ToolNode.langfuse.test.ts +0 -57
  878. package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +0 -736
  879. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +0 -1710
  880. package/src/tools/__tests__/ToolNode.runtimeState.test.ts +0 -120
  881. package/src/tools/__tests__/ToolNode.session.test.ts +0 -1352
  882. package/src/tools/__tests__/ToolNode.streamLimits.test.ts +0 -69
  883. package/src/tools/__tests__/ToolSearch.integration.test.ts +0 -161
  884. package/src/tools/__tests__/ToolSearch.test.ts +0 -1227
  885. package/src/tools/__tests__/annotateMessagesForLLM.test.ts +0 -561
  886. package/src/tools/__tests__/directToolHITLResumeScope.test.ts +0 -980
  887. package/src/tools/__tests__/directToolHooks.test.ts +0 -700
  888. package/src/tools/__tests__/eagerEventExecution.session.test.ts +0 -92
  889. package/src/tools/__tests__/handlers.test.ts +0 -997
  890. package/src/tools/__tests__/hitl.test.ts +0 -4663
  891. package/src/tools/__tests__/intentArg.test.ts +0 -357
  892. package/src/tools/__tests__/intentCoverage.test.ts +0 -139
  893. package/src/tools/__tests__/localToolNames.test.ts +0 -75
  894. package/src/tools/__tests__/skillCatalog.test.ts +0 -161
  895. package/src/tools/__tests__/subagentHooks.test.ts +0 -1651
  896. package/src/tools/__tests__/toolOutputReferences.test.ts +0 -515
  897. package/src/tools/__tests__/workspaceSeam.test.ts +0 -147
  898. package/src/tools/local/__tests__/FileCheckpointer.test.ts +0 -162
  899. package/src/tools/local/__tests__/editStrategies.test.ts +0 -134
  900. package/src/tools/search/content.test.ts +0 -173
  901. package/src/tools/search/crw.test.ts +0 -836
  902. package/src/tools/search/format.test.ts +0 -242
  903. package/src/tools/search/http-agent.test.ts +0 -133
  904. package/src/tools/search/jina-reranker.test.ts +0 -306
  905. package/src/tools/search/keenable-scraper.test.ts +0 -153
  906. package/src/tools/search/keenable.test.ts +0 -183
  907. package/src/tools/search/outcome.test.ts +0 -90
  908. package/src/tools/search/output.md +0 -2775
  909. package/src/tools/search/rag-api-reranker.test.ts +0 -891
  910. package/src/tools/search/source-processing.test.ts +0 -516
  911. package/src/tools/search/tavily.test.ts +0 -965
  912. package/src/tools/search/test.html +0 -884
  913. package/src/tools/search/test.md +0 -643
  914. package/src/tools/subagent/__tests__/SubagentExecutionRegistry.test.ts +0 -637
  915. package/src/tools/subagent/__tests__/SubagentExecutor.lazy.test.ts +0 -3054
  916. package/src/tools/subagent/__tests__/childGraphConfig.test.ts +0 -521
  917. package/src/utils/__tests__/apportion.test.ts +0 -32
  918. package/src/utils/__tests__/errors.test.ts +0 -270
  919. package/src/utils/__tests__/fixtures/contextOverflowSignatures.ts +0 -336
  920. package/src/utils/__tests__/redactSecrets.test.ts +0 -56
  921. package/src/utils/__tests__/truncation.test.ts +0 -66
  922. package/src/utils/llm.test.ts +0 -18
  923. package/src/utils/proxy.test.ts +0 -176
  924. package/src/utils/toolContent.test.ts +0 -1432
@@ -1,1996 +0,0 @@
1
- import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
2
- import type { RunnableConfig } from '@langchain/core/runnables';
3
- import type { BaseMessage } from '@langchain/core/messages';
4
- import type * as t from '@/types';
5
- import {
6
- createSummarizeNode,
7
- DEFAULT_SUMMARIZATION_PROMPT,
8
- DEFAULT_UPDATE_SUMMARIZATION_PROMPT,
9
- } from '@/summarization/node';
10
- import { StreamLimitExceededError } from '@/llm/streamLimits';
11
- import { convertInjectedMessages } from '@/messages/injected';
12
- import { Constants, GraphEvents, Providers } from '@/common';
13
- import { AgentContext } from '@/agents/AgentContext';
14
- import * as providers from '@/llm/providers';
15
- import * as eventUtils from '@/utils/events';
16
-
17
- // ---------------------------------------------------------------------------
18
- // Helpers
19
- // ---------------------------------------------------------------------------
20
-
21
- /** Creates a real AgentContext via fromConfig with sensible defaults.
22
- * Extra properties are assigned directly for test-specific overrides.
23
- *
24
- * Defaults `retainRecent.turns` to `0` so that tests which use 1–2 message
25
- * states still exercise the LLM-call summarization path. The recency-window
26
- * default of `2` turns would otherwise short-circuit summarization for those
27
- * inputs. Tests that target recency-window behavior should pass an explicit
28
- * `summarizationConfig.retainRecent` value. */
29
- function createAgentContext(
30
- overrides: Record<string, unknown> = {}
31
- ): AgentContext {
32
- const {
33
- // AgentInputs fields
34
- agentId = 'agent_0',
35
- provider = Providers.OPENAI,
36
- instructions = 'Test agent',
37
- summarizationEnabled = true,
38
- summarizationConfig,
39
- maxContextTokens,
40
- tools,
41
- ...extra
42
- } = overrides;
43
-
44
- const effectiveSummarizationConfig =
45
- summarizationConfig != null
46
- ? summarizationConfig
47
- : { retainRecent: { turns: 0 } };
48
-
49
- const ctx = AgentContext.fromConfig({
50
- agentId: agentId as string,
51
- provider: provider as Providers,
52
- instructions: instructions as string,
53
- summarizationEnabled: summarizationEnabled as boolean,
54
- summarizationConfig: effectiveSummarizationConfig,
55
- ...(maxContextTokens != null ? { maxContextTokens } : {}),
56
- ...(tools != null ? { tools } : {}),
57
- } as import('@/types').AgentInputs);
58
-
59
- // Apply direct property overrides for test-specific internal state
60
- for (const [key, value] of Object.entries(extra)) {
61
- (ctx as unknown as Record<string, unknown>)[key] = value;
62
- }
63
-
64
- return ctx;
65
- }
66
-
67
- /** Creates a mock graph container for createSummarizeNode. */
68
- function mockGraph(
69
- onStepCompleted?: (stepId: string, result: t.StepCompleted) => void
70
- ): {
71
- contentData: t.RunStep[];
72
- contentIndexMap: Map<string, number>;
73
- config: RunnableConfig;
74
- runId: string;
75
- isMultiAgent: boolean;
76
- dispatchRunStep: (
77
- runStep: t.RunStep,
78
- config?: RunnableConfig
79
- ) => Promise<void>;
80
- dispatchRunStepCompleted: (
81
- stepId: string,
82
- result: t.StepCompleted,
83
- config?: RunnableConfig
84
- ) => Promise<void>;
85
- } {
86
- const contentData: t.RunStep[] = [];
87
- const contentIndexMap = new Map<string, number>();
88
- return {
89
- contentData,
90
- contentIndexMap,
91
- config: {} as RunnableConfig,
92
- runId: 'run_1',
93
- isMultiAgent: false,
94
- dispatchRunStep: async (runStep: t.RunStep): Promise<void> => {
95
- contentData.push(runStep);
96
- contentIndexMap.set(runStep.id, runStep.index);
97
- },
98
- dispatchRunStepCompleted: async (
99
- stepId: string,
100
- result: t.StepCompleted
101
- ): Promise<void> => {
102
- onStepCompleted?.(stepId, result);
103
- },
104
- };
105
- }
106
-
107
- let stepCounter = 0;
108
- function generateStepId(_stepKey: string): [string, number] {
109
- const id = `step_test_${stepCounter++}`;
110
- return [id, 0];
111
- }
112
-
113
- /** Collects custom events dispatched during the node execution. */
114
- function captureEvents(): Array<{ event: string; data: unknown }> {
115
- const events: Array<{ event: string; data: unknown }> = [];
116
- jest.spyOn(eventUtils, 'safeDispatchCustomEvent').mockImplementation((async (
117
- ...args: unknown[]
118
- ) => {
119
- events.push({ event: args[0] as string, data: args[1] });
120
- }) as never);
121
- return events;
122
- }
123
-
124
- /** Creates a mock model that returns a canned response via invoke(). */
125
- function mockInvokeModel(response: string): { invoke: jest.Mock } {
126
- return {
127
- invoke: jest.fn().mockResolvedValue({ content: response }),
128
- };
129
- }
130
-
131
- /**
132
- * Creates a mock model that streams text chunk-by-chunk.
133
- * invoke() returns the full text; stream() yields one chunk per word.
134
- */
135
- function mockStreamingModel(response: string): {
136
- invoke: jest.Mock;
137
- stream: jest.Mock;
138
- } {
139
- const words = response.split(' ');
140
- return {
141
- invoke: jest.fn().mockResolvedValue({ content: response }),
142
- stream: jest.fn().mockImplementation(async () => {
143
- return (async function* (): AsyncGenerator<{ content: string }> {
144
- for (const word of words) {
145
- // Add space back except for first word
146
- yield { content: word + ' ' };
147
- }
148
- })();
149
- }),
150
- };
151
- }
152
-
153
- // ---------------------------------------------------------------------------
154
- // Tests
155
- // ---------------------------------------------------------------------------
156
-
157
- beforeEach(() => {
158
- stepCounter = 0;
159
- jest.restoreAllMocks();
160
- });
161
-
162
- describe('createSummarizeNode', () => {
163
- it('emits ON_SUMMARIZE_START and ON_SUMMARIZE_COMPLETE on success', async () => {
164
- const events = captureEvents();
165
-
166
- // Mock getChatModelClass to return our mock model
167
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
168
- class {
169
- constructor() {
170
- return mockInvokeModel('Test summary output');
171
- }
172
- } as never
173
- );
174
-
175
- const agentContext = createAgentContext();
176
- const graph = mockGraph((_stepId, result) => {
177
- if (result.type === 'summary') {
178
- events.push({
179
- event: GraphEvents.ON_SUMMARIZE_COMPLETE,
180
- data: { summary: result.summary },
181
- });
182
- }
183
- });
184
- const node = createSummarizeNode({
185
- agentContext,
186
- graph,
187
- generateStepId,
188
- });
189
-
190
- await node(
191
- {
192
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
193
- summarizationRequest: {
194
- remainingContextTokens: 1000,
195
- agentId: 'agent_0',
196
- },
197
- },
198
- {} as RunnableConfig
199
- );
200
-
201
- const eventNames = events.map((e) => e.event);
202
- // ON_RUN_STEP now goes through graph.dispatchRunStep, not safeDispatchCustomEvent
203
- expect(graph.contentData.length).toBeGreaterThan(0);
204
- expect(eventNames).toContain(GraphEvents.ON_SUMMARIZE_START);
205
- expect(eventNames).toContain(GraphEvents.ON_SUMMARIZE_COMPLETE);
206
-
207
- // Complete event should have the summary text
208
- const completeEvent = events.find(
209
- (e) => e.event === GraphEvents.ON_SUMMARIZE_COMPLETE
210
- );
211
- expect(
212
- (
213
- (completeEvent?.data as t.SummarizeCompleteEvent).summary!
214
- .content?.[0] as { text: string } | undefined
215
- )?.text
216
- ).toBe('Test summary output');
217
- expect(
218
- (completeEvent?.data as t.SummarizeCompleteEvent).error
219
- ).toBeUndefined();
220
- });
221
-
222
- it('stamps INVOKED_MODEL/INVOKED_PROVIDER metadata for a dedicated summarizer model', async () => {
223
- captureEvents();
224
-
225
- const capturedConfigs: unknown[] = [];
226
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
227
- class {
228
- constructor() {
229
- return {
230
- invoke: jest
231
- .fn()
232
- .mockImplementation(
233
- async (_messages: unknown, config?: unknown) => {
234
- capturedConfigs.push(config);
235
- return { content: 'Summary text' };
236
- }
237
- ),
238
- };
239
- }
240
- } as never
241
- );
242
-
243
- const agentContext = createAgentContext({
244
- summarizationConfig: {
245
- retainRecent: { turns: 0 },
246
- model: 'gpt-4.1-mini',
247
- },
248
- });
249
- const graph = mockGraph();
250
- const node = createSummarizeNode({
251
- agentContext,
252
- graph,
253
- generateStepId,
254
- });
255
-
256
- await node(
257
- {
258
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
259
- summarizationRequest: {
260
- remainingContextTokens: 1000,
261
- agentId: 'agent_0',
262
- },
263
- },
264
- {} as RunnableConfig
265
- );
266
-
267
- /**
268
- * Usage consumers (the subagent usage-capture handler) attribute the
269
- * call from these keys — without them, a summarizer model that differs
270
- * from the agent's primary would be billed against the primary config.
271
- */
272
- const config = capturedConfigs[0] as {
273
- metadata?: Record<string, unknown>;
274
- };
275
- expect(config.metadata?.[Constants.INVOKED_MODEL]).toBe('gpt-4.1-mini');
276
- expect(config.metadata?.[Constants.INVOKED_PROVIDER]).toBe(
277
- Providers.OPENAI
278
- );
279
- });
280
-
281
- it('collects streamed text when model supports stream()', async () => {
282
- captureEvents();
283
-
284
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
285
- class {
286
- constructor() {
287
- return mockStreamingModel('one two three');
288
- }
289
- } as never
290
- );
291
-
292
- const setSummary = jest.fn();
293
- const agentContext = createAgentContext({ setSummary } as never);
294
- const graph = mockGraph();
295
- const node = createSummarizeNode({
296
- agentContext,
297
- graph,
298
- generateStepId,
299
- });
300
-
301
- await node(
302
- {
303
- messages: [new HumanMessage('Test message')],
304
- summarizationRequest: {
305
- remainingContextTokens: 1000,
306
- agentId: 'agent_0',
307
- },
308
- },
309
- {} as RunnableConfig
310
- );
311
-
312
- // Node collects the full streamed text and calls setSummary.
313
- // Delta events are dispatched by ChatModelStreamHandler, not the node.
314
- expect(setSummary).toHaveBeenCalledWith(
315
- 'one two three',
316
- expect.any(Number)
317
- );
318
- });
319
-
320
- it('falls back to invoke when model has no stream()', async () => {
321
- captureEvents();
322
-
323
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
324
- class {
325
- constructor() {
326
- return mockInvokeModel('Full summary text');
327
- }
328
- } as never
329
- );
330
-
331
- const setSummary = jest.fn();
332
- const agentContext = createAgentContext({ setSummary } as never);
333
- const graph = mockGraph();
334
- const node = createSummarizeNode({
335
- agentContext,
336
- graph,
337
- generateStepId,
338
- });
339
-
340
- await node(
341
- {
342
- messages: [new HumanMessage('Test message')],
343
- summarizationRequest: {
344
- remainingContextTokens: 1000,
345
- agentId: 'agent_0',
346
- },
347
- },
348
- {} as RunnableConfig
349
- );
350
-
351
- // Falls back to invoke and still collects the text
352
- expect(setSummary).toHaveBeenCalledWith(
353
- 'Full summary text',
354
- expect.any(Number)
355
- );
356
- });
357
-
358
- it('produces metadata stub when all LLM attempts fail', async () => {
359
- const events = captureEvents();
360
-
361
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
362
- class {
363
- constructor() {
364
- return {
365
- invoke: jest.fn().mockRejectedValue(new Error('Model error')),
366
- };
367
- }
368
- } as never
369
- );
370
-
371
- const setSummary = jest.fn();
372
- const agentContext = createAgentContext({ setSummary } as never);
373
- const graph = mockGraph((_stepId, result) => {
374
- if (result.type === 'summary') {
375
- events.push({
376
- event: GraphEvents.ON_SUMMARIZE_COMPLETE,
377
- data: { summary: result.summary },
378
- });
379
- }
380
- });
381
- const node = createSummarizeNode({
382
- agentContext,
383
- graph,
384
- generateStepId,
385
- });
386
-
387
- const result = await node(
388
- {
389
- messages: [new HumanMessage('Test')],
390
- summarizationRequest: {
391
- remainingContextTokens: 1000,
392
- agentId: 'agent_0',
393
- },
394
- },
395
- {} as RunnableConfig
396
- );
397
-
398
- expect(result.summarizationRequest).toBeUndefined();
399
- // After summarization, REMOVE_ALL + surviving context is returned
400
- expect(result.messages).toBeDefined();
401
- expect(result.messages!.length).toBeGreaterThanOrEqual(1);
402
- expect(result.messages![0]._getType()).toBe('remove');
403
-
404
- // Tier 3 fallback: metadata stub is used as summary text
405
- const completeEvent = events.find(
406
- (e) => e.event === GraphEvents.ON_SUMMARIZE_COMPLETE
407
- );
408
- expect(
409
- (
410
- (completeEvent?.data as t.SummarizeCompleteEvent).summary!
411
- .content?.[0] as { text: string } | undefined
412
- )?.text
413
- ).toMatch(/^\[Metadata summary:/);
414
- expect(
415
- (completeEvent?.data as t.SummarizeCompleteEvent).error
416
- ).toBeUndefined();
417
- });
418
-
419
- it('catches model initialization errors and falls back to metadata stub', async () => {
420
- captureEvents();
421
-
422
- /**
423
- * Simulate the "Unsupported LLM provider" case — e.g. when a caller
424
- * forwards an unrecognized provider name (custom-endpoint label) that
425
- * getChatModelClass cannot resolve. Prior to the defense-in-depth fix,
426
- * this error was thrown outside the try/catch in executeSummarizationWithFallback
427
- * and bubbled up silently. Now it is caught and the metadata stub is used.
428
- */
429
- jest.spyOn(providers, 'getChatModelClass').mockImplementation(() => {
430
- throw new Error('Unsupported LLM provider: Ollama');
431
- });
432
-
433
- const setSummary = jest.fn();
434
- const agentContext = createAgentContext({ setSummary } as never);
435
- const graph = mockGraph();
436
- const node = createSummarizeNode({
437
- agentContext,
438
- graph,
439
- generateStepId,
440
- });
441
-
442
- await expect(
443
- node(
444
- {
445
- messages: [new HumanMessage('Test message')],
446
- summarizationRequest: {
447
- remainingContextTokens: 1000,
448
- agentId: 'agent_0',
449
- },
450
- },
451
- {} as RunnableConfig
452
- )
453
- ).resolves.not.toThrow();
454
-
455
- expect(setSummary).toHaveBeenCalledWith(
456
- expect.stringContaining('[Metadata summary:'),
457
- expect.any(Number)
458
- );
459
- });
460
-
461
- it('falls back to metadata stub when primary LLM call fails', async () => {
462
- captureEvents();
463
-
464
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
465
- class {
466
- constructor() {
467
- return {
468
- invoke: jest.fn().mockRejectedValue(new Error('LLM unavailable')),
469
- };
470
- }
471
- } as never
472
- );
473
-
474
- const setSummary = jest.fn();
475
- const agentContext = createAgentContext({ setSummary } as never);
476
- const graph = mockGraph();
477
- const node = createSummarizeNode({
478
- agentContext,
479
- graph,
480
- generateStepId,
481
- });
482
-
483
- await node(
484
- {
485
- messages: [new HumanMessage('Test message')],
486
- summarizationRequest: {
487
- remainingContextTokens: 1000,
488
- agentId: 'agent_0',
489
- },
490
- },
491
- {} as RunnableConfig
492
- );
493
-
494
- expect(setSummary).toHaveBeenCalledWith(
495
- expect.stringContaining('[Metadata summary:'),
496
- expect.any(Number)
497
- );
498
- });
499
-
500
- it('calls setSummary with the final text', async () => {
501
- captureEvents();
502
-
503
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
504
- class {
505
- constructor() {
506
- return mockInvokeModel('Final summary');
507
- }
508
- } as never
509
- );
510
-
511
- const setSummary = jest.fn();
512
- const agentContext = createAgentContext({ setSummary } as never);
513
- const graph = mockGraph();
514
- const node = createSummarizeNode({
515
- agentContext,
516
- graph,
517
- generateStepId,
518
- });
519
-
520
- await node(
521
- {
522
- messages: [new HumanMessage('Test')],
523
- summarizationRequest: {
524
- remainingContextTokens: 1000,
525
- agentId: 'agent_0',
526
- },
527
- },
528
- {} as RunnableConfig
529
- );
530
-
531
- expect(setSummary).toHaveBeenCalledWith(
532
- 'Final summary',
533
- expect.any(Number)
534
- );
535
- });
536
-
537
- it('cache-hit path sends raw messages with instruction appended as final HumanMessage', async () => {
538
- captureEvents();
539
-
540
- const capturedMessages: Array<{ type: string; content: string }> = [];
541
-
542
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
543
- class {
544
- constructor() {
545
- return {
546
- invoke: jest
547
- .fn()
548
- .mockImplementation(async (messages: unknown[]) => {
549
- for (const msg of messages as {
550
- getType: () => string;
551
- content: string | unknown[];
552
- }[]) {
553
- capturedMessages.push({
554
- type: msg.getType(),
555
- content:
556
- typeof msg.content === 'string'
557
- ? msg.content
558
- : JSON.stringify(msg.content),
559
- });
560
- }
561
- return {
562
- content:
563
- '## Goal\nTest goal\n\n<events>\n<event key="test" turn="0">value</event>\n</events>',
564
- };
565
- }),
566
- };
567
- }
568
- } as never
569
- );
570
-
571
- const agentContext = createAgentContext();
572
- const graph = mockGraph();
573
- const node = createSummarizeNode({
574
- agentContext,
575
- graph: graph as never,
576
- generateStepId,
577
- });
578
-
579
- await node(
580
- {
581
- messages: [
582
- new HumanMessage('Message 1'),
583
- new HumanMessage('Message 2'),
584
- new HumanMessage('Message 3'),
585
- ],
586
- summarizationRequest: {
587
- remainingContextTokens: 1000,
588
- agentId: 'agent_0',
589
- },
590
- },
591
- {} as RunnableConfig
592
- );
593
-
594
- // The raw messages should be sent + instruction appended as the last HumanMessage
595
- // messagesToRefine has 3 HumanMessages, instruction adds 1 more
596
- expect(capturedMessages.length).toBe(4);
597
- expect(capturedMessages[0].type).toBe('human');
598
- expect(capturedMessages[0].content).toBe('Message 1');
599
- expect(capturedMessages[3].type).toBe('human');
600
- // The last message should contain the summarization prompt
601
- expect(capturedMessages[3].content).toContain(
602
- 'context window is filling up'
603
- );
604
- });
605
-
606
- it('cache-hit path includes prior summary in the instruction message', async () => {
607
- captureEvents();
608
-
609
- const capturedMessages: Array<{ type: string; content: string }> = [];
610
-
611
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
612
- class {
613
- constructor() {
614
- return {
615
- invoke: jest
616
- .fn()
617
- .mockImplementation(async (messages: unknown[]) => {
618
- for (const msg of messages as {
619
- getType: () => string;
620
- content: string | unknown[];
621
- }[]) {
622
- capturedMessages.push({
623
- type: msg.getType(),
624
- content:
625
- typeof msg.content === 'string'
626
- ? msg.content
627
- : JSON.stringify(msg.content),
628
- });
629
- }
630
- return { content: '## Goal\nUpdated summary' };
631
- }),
632
- };
633
- }
634
- } as never
635
- );
636
-
637
- // Create context with a prior summary
638
- const agentContext = createAgentContext();
639
- agentContext.setSummary('## Goal\nPrior summary content.', 50);
640
-
641
- const graph = mockGraph();
642
- const node = createSummarizeNode({
643
- agentContext,
644
- graph: graph as never,
645
- generateStepId,
646
- });
647
-
648
- await node(
649
- {
650
- messages: [new HumanMessage('New message')],
651
- summarizationRequest: {
652
- remainingContextTokens: 1000,
653
- agentId: 'agent_0',
654
- },
655
- },
656
- {} as RunnableConfig
657
- );
658
-
659
- // The last message should contain the update prompt (prior summary exists)
660
- const lastMsg = capturedMessages[capturedMessages.length - 1];
661
- expect(lastMsg.type).toBe('human');
662
- expect(lastMsg.content).toContain('Merge the new messages');
663
- // Should include the prior summary
664
- expect(lastMsg.content).toContain('<previous-summary>');
665
- expect(lastMsg.content).toContain('Prior summary content');
666
- });
667
- });
668
-
669
- describe('DEFAULT_SUMMARIZATION_PROMPT', () => {
670
- it('is exported and non-empty', () => {
671
- expect(typeof DEFAULT_SUMMARIZATION_PROMPT).toBe('string');
672
- expect(DEFAULT_SUMMARIZATION_PROMPT.length).toBeGreaterThan(0);
673
- });
674
-
675
- it('contains structured checkpoint sections', () => {
676
- expect(DEFAULT_SUMMARIZATION_PROMPT).toContain('## Goal');
677
- expect(DEFAULT_SUMMARIZATION_PROMPT).toContain('## Progress');
678
- expect(DEFAULT_SUMMARIZATION_PROMPT).toContain('## Key Decisions');
679
- expect(DEFAULT_SUMMARIZATION_PROMPT).toContain('## Next Steps');
680
- });
681
- });
682
-
683
- describe('DEFAULT_UPDATE_SUMMARIZATION_PROMPT', () => {
684
- it('is exported and non-empty', () => {
685
- expect(typeof DEFAULT_UPDATE_SUMMARIZATION_PROMPT).toBe('string');
686
- expect(DEFAULT_UPDATE_SUMMARIZATION_PROMPT.length).toBeGreaterThan(0);
687
- });
688
-
689
- it('instructs merging new content', () => {
690
- expect(DEFAULT_UPDATE_SUMMARIZATION_PROMPT).toMatch(
691
- /Merge the new messages/i
692
- );
693
- });
694
-
695
- it('instructs updating progress tracking', () => {
696
- expect(DEFAULT_UPDATE_SUMMARIZATION_PROMPT).toMatch(/Done/);
697
- expect(DEFAULT_UPDATE_SUMMARIZATION_PROMPT).toMatch(/In Progress/);
698
- });
699
- });
700
-
701
- describe('budget check — instructions exceed context', () => {
702
- it('skips summarization when instructionTokens >= maxContextTokens', async () => {
703
- const events = captureEvents();
704
- const agentContext = createAgentContext({
705
- maxContextTokens: 4000,
706
- systemMessageTokens: 5000,
707
- formatTokenBudgetBreakdown: () => 'mock breakdown',
708
- });
709
-
710
- const graph = mockGraph();
711
- const summarizeNode = createSummarizeNode({
712
- agentContext,
713
- graph: graph as never,
714
- generateStepId,
715
- });
716
-
717
- const result = await summarizeNode(
718
- {
719
- messages: [new HumanMessage('test')],
720
- summarizationRequest: {
721
- remainingContextTokens: -1000,
722
- agentId: 'agent_0',
723
- },
724
- },
725
- {} as RunnableConfig
726
- );
727
-
728
- expect(result.summarizationRequest).toBeUndefined();
729
- expect(result.messages).toBeUndefined();
730
-
731
- // No summarization events should have fired
732
- const summarizeEvents = events.filter(
733
- (e) =>
734
- e.event === GraphEvents.ON_SUMMARIZE_START ||
735
- e.event === GraphEvents.ON_SUMMARIZE_DELTA ||
736
- e.event === GraphEvents.ON_SUMMARIZE_COMPLETE
737
- );
738
- expect(summarizeEvents).toHaveLength(0);
739
- });
740
-
741
- it('proceeds normally when instructionTokens < maxContextTokens', async () => {
742
- captureEvents();
743
-
744
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
745
- class {
746
- constructor() {
747
- return mockInvokeModel('Budget is fine summary');
748
- }
749
- } as never
750
- );
751
-
752
- const agentContext = createAgentContext({
753
- maxContextTokens: 8000,
754
- systemMessageTokens: 2000,
755
- formatTokenBudgetBreakdown: () => 'mock breakdown',
756
- });
757
-
758
- const graph = mockGraph();
759
- const summarizeNode = createSummarizeNode({
760
- agentContext,
761
- graph: graph as never,
762
- generateStepId,
763
- });
764
-
765
- const result = await summarizeNode(
766
- {
767
- messages: [new HumanMessage('hello')],
768
- summarizationRequest: {
769
- remainingContextTokens: 500,
770
- agentId: 'agent_0',
771
- },
772
- },
773
- {} as RunnableConfig
774
- );
775
-
776
- // Should have summarized — messages returned for state replacement
777
- expect(result.messages).toBeDefined();
778
- expect(result.messages!.length).toBeGreaterThan(0);
779
- });
780
- });
781
-
782
- describe('recency window — first-turn protection', () => {
783
- it('skips the LLM call when only one turn exists (default turns: 2)', async () => {
784
- const events = captureEvents();
785
-
786
- const invokeMock = jest
787
- .fn()
788
- .mockResolvedValue({ content: 'should not be called' });
789
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
790
- class {
791
- constructor() {
792
- return { invoke: invokeMock };
793
- }
794
- } as never
795
- );
796
-
797
- const setSummary = jest.fn();
798
- const agentContext = createAgentContext({
799
- summarizationConfig: {} /* defaults to retainRecent.turns = 2 */,
800
- setSummary,
801
- } as never);
802
- const graph = mockGraph();
803
- const summarizeNode = createSummarizeNode({
804
- agentContext,
805
- graph: graph as never,
806
- generateStepId,
807
- });
808
-
809
- const largePayload = 'paste'.repeat(10_000);
810
- const result = await summarizeNode(
811
- {
812
- messages: [new HumanMessage(largePayload)],
813
- summarizationRequest: {
814
- remainingContextTokens: 0,
815
- agentId: 'agent_0',
816
- },
817
- },
818
- {} as RunnableConfig
819
- );
820
-
821
- // No LLM call — first user message is preserved verbatim.
822
- expect(invokeMock).not.toHaveBeenCalled();
823
- expect(setSummary).not.toHaveBeenCalled();
824
- // No state mutation — original messages stay.
825
- expect(result.messages).toBeUndefined();
826
- expect(result.summarizationRequest).toBeUndefined();
827
- // No ON_SUMMARIZE_START emitted on the skip path.
828
- const eventNames = events.map((e) => e.event);
829
- expect(eventNames).not.toContain(GraphEvents.ON_SUMMARIZE_START);
830
- expect(eventNames).not.toContain(GraphEvents.ON_SUMMARIZE_COMPLETE);
831
- });
832
-
833
- it('skips when a single-turn includes assistant + tool messages', async () => {
834
- captureEvents();
835
-
836
- const invokeMock = jest.fn().mockResolvedValue({ content: 'unused' });
837
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
838
- class {
839
- constructor() {
840
- return { invoke: invokeMock };
841
- }
842
- } as never
843
- );
844
-
845
- const setSummary = jest.fn();
846
- const agentContext = createAgentContext({
847
- summarizationConfig: {},
848
- setSummary,
849
- } as never);
850
- const graph = mockGraph();
851
- const summarizeNode = createSummarizeNode({
852
- agentContext,
853
- graph: graph as never,
854
- generateStepId,
855
- });
856
-
857
- const result = await summarizeNode(
858
- {
859
- messages: [
860
- new HumanMessage('the first user prompt'),
861
- new AIMessage({
862
- content: '',
863
- tool_calls: [{ id: 'c', name: 'search', args: {} }],
864
- }),
865
- new ToolMessage({
866
- content: 'result',
867
- tool_call_id: 'c',
868
- name: 'search',
869
- }),
870
- new AIMessage('here is what i found'),
871
- ],
872
- summarizationRequest: {
873
- remainingContextTokens: 0,
874
- agentId: 'agent_0',
875
- },
876
- },
877
- {} as RunnableConfig
878
- );
879
-
880
- expect(invokeMock).not.toHaveBeenCalled();
881
- expect(setSummary).not.toHaveBeenCalled();
882
- expect(result.messages).toBeUndefined();
883
- });
884
-
885
- it('still summarizes the head when older turns exist beyond the recency window', async () => {
886
- captureEvents();
887
-
888
- let capturedMessages: { type: string; content: unknown }[] = [];
889
- const invokeMock = jest.fn().mockImplementation((messages: unknown) => {
890
- capturedMessages = (
891
- messages as Array<{ getType: () => string; content: unknown }>
892
- ).map((m) => ({ type: m.getType(), content: m.content }));
893
- return Promise.resolve({ content: 'Summary of older turns' });
894
- });
895
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
896
- class {
897
- constructor() {
898
- return { invoke: invokeMock };
899
- }
900
- } as never
901
- );
902
-
903
- const setSummary = jest.fn();
904
- const agentContext = createAgentContext({
905
- summarizationConfig: { retainRecent: { turns: 1 } },
906
- setSummary,
907
- } as never);
908
- const graph = mockGraph();
909
- const summarizeNode = createSummarizeNode({
910
- agentContext,
911
- graph: graph as never,
912
- generateStepId,
913
- });
914
-
915
- const messages = [
916
- new HumanMessage('turn 1 query'),
917
- new AIMessage('turn 1 reply'),
918
- new HumanMessage('turn 2 query'),
919
- new AIMessage('turn 2 reply'),
920
- ];
921
- const result = await summarizeNode(
922
- {
923
- messages,
924
- summarizationRequest: {
925
- remainingContextTokens: 0,
926
- agentId: 'agent_0',
927
- },
928
- },
929
- {} as RunnableConfig
930
- );
931
-
932
- // Head (turn 1) summarized; tail (turn 2) preserved verbatim.
933
- expect(setSummary).toHaveBeenCalledWith(
934
- expect.stringContaining('Summary of older turns'),
935
- expect.any(Number)
936
- );
937
- // Captured messages are the head + the appended summarization instruction.
938
- // Head has 2 messages (turn 1) + 1 instruction = 3 total.
939
- expect(capturedMessages).toHaveLength(3);
940
- expect(capturedMessages[0]?.content).toBe('turn 1 query');
941
- expect(capturedMessages[1]?.content).toBe('turn 1 reply');
942
-
943
- // Returned messages: removeAll marker + tail.
944
- expect(result.messages).toBeDefined();
945
- expect(result.messages![0]?._getType()).toBe('remove');
946
- expect(result.messages!.slice(1)).toHaveLength(2);
947
- expect((result.messages![1] as HumanMessage).content).toBe('turn 2 query');
948
- expect((result.messages![2] as AIMessage).content).toBe('turn 2 reply');
949
- });
950
-
951
- describe('summary coverage', () => {
952
- const runCompaction = async (
953
- messages: BaseMessage[],
954
- turns = 1
955
- ): Promise<t.SummaryContentBlock | undefined> => {
956
- captureEvents();
957
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
958
- class {
959
- constructor() {
960
- return mockInvokeModel('Summary of older turns');
961
- }
962
- } as never
963
- );
964
-
965
- let summaryBlock: t.SummaryContentBlock | undefined;
966
- const graph = mockGraph((_stepId, result) => {
967
- if (result.type === 'summary') {
968
- summaryBlock = result.summary;
969
- }
970
- });
971
- const summarizeNode = createSummarizeNode({
972
- agentContext: createAgentContext({
973
- summarizationConfig: { retainRecent: { turns } },
974
- } as never),
975
- graph: graph as never,
976
- generateStepId,
977
- });
978
-
979
- await summarizeNode(
980
- {
981
- messages,
982
- summarizationRequest: {
983
- remainingContextTokens: 0,
984
- agentId: 'agent_0',
985
- },
986
- },
987
- {} as RunnableConfig
988
- );
989
-
990
- return summaryBlock;
991
- };
992
-
993
- it('records the first retained message as the coverage anchor', async () => {
994
- const summaryBlock = await runCompaction([
995
- new HumanMessage({ content: 'turn 1 query', id: 'm1' }),
996
- new AIMessage({ content: 'turn 1 reply', id: 'm2' }),
997
- new HumanMessage({ content: 'turn 2 query', id: 'm3' }),
998
- new AIMessage({ content: 'turn 2 reply', id: 'm4' }),
999
- ]);
1000
-
1001
- expect(summaryBlock?.coverage).toEqual({ retainedFromMessageId: 'm3' });
1002
- });
1003
-
1004
- it('skips a retained message that carries no source id', async () => {
1005
- const summaryBlock = await runCompaction([
1006
- new HumanMessage({ content: 'turn 1 query', id: 'm1' }),
1007
- new AIMessage({ content: 'turn 1 reply', id: 'm2' }),
1008
- new HumanMessage({ content: 'turn 2 query' }),
1009
- new AIMessage({ content: 'turn 2 reply', id: 'm4' }),
1010
- ]);
1011
-
1012
- expect(summaryBlock?.coverage).toEqual({ retainedFromMessageId: 'm4' });
1013
- });
1014
-
1015
- it('omits coverage when no retained message carries a source id', async () => {
1016
- const summaryBlock = await runCompaction([
1017
- new HumanMessage('turn 1 query'),
1018
- new AIMessage('turn 1 reply'),
1019
- new HumanMessage('turn 2 query'),
1020
- new AIMessage('turn 2 reply'),
1021
- ]);
1022
-
1023
- expect(summaryBlock?.coverage).toBeUndefined();
1024
- });
1025
-
1026
- /** A steer expands one source message into pre-steer, steer, and post-steer
1027
- * messages sharing its ID, and the recency split lands on the steer. The
1028
- * straddling message is the anchor, so it survives whole. */
1029
- it('anchors on a source id that straddles the recency boundary', async () => {
1030
- const summaryBlock = await runCompaction([
1031
- new HumanMessage({ content: 'turn 1 query', id: 'm1' }),
1032
- new AIMessage({ content: 'pre-steer reply', id: 'm2' }),
1033
- new HumanMessage({
1034
- content: 'steer',
1035
- id: 'm2',
1036
- additional_kwargs: { role: 'user', source: 'steer' },
1037
- }),
1038
- new AIMessage({ content: 'post-steer reply', id: 'm2' }),
1039
- ]);
1040
-
1041
- expect(summaryBlock?.coverage).toEqual({ retainedFromMessageId: 'm2' });
1042
- });
1043
-
1044
- /** A steer carries `source: 'steer'` but is replayed from a payload entry
1045
- * and stamped with its ID, so it is a valid anchor. When compaction lands
1046
- * before any post-steer message exists it is the *only* retained entry —
1047
- * treating every marked message as synthetic drops it. */
1048
- it('anchors on a retained steer with no post-steer message', async () => {
1049
- const summaryBlock = await runCompaction([
1050
- new HumanMessage({ content: 'turn 1 query', id: 'm1' }),
1051
- new AIMessage({ content: 'pre-steer reply', id: 'm2' }),
1052
- new HumanMessage({
1053
- content: 'steer',
1054
- id: 'm2',
1055
- additional_kwargs: { role: 'user', source: 'steer' },
1056
- }),
1057
- ]);
1058
-
1059
- expect(summaryBlock?.coverage).toEqual({ retainedFromMessageId: 'm2' });
1060
- });
1061
-
1062
- it('anchors a derived retained message on its persisted source id', async () => {
1063
- const summaryBlock = await runCompaction([
1064
- new HumanMessage({ content: 'turn 1 query', id: 'm1' }),
1065
- new AIMessage({ content: 'pre-steer reply', id: 'm2' }),
1066
- new HumanMessage({
1067
- content: 'steer',
1068
- id: 'reducer-uuid',
1069
- additional_kwargs: {
1070
- role: 'user',
1071
- source: 'steer',
1072
- sourceMessageId: 'm2',
1073
- },
1074
- }),
1075
- ]);
1076
-
1077
- expect(summaryBlock?.coverage).toEqual({ retainedFromMessageId: 'm2' });
1078
- });
1079
-
1080
- /** `formatAgentMessages` reconstructs skill bodies inside its payload loop
1081
- * and keeps processing payload entries after, so this unstamped entry — a
1082
- * reducer UUID by the time compaction sees it — precedes stamped messages.
1083
- * Anchoring on it would resolve to nothing on the next run. */
1084
- it('skips a reconstructed skill body to reach the stamped message behind it', async () => {
1085
- const summaryBlock = await runCompaction(
1086
- [
1087
- new HumanMessage({ content: 'turn 1 query', id: 'm1' }),
1088
- new AIMessage({ content: 'turn 1 reply', id: 'm2' }),
1089
- new HumanMessage({
1090
- content: 'skill body',
1091
- id: 'reducer-uuid',
1092
- additional_kwargs: {
1093
- role: 'user',
1094
- isMeta: true,
1095
- source: 'skill',
1096
- skillName: 'demo',
1097
- },
1098
- }),
1099
- new HumanMessage({ content: 'turn 2 query', id: 'm3' }),
1100
- new AIMessage({ content: 'turn 2 reply', id: 'm4' }),
1101
- ],
1102
- 2
1103
- );
1104
-
1105
- expect(summaryBlock?.coverage).toEqual({ retainedFromMessageId: 'm3' });
1106
- });
1107
-
1108
- /** `InjectedMessage` leaves both `isMeta` and `source` optional, so a bare
1109
- * injected turn carries no marker of its own — and an injected `steer` is
1110
- * otherwise indistinguishable from a replayed one. `convertInjectedMessages`
1111
- * records `injected` on everything it builds, which decides both. */
1112
- it.each([
1113
- ['a bare injected turn', { role: 'user' as const, content: 'injected' }],
1114
- [
1115
- 'an injected steer',
1116
- {
1117
- role: 'user' as const,
1118
- content: 'injected steer',
1119
- source: 'steer' as const,
1120
- },
1121
- ],
1122
- ])('skips %s when anchoring', async (_label, injected) => {
1123
- const [converted] = convertInjectedMessages([injected]);
1124
- converted.id = 'reducer-uuid';
1125
-
1126
- const summaryBlock = await runCompaction(
1127
- [
1128
- new HumanMessage({ content: 'turn 1 query', id: 'm1' }),
1129
- new AIMessage({ content: 'turn 1 reply', id: 'm2' }),
1130
- converted,
1131
- new HumanMessage({ content: 'turn 2 query', id: 'm3' }),
1132
- new AIMessage({ content: 'turn 2 reply', id: 'm4' }),
1133
- ],
1134
- 2
1135
- );
1136
-
1137
- expect(summaryBlock?.coverage).toEqual({ retainedFromMessageId: 'm3' });
1138
- });
1139
-
1140
- it('anchors on the straddling id when it is the only source', async () => {
1141
- const summaryBlock = await runCompaction([
1142
- new AIMessage({ content: 'pre-steer reply', id: 'm1' }),
1143
- new HumanMessage({
1144
- content: 'steer',
1145
- id: 'm1',
1146
- additional_kwargs: { role: 'user', source: 'steer' },
1147
- }),
1148
- new AIMessage({ content: 'post-steer reply', id: 'm1' }),
1149
- ]);
1150
-
1151
- expect(summaryBlock?.coverage).toEqual({ retainedFromMessageId: 'm1' });
1152
- });
1153
- });
1154
-
1155
- it('keeps the masked tail content (does not re-inject restored tool payloads into state)', async () => {
1156
- captureEvents();
1157
-
1158
- let summarizerSawRestored = false;
1159
- const invokeMock = jest.fn().mockImplementation((messages: unknown) => {
1160
- const arr = messages as Array<{
1161
- getType: () => string;
1162
- content: unknown;
1163
- tool_call_id?: string;
1164
- }>;
1165
- // Confirm the summarizer's input for the restored tool result has
1166
- // the FULL content, not the masked stub.
1167
- summarizerSawRestored = arr.some(
1168
- (m) =>
1169
- m.getType() === 'tool' &&
1170
- typeof m.content === 'string' &&
1171
- (m.content as string).includes('FULL_ORIGINAL_OUTPUT')
1172
- );
1173
- return Promise.resolve({ content: 'summary' });
1174
- });
1175
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1176
- class {
1177
- constructor() {
1178
- return { invoke: invokeMock };
1179
- }
1180
- } as never
1181
- );
1182
-
1183
- const setSummary = jest.fn();
1184
- const agentContext = createAgentContext({
1185
- summarizationConfig: { retainRecent: { turns: 1 } },
1186
- setSummary,
1187
- } as never);
1188
- // Restoration map keyed by message index — applies to head (idx 2)
1189
- // AND to a tool message inside the retained tail (idx 5). Only the
1190
- // head's restoration should leak into the summarizer; the tail must
1191
- // keep the masked content.
1192
- agentContext.pendingOriginalToolContent = new Map<number, string>([
1193
- [2, 'FULL_ORIGINAL_OUTPUT for head tool result'],
1194
- [5, 'FULL_ORIGINAL_OUTPUT for tail tool result — must NOT survive'],
1195
- ]);
1196
-
1197
- const graph = mockGraph();
1198
- const summarizeNode = createSummarizeNode({
1199
- agentContext,
1200
- graph: graph as never,
1201
- generateStepId,
1202
- });
1203
-
1204
- const headToolCall = new AIMessage({
1205
- content: '',
1206
- tool_calls: [{ id: 'h', name: 'search', args: {} }],
1207
- });
1208
- const headToolResult = new ToolMessage({
1209
- content: 'masked-head-stub',
1210
- tool_call_id: 'h',
1211
- name: 'search',
1212
- });
1213
- const tailToolCall = new AIMessage({
1214
- content: '',
1215
- tool_calls: [{ id: 't', name: 'search', args: {} }],
1216
- });
1217
- const tailToolResult = new ToolMessage({
1218
- content: 'masked-tail-stub',
1219
- tool_call_id: 't',
1220
- name: 'search',
1221
- });
1222
- const messages = [
1223
- new HumanMessage('turn 1 query'),
1224
- headToolCall,
1225
- headToolResult, // index 2 — restored for summarizer
1226
- new HumanMessage('turn 2 query'),
1227
- tailToolCall,
1228
- tailToolResult, // index 5 — must stay masked in returned tail
1229
- ];
1230
-
1231
- const result = await summarizeNode(
1232
- {
1233
- messages,
1234
- summarizationRequest: {
1235
- remainingContextTokens: 0,
1236
- agentId: 'agent_0',
1237
- },
1238
- },
1239
- {} as RunnableConfig
1240
- );
1241
-
1242
- // Summarizer saw the full restored head tool result.
1243
- expect(summarizerSawRestored).toBe(true);
1244
-
1245
- // Returned tail must contain the MASKED tool result, not the restored one.
1246
- expect(result.messages).toBeDefined();
1247
- const tailToolMsg = result.messages!.find(
1248
- (m) => m._getType() === 'tool'
1249
- ) as ToolMessage | undefined;
1250
- expect(tailToolMsg).toBeDefined();
1251
- expect(tailToolMsg!.content).toBe('masked-tail-stub');
1252
- expect(tailToolMsg!.content).not.toContain('FULL_ORIGINAL_OUTPUT');
1253
- });
1254
-
1255
- it('bounds restored tool originals and preserves ToolMessage metadata', async () => {
1256
- captureEvents();
1257
-
1258
- let restoredToolMessage: ToolMessage | undefined;
1259
- const invokeMock = jest.fn().mockImplementation((messages: unknown) => {
1260
- restoredToolMessage = (messages as Array<unknown>).find(
1261
- (message) => message instanceof ToolMessage
1262
- ) as ToolMessage | undefined;
1263
- return Promise.resolve({ content: 'summary' });
1264
- });
1265
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1266
- class {
1267
- constructor() {
1268
- return { invoke: invokeMock };
1269
- }
1270
- } as never
1271
- );
1272
-
1273
- const artifact = { source: 'clickhouse', rows: 1_000 };
1274
- const originalToolMessage = new ToolMessage({
1275
- content: 'masked-stub',
1276
- tool_call_id: 'bounded',
1277
- name: 'run_select_query',
1278
- status: 'success',
1279
- artifact,
1280
- metadata: { traceId: 'trace-1' },
1281
- additional_kwargs: { retained: true },
1282
- response_metadata: { requestId: 'request-1' },
1283
- });
1284
- const agentContext = createAgentContext({
1285
- maxContextTokens: 2_000,
1286
- summarizationConfig: { retainRecent: { turns: 0 } },
1287
- setSummary: jest.fn(),
1288
- });
1289
- agentContext.pendingOriginalToolContent = new Map([
1290
- [2, `[{"rows":"${'x'.repeat(20_000)}"}]`],
1291
- ]);
1292
-
1293
- const summarizeNode = createSummarizeNode({
1294
- agentContext,
1295
- graph: mockGraph() as never,
1296
- generateStepId,
1297
- });
1298
- await summarizeNode(
1299
- {
1300
- messages: [
1301
- new HumanMessage('query the table'),
1302
- new AIMessage({
1303
- content: '',
1304
- tool_calls: [{ id: 'bounded', name: 'run_select_query', args: {} }],
1305
- }),
1306
- originalToolMessage,
1307
- ],
1308
- summarizationRequest: {
1309
- remainingContextTokens: 0,
1310
- agentId: 'agent_0',
1311
- },
1312
- },
1313
- {} as RunnableConfig
1314
- );
1315
-
1316
- expect(restoredToolMessage).toBeDefined();
1317
- expect(typeof restoredToolMessage!.content).toBe('string');
1318
- expect(String(restoredToolMessage!.content).length).toBeLessThanOrEqual(
1319
- 2_400
1320
- );
1321
- expect(restoredToolMessage!.content).toContain('[truncated:');
1322
- expect(restoredToolMessage!.status).toBe('success');
1323
- expect(restoredToolMessage!.artifact).toBe(artifact);
1324
- expect(restoredToolMessage!.metadata).toEqual({ traceId: 'trace-1' });
1325
- expect(restoredToolMessage!.additional_kwargs).toEqual({ retained: true });
1326
- expect(restoredToolMessage!.response_metadata).toEqual({
1327
- requestId: 'request-1',
1328
- });
1329
- expect(originalToolMessage.content).toBe('masked-stub');
1330
- });
1331
-
1332
- it('preserves tail-relevant pendingOriginalToolContent entries (reindexed) for future summaries', async () => {
1333
- captureEvents();
1334
-
1335
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1336
- class {
1337
- constructor() {
1338
- return mockInvokeModel('summary');
1339
- }
1340
- } as never
1341
- );
1342
-
1343
- const setSummary = jest.fn();
1344
- const agentContext = createAgentContext({
1345
- summarizationConfig: { retainRecent: { turns: 1 } },
1346
- setSummary,
1347
- } as never);
1348
- // Original-content map covers BOTH a head index (1) and tail indices
1349
- // (3, 5). Only the tail entries should survive, reindexed to the
1350
- // post-removeAll state where tail messages start at 0.
1351
- agentContext.pendingOriginalToolContent = new Map<number, string>([
1352
- [1, 'fullHead'], // belongs to summarized head — should be dropped
1353
- [3, 'fullTailA'], // tail position 3 → reindexed to 0
1354
- [5, 'fullTailB'], // tail position 5 → reindexed to 2
1355
- ]);
1356
-
1357
- const graph = mockGraph();
1358
- const summarizeNode = createSummarizeNode({
1359
- agentContext,
1360
- graph: graph as never,
1361
- generateStepId,
1362
- });
1363
-
1364
- const messages = [
1365
- new HumanMessage('turn 1 query'),
1366
- new AIMessage('turn 1 reply'), // idx 1
1367
- new HumanMessage('turn 2 query'), // idx 2 — tail starts here
1368
- new ToolMessage({
1369
- // idx 3
1370
- content: 'masked-stub-A',
1371
- tool_call_id: 'a',
1372
- name: 'search',
1373
- }),
1374
- new AIMessage('turn 2 reply'), // idx 4
1375
- new ToolMessage({
1376
- // idx 5
1377
- content: 'masked-stub-B',
1378
- tool_call_id: 'b',
1379
- name: 'search',
1380
- }),
1381
- ];
1382
-
1383
- await summarizeNode(
1384
- {
1385
- messages,
1386
- summarizationRequest: {
1387
- remainingContextTokens: 0,
1388
- agentId: 'agent_0',
1389
- },
1390
- },
1391
- {} as RunnableConfig
1392
- );
1393
-
1394
- // Summarize fired (we used a real summary text), so head index 1
1395
- // is gone. Tail entries should remain, reindexed by subtracting
1396
- // tailStartIndex (=2): 3→1, 5→3.
1397
- const carryOver = agentContext.pendingOriginalToolContent;
1398
- expect(carryOver).toBeDefined();
1399
- expect(carryOver!.size).toBe(2);
1400
- expect(carryOver!.get(1)).toBe('fullTailA');
1401
- expect(carryOver!.get(3)).toBe('fullTailB');
1402
- expect(carryOver!.has(0)).toBe(false);
1403
- expect(carryOver!.has(5)).toBe(false);
1404
- });
1405
-
1406
- it('aligns the dedupe baseline with the surviving tail length after compaction', async () => {
1407
- captureEvents();
1408
-
1409
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1410
- class {
1411
- constructor() {
1412
- return mockInvokeModel('summary');
1413
- }
1414
- } as never
1415
- );
1416
-
1417
- const agentContext = createAgentContext({
1418
- summarizationConfig: { retainRecent: { turns: 1 } },
1419
- } as never);
1420
- const graph = mockGraph();
1421
- const summarizeNode = createSummarizeNode({
1422
- agentContext,
1423
- graph: graph as never,
1424
- generateStepId,
1425
- });
1426
-
1427
- const messages = [
1428
- new HumanMessage('turn 1 query'),
1429
- new AIMessage('turn 1 reply'),
1430
- new HumanMessage('turn 2 query'),
1431
- new AIMessage('turn 2 reply'),
1432
- ];
1433
-
1434
- await summarizeNode(
1435
- {
1436
- messages,
1437
- summarizationRequest: {
1438
- remainingContextTokens: 0,
1439
- agentId: 'agent_0',
1440
- },
1441
- },
1442
- {} as RunnableConfig
1443
- );
1444
-
1445
- // Tail = last turn = 2 messages. Baseline must equal that count
1446
- // so a follow-up prune call on the unchanged tail short-circuits
1447
- // via shouldSkipSummarization rather than re-triggering compaction.
1448
- expect(agentContext.shouldSkipSummarization(2)).toBe(true);
1449
- expect(agentContext.shouldSkipSummarization(3)).toBe(false);
1450
- });
1451
-
1452
- it('does not clear pendingOriginalToolContent on the skip path (state unchanged)', async () => {
1453
- captureEvents();
1454
-
1455
- const invokeMock = jest.fn();
1456
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1457
- class {
1458
- constructor() {
1459
- return { invoke: invokeMock };
1460
- }
1461
- } as never
1462
- );
1463
-
1464
- const agentContext = createAgentContext({
1465
- summarizationConfig: { retainRecent: { turns: 2 } },
1466
- } as never);
1467
- const seededMap = new Map<number, string>([[1, 'preserved-original']]);
1468
- agentContext.pendingOriginalToolContent = seededMap;
1469
-
1470
- const graph = mockGraph();
1471
- const summarizeNode = createSummarizeNode({
1472
- agentContext,
1473
- graph: graph as never,
1474
- generateStepId,
1475
- });
1476
-
1477
- await summarizeNode(
1478
- {
1479
- messages: [
1480
- new HumanMessage('only turn'),
1481
- new ToolMessage({
1482
- content: 'masked',
1483
- tool_call_id: 'x',
1484
- name: 'search',
1485
- }),
1486
- ],
1487
- summarizationRequest: {
1488
- remainingContextTokens: 0,
1489
- agentId: 'agent_0',
1490
- },
1491
- },
1492
- {} as RunnableConfig
1493
- );
1494
-
1495
- // No LLM call (skip path); pendingOriginalToolContent must still be
1496
- // available so a future summarization can restore the original.
1497
- expect(invokeMock).not.toHaveBeenCalled();
1498
- expect(agentContext.pendingOriginalToolContent).toBe(seededMap);
1499
- expect(agentContext.pendingOriginalToolContent!.get(1)).toBe(
1500
- 'preserved-original'
1501
- );
1502
- });
1503
-
1504
- it('preserves the legacy "remove all, summary only" shape when retainRecent.turns is 0', async () => {
1505
- captureEvents();
1506
-
1507
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1508
- class {
1509
- constructor() {
1510
- return mockInvokeModel('Legacy summary');
1511
- }
1512
- } as never
1513
- );
1514
-
1515
- const setSummary = jest.fn();
1516
- const agentContext = createAgentContext({
1517
- summarizationConfig: { retainRecent: { turns: 0 } },
1518
- setSummary,
1519
- } as never);
1520
- const graph = mockGraph();
1521
- const summarizeNode = createSummarizeNode({
1522
- agentContext,
1523
- graph: graph as never,
1524
- generateStepId,
1525
- });
1526
-
1527
- const result = await summarizeNode(
1528
- {
1529
- messages: [
1530
- new HumanMessage('only message'),
1531
- new AIMessage('only reply'),
1532
- ],
1533
- summarizationRequest: {
1534
- remainingContextTokens: 0,
1535
- agentId: 'agent_0',
1536
- },
1537
- },
1538
- {} as RunnableConfig
1539
- );
1540
-
1541
- expect(setSummary).toHaveBeenCalled();
1542
- // Legacy: remove-all only, no tail re-injection.
1543
- expect(result.messages).toHaveLength(1);
1544
- expect(result.messages![0]?._getType()).toBe('remove');
1545
- });
1546
- });
1547
-
1548
- describe('emoji-heavy content does not break summarization', () => {
1549
- it('summarization completes without JSON errors on emoji-heavy messages', async () => {
1550
- captureEvents();
1551
-
1552
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1553
- class {
1554
- constructor() {
1555
- return mockInvokeModel('Summary of emoji conversation');
1556
- }
1557
- } as never
1558
- );
1559
-
1560
- const emojiContent = '👨‍💻 coding 🎉 party 🌍 world 🚀 rocket '.repeat(30);
1561
- const agentContext = createAgentContext({
1562
- maxContextTokens: 8000,
1563
- systemMessageTokens: 100,
1564
- formatTokenBudgetBreakdown: () => 'mock breakdown',
1565
- });
1566
-
1567
- const graph = mockGraph();
1568
- const summarizeNode = createSummarizeNode({
1569
- agentContext,
1570
- graph: graph as never,
1571
- generateStepId,
1572
- });
1573
-
1574
- const result = await summarizeNode(
1575
- {
1576
- messages: [new HumanMessage(emojiContent)],
1577
- summarizationRequest: {
1578
- remainingContextTokens: 500,
1579
- agentId: 'agent_0',
1580
- },
1581
- },
1582
- {} as RunnableConfig
1583
- );
1584
-
1585
- // Should complete without throwing JSON serialization errors
1586
- expect(result.messages).toBeDefined();
1587
- expect(result.messages!.length).toBeGreaterThan(0);
1588
- });
1589
- });
1590
-
1591
- describe('createSummarizeNode — overflow recovery', () => {
1592
- /**
1593
- * The first recovery compacts deterministically: re-pruning under the
1594
- * corrected budget drives the pruner's tool-output compression. Spending a
1595
- * summarization call there would cost money and risk replacing message
1596
- * content that compression alone can shrink.
1597
- */
1598
- it('skips the model call when summarization is not yet escalated', async () => {
1599
- const modelFactory = jest.spyOn(providers, 'getChatModelClass');
1600
- const agentContext = createAgentContext();
1601
- const graph = mockGraph(() => {});
1602
- const node = createSummarizeNode({ agentContext, graph, generateStepId });
1603
-
1604
- const result = await node(
1605
- {
1606
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
1607
- summarizationRequest: {
1608
- remainingContextTokens: 0,
1609
- agentId: 'agent_0',
1610
- reason: 'overflow',
1611
- allowSummarization: false,
1612
- },
1613
- },
1614
- {} as RunnableConfig
1615
- );
1616
-
1617
- expect(modelFactory).not.toHaveBeenCalled();
1618
- expect(result.summarizationRequest).toBeUndefined();
1619
- /** State untouched, so the agent node re-prunes and retries. */
1620
- expect(result.messages).toBeUndefined();
1621
- });
1622
-
1623
- it('runs the model call once the recovery escalates', async () => {
1624
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1625
- class {
1626
- constructor() {
1627
- return mockInvokeModel('Escalated summary');
1628
- }
1629
- } as never
1630
- );
1631
- const agentContext = createAgentContext();
1632
- const graph = mockGraph(() => {});
1633
- const node = createSummarizeNode({ agentContext, graph, generateStepId });
1634
-
1635
- await node(
1636
- {
1637
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
1638
- summarizationRequest: {
1639
- remainingContextTokens: 0,
1640
- agentId: 'agent_0',
1641
- reason: 'overflow',
1642
- allowSummarization: true,
1643
- },
1644
- },
1645
- {} as RunnableConfig
1646
- );
1647
-
1648
- expect(agentContext.getSummaryText()).toContain('Escalated summary');
1649
- });
1650
-
1651
- /**
1652
- * The metadata stub describes the history instead of summarizing it, so
1653
- * committing it removes the head and keeps nothing of what it said. That is
1654
- * never an acceptable way to paper over an overflow.
1655
- */
1656
- it('keeps history when the escalated summarizer falls back to a stub', async () => {
1657
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1658
- class {
1659
- constructor() {
1660
- return {
1661
- invoke: (): never => {
1662
- throw new Error(
1663
- '400 prompt is too long: 900 tokens > 500 maximum'
1664
- );
1665
- },
1666
- stream: (): never => {
1667
- throw new Error(
1668
- '400 prompt is too long: 900 tokens > 500 maximum'
1669
- );
1670
- },
1671
- };
1672
- }
1673
- } as never
1674
- );
1675
- const agentContext = createAgentContext();
1676
- const graph = mockGraph(() => {});
1677
- const node = createSummarizeNode({ agentContext, graph, generateStepId });
1678
-
1679
- const result = await node(
1680
- {
1681
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
1682
- summarizationRequest: {
1683
- remainingContextTokens: 0,
1684
- agentId: 'agent_0',
1685
- reason: 'overflow',
1686
- allowSummarization: true,
1687
- },
1688
- },
1689
- {} as RunnableConfig
1690
- );
1691
-
1692
- expect(agentContext.hasSummary()).toBe(false);
1693
- expect(result.messages).toBeUndefined();
1694
- });
1695
-
1696
- it('still commits a stub for a configured trigger, preserving prior behavior', async () => {
1697
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1698
- class {
1699
- constructor() {
1700
- return {
1701
- invoke: (): never => {
1702
- throw new Error('upstream unavailable');
1703
- },
1704
- stream: (): never => {
1705
- throw new Error('upstream unavailable');
1706
- },
1707
- };
1708
- }
1709
- } as never
1710
- );
1711
- const agentContext = createAgentContext();
1712
- const graph = mockGraph(() => {});
1713
- const node = createSummarizeNode({ agentContext, graph, generateStepId });
1714
-
1715
- await node(
1716
- {
1717
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
1718
- summarizationRequest: {
1719
- remainingContextTokens: 0,
1720
- agentId: 'agent_0',
1721
- },
1722
- },
1723
- {} as RunnableConfig
1724
- );
1725
-
1726
- expect(agentContext.hasSummary()).toBe(true);
1727
- });
1728
- });
1729
-
1730
- describe('summarize node breaker capture', () => {
1731
- it('stamps the entry-captured breaker epoch into summary attempt metadata', async () => {
1732
- captureEvents();
1733
- const capturedConfigs: Array<{ metadata?: Record<string, unknown> }> = [];
1734
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1735
- class {
1736
- constructor() {
1737
- return {
1738
- invoke: jest
1739
- .fn()
1740
- .mockImplementation(
1741
- async (_messages: unknown, config?: unknown) => {
1742
- capturedConfigs.push(
1743
- config as { metadata?: Record<string, unknown> }
1744
- );
1745
- return { content: 'Summary text' };
1746
- }
1747
- ),
1748
- };
1749
- }
1750
- } as never
1751
- );
1752
-
1753
- const agentContext = createAgentContext();
1754
- const graph = {
1755
- ...mockGraph(),
1756
- getBreakerEpoch: (): number => 7,
1757
- };
1758
- const node = createSummarizeNode({
1759
- agentContext,
1760
- graph,
1761
- generateStepId,
1762
- });
1763
-
1764
- await node(
1765
- {
1766
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
1767
- summarizationRequest: {
1768
- remainingContextTokens: 1000,
1769
- agentId: 'agent_0',
1770
- },
1771
- },
1772
- {} as RunnableConfig
1773
- );
1774
-
1775
- expect(capturedConfigs.length).toBeGreaterThan(0);
1776
- for (const config of capturedConfigs) {
1777
- expect(config.metadata?.lc_stream_limit_epoch).toBe(7);
1778
- }
1779
- });
1780
-
1781
- it('rejects before the model call when the breaker trips during pre-call awaits', async () => {
1782
- const trip = new StreamLimitExceededError({
1783
- kind: 'tool_call_args',
1784
- limit: 10,
1785
- observed: 11,
1786
- toolName: 'db_query',
1787
- });
1788
- const entryBreaker = new AbortController();
1789
- /** The trip lands while ON_SUMMARIZE_START is awaited — after the
1790
- * entry check already passed. */
1791
- jest
1792
- .spyOn(eventUtils, 'safeDispatchCustomEvent')
1793
- .mockImplementation((async (...args: unknown[]) => {
1794
- if (args[0] === GraphEvents.ON_SUMMARIZE_START) {
1795
- entryBreaker.abort(trip);
1796
- }
1797
- }) as never);
1798
- const modelClassSpy = jest
1799
- .spyOn(providers, 'getChatModelClass')
1800
- .mockReturnValue(
1801
- class {
1802
- constructor() {
1803
- return mockInvokeModel('should never run');
1804
- }
1805
- } as never
1806
- );
1807
-
1808
- const agentContext = createAgentContext();
1809
- const graph = {
1810
- ...mockGraph(),
1811
- getBreakerSignal: (): AbortSignal => entryBreaker.signal,
1812
- };
1813
- const node = createSummarizeNode({
1814
- agentContext,
1815
- graph,
1816
- generateStepId,
1817
- });
1818
-
1819
- await expect(
1820
- node(
1821
- {
1822
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
1823
- summarizationRequest: {
1824
- remainingContextTokens: 1000,
1825
- agentId: 'agent_0',
1826
- },
1827
- },
1828
- {} as RunnableConfig
1829
- )
1830
- ).rejects.toBe(trip);
1831
- expect(modelClassSpy).not.toHaveBeenCalled();
1832
- });
1833
-
1834
- it('rethrows a parent trip on the config signal instead of degrading to the stub', async () => {
1835
- const trip = new StreamLimitExceededError({
1836
- kind: 'tool_call_args',
1837
- limit: 10,
1838
- observed: 11,
1839
- toolName: 'db_query',
1840
- });
1841
- /** Child graph's own breaker stays live: in a subagent, a ROOT
1842
- * sibling's trip arrives only through the composed invocation signal. */
1843
- const childBreaker = new AbortController();
1844
- const configAbort = new AbortController();
1845
- captureEvents();
1846
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1847
- class {
1848
- constructor() {
1849
- return {
1850
- invoke: jest.fn().mockImplementation(async () => {
1851
- configAbort.abort(trip);
1852
- throw new Error('The operation was aborted');
1853
- }),
1854
- };
1855
- }
1856
- } as never
1857
- );
1858
-
1859
- const agentContext = createAgentContext();
1860
- const graph = {
1861
- ...mockGraph(),
1862
- getBreakerSignal: (): AbortSignal => childBreaker.signal,
1863
- };
1864
- const node = createSummarizeNode({
1865
- agentContext,
1866
- graph,
1867
- generateStepId,
1868
- });
1869
-
1870
- await expect(
1871
- node(
1872
- {
1873
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
1874
- summarizationRequest: {
1875
- remainingContextTokens: 1000,
1876
- agentId: 'agent_0',
1877
- },
1878
- },
1879
- { signal: configAbort.signal } as RunnableConfig
1880
- )
1881
- ).rejects.toBe(trip);
1882
- });
1883
-
1884
- it('rejects at entry when the breaker has already tripped', async () => {
1885
- const trip = new StreamLimitExceededError({
1886
- kind: 'tool_call_args',
1887
- limit: 10,
1888
- observed: 11,
1889
- toolName: 'db_query',
1890
- });
1891
- const entryBreaker = new AbortController();
1892
- entryBreaker.abort(trip);
1893
-
1894
- const modelClassSpy = jest
1895
- .spyOn(providers, 'getChatModelClass')
1896
- .mockReturnValue(
1897
- class {
1898
- constructor() {
1899
- return mockInvokeModel('should never run');
1900
- }
1901
- } as never
1902
- );
1903
-
1904
- const agentContext = createAgentContext();
1905
- const graph = {
1906
- ...mockGraph(),
1907
- getBreakerSignal: (): AbortSignal => entryBreaker.signal,
1908
- };
1909
- const node = createSummarizeNode({
1910
- agentContext,
1911
- graph,
1912
- generateStepId,
1913
- });
1914
-
1915
- await expect(
1916
- node(
1917
- {
1918
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
1919
- summarizationRequest: {
1920
- remainingContextTokens: 1000,
1921
- agentId: 'agent_0',
1922
- },
1923
- },
1924
- {} as RunnableConfig
1925
- )
1926
- ).rejects.toBe(trip);
1927
-
1928
- expect(graph.contentData).toHaveLength(0);
1929
- expect(modelClassSpy).not.toHaveBeenCalled();
1930
- });
1931
-
1932
- it('binds the model call to the breaker signal read at node entry', async () => {
1933
- const entryBreaker = new AbortController();
1934
- const lateBreaker = new AbortController();
1935
- let started = false;
1936
- jest
1937
- .spyOn(eventUtils, 'safeDispatchCustomEvent')
1938
- .mockImplementation((async (...args: unknown[]) => {
1939
- if (args[0] === GraphEvents.ON_SUMMARIZE_START) {
1940
- started = true;
1941
- }
1942
- }) as never);
1943
-
1944
- const capturedSignals: Array<AbortSignal | undefined> = [];
1945
- jest.spyOn(providers, 'getChatModelClass').mockReturnValue(
1946
- class {
1947
- constructor() {
1948
- return {
1949
- invoke: jest
1950
- .fn()
1951
- .mockImplementation(
1952
- async (_messages: unknown, config?: unknown) => {
1953
- capturedSignals.push(
1954
- (config as RunnableConfig | undefined)?.signal
1955
- );
1956
- return { content: 'Summary text' };
1957
- }
1958
- ),
1959
- };
1960
- }
1961
- } as never
1962
- );
1963
-
1964
- const agentContext = createAgentContext();
1965
- /** Simulates a graph reset between node entry and the model call: once
1966
- * ON_SUMMARIZE_START has been awaited, the live accessor hands out a
1967
- * fresh controller's signal. The model call must still see the signal
1968
- * captured at entry. */
1969
- const graph = {
1970
- ...mockGraph(),
1971
- getBreakerSignal: (): AbortSignal =>
1972
- started ? lateBreaker.signal : entryBreaker.signal,
1973
- };
1974
- const node = createSummarizeNode({
1975
- agentContext,
1976
- graph,
1977
- generateStepId,
1978
- });
1979
-
1980
- await node(
1981
- {
1982
- messages: [new HumanMessage('Hello'), new HumanMessage('World')],
1983
- summarizationRequest: {
1984
- remainingContextTokens: 1000,
1985
- agentId: 'agent_0',
1986
- },
1987
- },
1988
- {} as RunnableConfig
1989
- );
1990
-
1991
- expect(capturedSignals.length).toBeGreaterThan(0);
1992
- for (const signal of capturedSignals) {
1993
- expect(signal).toBe(entryBreaker.signal);
1994
- }
1995
- });
1996
- });