@illuma-ai/agents 1.4.0-alpha.6 → 1.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 (653) hide show
  1. package/README.md +62 -0
  2. package/dist/cjs/agents/AgentContext.cjs +274 -67
  3. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  4. package/dist/cjs/common/enum.cjs +44 -13
  5. package/dist/cjs/common/enum.cjs.map +1 -1
  6. package/dist/cjs/graphs/Graph.cjs +182 -5
  7. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs +152 -1167
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  10. package/dist/cjs/graphs/phases/memoryFlushPhase.cjs +1 -1
  11. package/dist/cjs/graphs/phases/memoryFlushPhase.cjs.map +1 -1
  12. package/dist/cjs/hooks/HookRegistry.cjs +162 -0
  13. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -0
  14. package/dist/cjs/hooks/executeHooks.cjs +276 -0
  15. package/dist/cjs/hooks/executeHooks.cjs.map +1 -0
  16. package/dist/cjs/hooks/matchers.cjs +256 -0
  17. package/dist/cjs/hooks/matchers.cjs.map +1 -0
  18. package/dist/cjs/hooks/types.cjs +27 -0
  19. package/dist/cjs/hooks/types.cjs.map +1 -0
  20. package/dist/cjs/langchain/google-common.cjs +3 -0
  21. package/dist/cjs/langchain/google-common.cjs.map +1 -0
  22. package/dist/cjs/langchain/index.cjs +86 -0
  23. package/dist/cjs/langchain/index.cjs.map +1 -0
  24. package/dist/cjs/langchain/language_models/chat_models.cjs +3 -0
  25. package/dist/cjs/langchain/language_models/chat_models.cjs.map +1 -0
  26. package/dist/cjs/langchain/messages/tool.cjs +3 -0
  27. package/dist/cjs/langchain/messages/tool.cjs.map +1 -0
  28. package/dist/cjs/langchain/messages.cjs +51 -0
  29. package/dist/cjs/langchain/messages.cjs.map +1 -0
  30. package/dist/cjs/langchain/openai.cjs +3 -0
  31. package/dist/cjs/langchain/openai.cjs.map +1 -0
  32. package/dist/cjs/langchain/prompts.cjs +11 -0
  33. package/dist/cjs/langchain/prompts.cjs.map +1 -0
  34. package/dist/cjs/langchain/runnables.cjs +19 -0
  35. package/dist/cjs/langchain/runnables.cjs.map +1 -0
  36. package/dist/cjs/langchain/tools.cjs +23 -0
  37. package/dist/cjs/langchain/tools.cjs.map +1 -0
  38. package/dist/cjs/langchain/utils/env.cjs +11 -0
  39. package/dist/cjs/langchain/utils/env.cjs.map +1 -0
  40. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +5 -1
  41. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  42. package/dist/cjs/llm/bedrock/cacheSupport.cjs +55 -0
  43. package/dist/cjs/llm/bedrock/cacheSupport.cjs.map +1 -0
  44. package/dist/cjs/llm/bedrock/index.cjs +61 -33
  45. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  46. package/dist/cjs/llm/openai/index.cjs +1 -4
  47. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  48. package/dist/cjs/llm/openai/utils/index.cjs +27 -10
  49. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  50. package/dist/cjs/main.cjs +178 -127
  51. package/dist/cjs/main.cjs.map +1 -1
  52. package/dist/cjs/memory/citations.cjs +4 -4
  53. package/dist/cjs/memory/citations.cjs.map +1 -1
  54. package/dist/cjs/memory/constants.cjs +17 -17
  55. package/dist/cjs/memory/constants.cjs.map +1 -1
  56. package/dist/cjs/memory/mmr.cjs +1 -1
  57. package/dist/cjs/memory/mmr.cjs.map +1 -1
  58. package/dist/cjs/memory/paths.cjs +1 -1
  59. package/dist/cjs/memory/paths.cjs.map +1 -1
  60. package/dist/cjs/memory/recallTracking.cjs +3 -3
  61. package/dist/cjs/memory/recallTracking.cjs.map +1 -1
  62. package/dist/cjs/memory/temporalDecay.cjs +2 -2
  63. package/dist/cjs/memory/temporalDecay.cjs.map +1 -1
  64. package/dist/cjs/messages/cache.cjs +89 -0
  65. package/dist/cjs/messages/cache.cjs.map +1 -1
  66. package/dist/cjs/messages/contextPruning.cjs +156 -0
  67. package/dist/cjs/messages/contextPruning.cjs.map +1 -0
  68. package/dist/cjs/messages/contextPruningSettings.cjs +53 -0
  69. package/dist/cjs/messages/contextPruningSettings.cjs.map +1 -0
  70. package/dist/cjs/messages/format.cjs +144 -20
  71. package/dist/cjs/messages/format.cjs.map +1 -1
  72. package/dist/cjs/messages/prune.cjs +505 -4
  73. package/dist/cjs/messages/prune.cjs.map +1 -1
  74. package/dist/cjs/run.cjs +141 -1
  75. package/dist/cjs/run.cjs.map +1 -1
  76. package/dist/cjs/tools/BashExecutor.cjs +235 -0
  77. package/dist/cjs/tools/BashExecutor.cjs.map +1 -0
  78. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +297 -0
  79. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -0
  80. package/dist/cjs/tools/CodeExecutor.cjs +45 -47
  81. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  82. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +16 -11
  83. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  84. package/dist/cjs/tools/ReadFile.cjs +44 -0
  85. package/dist/cjs/tools/ReadFile.cjs.map +1 -0
  86. package/dist/cjs/tools/SkillTool.cjs +51 -0
  87. package/dist/cjs/tools/SkillTool.cjs.map +1 -0
  88. package/dist/cjs/tools/SubagentTool.cjs +93 -0
  89. package/dist/cjs/tools/SubagentTool.cjs.map +1 -0
  90. package/dist/cjs/tools/ToolNode.cjs +450 -24
  91. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  92. package/dist/cjs/tools/memory/memoryAppendTool.cjs +1 -1
  93. package/dist/cjs/tools/memory/memoryAppendTool.cjs.map +1 -1
  94. package/dist/cjs/tools/memory/memoryGetTool.cjs +2 -2
  95. package/dist/cjs/tools/memory/memoryGetTool.cjs.map +1 -1
  96. package/dist/cjs/tools/memory/memorySearchTool.cjs +3 -3
  97. package/dist/cjs/tools/memory/memorySearchTool.cjs.map +1 -1
  98. package/dist/cjs/tools/memory/shared.cjs +1 -1
  99. package/dist/cjs/tools/memory/shared.cjs.map +1 -1
  100. package/dist/cjs/tools/search/search.cjs +11 -3
  101. package/dist/cjs/tools/search/search.cjs.map +1 -1
  102. package/dist/cjs/tools/search/tavily-scraper.cjs +189 -0
  103. package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -0
  104. package/dist/cjs/tools/search/tavily-search.cjs +372 -0
  105. package/dist/cjs/tools/search/tavily-search.cjs.map +1 -0
  106. package/dist/cjs/tools/search/tool.cjs +28 -4
  107. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  108. package/dist/cjs/tools/search/utils.cjs +10 -3
  109. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  110. package/dist/cjs/tools/skillCatalog.cjs +84 -0
  111. package/dist/cjs/tools/skillCatalog.cjs.map +1 -0
  112. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +512 -0
  113. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -0
  114. package/dist/cjs/tools/toolOutputReferences.cjs +670 -0
  115. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -0
  116. package/dist/cjs/types/agent-cache.cjs +54 -0
  117. package/dist/cjs/types/agent-cache.cjs.map +1 -0
  118. package/dist/cjs/types/graph.cjs.map +1 -1
  119. package/dist/cjs/utils/truncation.cjs +135 -0
  120. package/dist/cjs/utils/truncation.cjs.map +1 -0
  121. package/dist/esm/agents/AgentContext.mjs +274 -67
  122. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  123. package/dist/esm/common/enum.mjs +44 -12
  124. package/dist/esm/common/enum.mjs.map +1 -1
  125. package/dist/esm/graphs/Graph.mjs +182 -5
  126. package/dist/esm/graphs/Graph.mjs.map +1 -1
  127. package/dist/esm/graphs/MultiAgentGraph.mjs +155 -1170
  128. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  129. package/dist/esm/graphs/phases/memoryFlushPhase.mjs +1 -1
  130. package/dist/esm/graphs/phases/memoryFlushPhase.mjs.map +1 -1
  131. package/dist/esm/hooks/HookRegistry.mjs +160 -0
  132. package/dist/esm/hooks/HookRegistry.mjs.map +1 -0
  133. package/dist/esm/hooks/executeHooks.mjs +273 -0
  134. package/dist/esm/hooks/executeHooks.mjs.map +1 -0
  135. package/dist/esm/hooks/matchers.mjs +251 -0
  136. package/dist/esm/hooks/matchers.mjs.map +1 -0
  137. package/dist/esm/hooks/types.mjs +25 -0
  138. package/dist/esm/hooks/types.mjs.map +1 -0
  139. package/dist/esm/langchain/google-common.mjs +2 -0
  140. package/dist/esm/langchain/google-common.mjs.map +1 -0
  141. package/dist/esm/langchain/index.mjs +5 -0
  142. package/dist/esm/langchain/language_models/chat_models.mjs +2 -0
  143. package/dist/esm/langchain/language_models/chat_models.mjs.map +1 -0
  144. package/dist/esm/langchain/messages/tool.mjs +2 -0
  145. package/dist/esm/langchain/messages/tool.mjs.map +1 -0
  146. package/dist/esm/langchain/messages.mjs +2 -0
  147. package/dist/esm/langchain/messages.mjs.map +1 -0
  148. package/dist/esm/langchain/openai.mjs +2 -0
  149. package/dist/esm/langchain/openai.mjs.map +1 -0
  150. package/dist/esm/langchain/prompts.mjs +2 -0
  151. package/dist/esm/langchain/prompts.mjs.map +1 -0
  152. package/dist/esm/langchain/runnables.mjs +2 -0
  153. package/dist/esm/langchain/runnables.mjs.map +1 -0
  154. package/dist/esm/langchain/tools.mjs +2 -0
  155. package/dist/esm/langchain/tools.mjs.map +1 -0
  156. package/dist/esm/langchain/utils/env.mjs +2 -0
  157. package/dist/esm/langchain/utils/env.mjs.map +1 -0
  158. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +5 -1
  159. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  160. package/dist/esm/llm/bedrock/cacheSupport.mjs +52 -0
  161. package/dist/esm/llm/bedrock/cacheSupport.mjs.map +1 -0
  162. package/dist/esm/llm/bedrock/index.mjs +61 -34
  163. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  164. package/dist/esm/llm/openai/index.mjs +1 -4
  165. package/dist/esm/llm/openai/index.mjs.map +1 -1
  166. package/dist/esm/llm/openai/utils/index.mjs +27 -10
  167. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  168. package/dist/esm/main.mjs +21 -27
  169. package/dist/esm/main.mjs.map +1 -1
  170. package/dist/esm/memory/citations.mjs +4 -4
  171. package/dist/esm/memory/citations.mjs.map +1 -1
  172. package/dist/esm/memory/constants.mjs +17 -17
  173. package/dist/esm/memory/constants.mjs.map +1 -1
  174. package/dist/esm/memory/mmr.mjs +1 -1
  175. package/dist/esm/memory/mmr.mjs.map +1 -1
  176. package/dist/esm/memory/paths.mjs +1 -1
  177. package/dist/esm/memory/paths.mjs.map +1 -1
  178. package/dist/esm/memory/recallTracking.mjs +3 -3
  179. package/dist/esm/memory/recallTracking.mjs.map +1 -1
  180. package/dist/esm/memory/temporalDecay.mjs +2 -2
  181. package/dist/esm/memory/temporalDecay.mjs.map +1 -1
  182. package/dist/esm/messages/cache.mjs +89 -0
  183. package/dist/esm/messages/cache.mjs.map +1 -1
  184. package/dist/esm/messages/contextPruning.mjs +154 -0
  185. package/dist/esm/messages/contextPruning.mjs.map +1 -0
  186. package/dist/esm/messages/contextPruningSettings.mjs +50 -0
  187. package/dist/esm/messages/contextPruningSettings.mjs.map +1 -0
  188. package/dist/esm/messages/format.mjs +136 -12
  189. package/dist/esm/messages/format.mjs.map +1 -1
  190. package/dist/esm/messages/prune.mjs +504 -7
  191. package/dist/esm/messages/prune.mjs.map +1 -1
  192. package/dist/esm/run.mjs +141 -1
  193. package/dist/esm/run.mjs.map +1 -1
  194. package/dist/esm/tools/BashExecutor.mjs +227 -0
  195. package/dist/esm/tools/BashExecutor.mjs.map +1 -0
  196. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +288 -0
  197. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -0
  198. package/dist/esm/tools/CodeExecutor.mjs +45 -48
  199. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  200. package/dist/esm/tools/ProgrammaticToolCalling.mjs +17 -12
  201. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  202. package/dist/esm/tools/ReadFile.mjs +39 -0
  203. package/dist/esm/tools/ReadFile.mjs.map +1 -0
  204. package/dist/esm/tools/SkillTool.mjs +46 -0
  205. package/dist/esm/tools/SkillTool.mjs.map +1 -0
  206. package/dist/esm/tools/SubagentTool.mjs +86 -0
  207. package/dist/esm/tools/SubagentTool.mjs.map +1 -0
  208. package/dist/esm/tools/ToolNode.mjs +452 -26
  209. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  210. package/dist/esm/tools/memory/memoryAppendTool.mjs +1 -1
  211. package/dist/esm/tools/memory/memoryAppendTool.mjs.map +1 -1
  212. package/dist/esm/tools/memory/memoryGetTool.mjs +2 -2
  213. package/dist/esm/tools/memory/memoryGetTool.mjs.map +1 -1
  214. package/dist/esm/tools/memory/memorySearchTool.mjs +3 -3
  215. package/dist/esm/tools/memory/memorySearchTool.mjs.map +1 -1
  216. package/dist/esm/tools/memory/shared.mjs +1 -1
  217. package/dist/esm/tools/memory/shared.mjs.map +1 -1
  218. package/dist/esm/tools/search/search.mjs +11 -3
  219. package/dist/esm/tools/search/search.mjs.map +1 -1
  220. package/dist/esm/tools/search/tavily-scraper.mjs +186 -0
  221. package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -0
  222. package/dist/esm/tools/search/tavily-search.mjs +370 -0
  223. package/dist/esm/tools/search/tavily-search.mjs.map +1 -0
  224. package/dist/esm/tools/search/tool.mjs +28 -4
  225. package/dist/esm/tools/search/tool.mjs.map +1 -1
  226. package/dist/esm/tools/search/utils.mjs +10 -3
  227. package/dist/esm/tools/search/utils.mjs.map +1 -1
  228. package/dist/esm/tools/skillCatalog.mjs +82 -0
  229. package/dist/esm/tools/skillCatalog.mjs.map +1 -0
  230. package/dist/esm/tools/subagent/SubagentExecutor.mjs +506 -0
  231. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -0
  232. package/dist/esm/tools/toolOutputReferences.mjs +662 -0
  233. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -0
  234. package/dist/esm/types/agent-cache.mjs +52 -0
  235. package/dist/esm/types/agent-cache.mjs.map +1 -0
  236. package/dist/esm/types/graph.mjs.map +1 -1
  237. package/dist/esm/utils/truncation.mjs +128 -0
  238. package/dist/esm/utils/truncation.mjs.map +1 -0
  239. package/dist/types/agents/AgentContext.d.ts +101 -8
  240. package/dist/types/common/enum.d.ts +39 -12
  241. package/dist/types/common/index.d.ts +0 -1
  242. package/dist/types/graphs/Graph.d.ts +43 -0
  243. package/dist/types/graphs/MultiAgentGraph.d.ts +26 -150
  244. package/dist/types/graphs/index.d.ts +0 -1
  245. package/dist/types/graphs/phases/memoryFlushPhase.d.ts +2 -2
  246. package/dist/types/hooks/HookRegistry.d.ts +56 -0
  247. package/dist/types/hooks/executeHooks.d.ts +79 -0
  248. package/dist/types/hooks/index.d.ts +6 -0
  249. package/dist/types/hooks/matchers.d.ts +95 -0
  250. package/dist/types/hooks/types.d.ts +320 -0
  251. package/dist/types/index.d.ts +9 -9
  252. package/dist/types/langchain/google-common.d.ts +1 -0
  253. package/dist/types/langchain/index.d.ts +8 -0
  254. package/dist/types/langchain/language_models/chat_models.d.ts +1 -0
  255. package/dist/types/langchain/messages/tool.d.ts +1 -0
  256. package/dist/types/langchain/messages.d.ts +2 -0
  257. package/dist/types/langchain/openai.d.ts +1 -0
  258. package/dist/types/langchain/prompts.d.ts +1 -0
  259. package/dist/types/langchain/runnables.d.ts +2 -0
  260. package/dist/types/langchain/tools.d.ts +2 -0
  261. package/dist/types/langchain/utils/env.d.ts +1 -0
  262. package/dist/types/llm/bedrock/cacheSupport.d.ts +35 -0
  263. package/dist/types/llm/bedrock/index.d.ts +54 -1
  264. package/dist/types/llm/openai/index.d.ts +1 -1
  265. package/dist/types/memory/citations.d.ts +4 -4
  266. package/dist/types/memory/constants.d.ts +17 -17
  267. package/dist/types/memory/mmr.d.ts +3 -3
  268. package/dist/types/memory/paths.d.ts +1 -1
  269. package/dist/types/memory/temporalDecay.d.ts +2 -2
  270. package/dist/types/memory/types.d.ts +3 -3
  271. package/dist/types/messages/contextPruning.d.ts +42 -0
  272. package/dist/types/messages/contextPruningSettings.d.ts +44 -0
  273. package/dist/types/messages/format.d.ts +9 -1
  274. package/dist/types/messages/index.d.ts +2 -0
  275. package/dist/types/messages/prune.d.ts +91 -1
  276. package/dist/types/run.d.ts +2 -0
  277. package/dist/types/tools/BashExecutor.d.ts +76 -0
  278. package/dist/types/tools/BashProgrammaticToolCalling.d.ts +72 -0
  279. package/dist/types/tools/CodeExecutor.d.ts +8 -26
  280. package/dist/types/tools/ReadFile.d.ts +28 -0
  281. package/dist/types/tools/SkillTool.d.ts +40 -0
  282. package/dist/types/tools/SubagentTool.d.ts +36 -0
  283. package/dist/types/tools/ToolNode.d.ts +77 -5
  284. package/dist/types/tools/memory/shared.d.ts +1 -1
  285. package/dist/types/tools/search/tavily-scraper.d.ts +19 -0
  286. package/dist/types/tools/search/tavily-search.d.ts +4 -0
  287. package/dist/types/tools/search/types.d.ts +99 -5
  288. package/dist/types/tools/search/utils.d.ts +2 -2
  289. package/dist/types/tools/skillCatalog.d.ts +19 -0
  290. package/dist/types/tools/subagent/SubagentExecutor.d.ts +137 -0
  291. package/dist/types/tools/subagent/index.d.ts +2 -0
  292. package/dist/types/tools/subagent/types.d.ts +84 -0
  293. package/dist/types/tools/toolOutputReferences.d.ts +236 -0
  294. package/dist/types/types/agent-cache.d.ts +71 -0
  295. package/dist/types/types/graph.d.ts +163 -22
  296. package/dist/types/types/index.d.ts +3 -0
  297. package/dist/types/types/messages.d.ts +26 -0
  298. package/dist/types/types/run.d.ts +22 -0
  299. package/dist/types/types/skill.d.ts +9 -0
  300. package/dist/types/types/tools.d.ts +111 -0
  301. package/dist/types/utils/index.d.ts +1 -3
  302. package/dist/types/utils/truncation.d.ts +70 -0
  303. package/package.json +57 -17
  304. package/src/agents/AgentContext.ts +321 -78
  305. package/src/agents/__tests__/AgentContext.cacheTtl.live.test.ts +259 -0
  306. package/src/agents/__tests__/AgentContext.crossAgentTier1.live.test.ts +266 -0
  307. package/src/agents/__tests__/AgentContext.crossUserCache.live.test.ts +342 -0
  308. package/src/agents/__tests__/AgentContext.test.ts +632 -0
  309. package/src/common/__tests__/enum.test.ts +7 -17
  310. package/src/common/enum.ts +43 -12
  311. package/src/common/index.ts +0 -1
  312. package/src/graphs/Graph.ts +222 -2
  313. package/src/graphs/MultiAgentGraph.ts +154 -1466
  314. package/src/graphs/__tests__/MultiAgentGraph.test.ts +91 -0
  315. package/src/graphs/gapFeatures.test.ts +1 -1
  316. package/src/graphs/index.ts +0 -1
  317. package/src/graphs/phases/__tests__/memoryFlushPhase.test.ts +1 -1
  318. package/src/graphs/phases/memoryFlushPhase.ts +2 -2
  319. package/src/hooks/HookRegistry.ts +208 -0
  320. package/src/hooks/__tests__/HookRegistry.test.ts +190 -0
  321. package/src/hooks/__tests__/compactHooks.test.ts +214 -0
  322. package/src/hooks/__tests__/executeHooks.test.ts +1013 -0
  323. package/src/hooks/__tests__/integration.test.ts +337 -0
  324. package/src/hooks/__tests__/matchers.test.ts +238 -0
  325. package/src/hooks/__tests__/toolHooks.test.ts +665 -0
  326. package/src/hooks/executeHooks.ts +375 -0
  327. package/src/hooks/index.ts +57 -0
  328. package/src/hooks/matchers.ts +280 -0
  329. package/src/hooks/types.ts +404 -0
  330. package/src/index.ts +15 -24
  331. package/src/langchain/google-common.ts +1 -0
  332. package/src/langchain/index.ts +8 -0
  333. package/src/langchain/language_models/chat_models.ts +1 -0
  334. package/src/langchain/messages/tool.ts +5 -0
  335. package/src/langchain/messages.ts +21 -0
  336. package/src/langchain/openai.ts +1 -0
  337. package/src/langchain/prompts.ts +1 -0
  338. package/src/langchain/runnables.ts +7 -0
  339. package/src/langchain/tools.ts +8 -0
  340. package/src/langchain/utils/env.ts +1 -0
  341. package/src/llm/anthropic/utils/message_inputs.ts +10 -1
  342. package/src/llm/anthropic/utils/server-tool-inputs.test.ts +436 -0
  343. package/src/llm/bedrock/__tests__/bedrock-caching.test.ts +166 -18
  344. package/src/llm/bedrock/cacheSupport.test.ts +99 -0
  345. package/src/llm/bedrock/cacheSupport.ts +53 -0
  346. package/src/llm/bedrock/index.ts +116 -41
  347. package/src/llm/openai/index.ts +2 -2
  348. package/src/llm/openai/utils/index.ts +31 -14
  349. package/src/memory/citations.ts +4 -4
  350. package/src/memory/constants.ts +17 -17
  351. package/src/memory/mmr.ts +3 -3
  352. package/src/memory/paths.ts +1 -1
  353. package/src/memory/recallTracking.ts +3 -3
  354. package/src/memory/temporalDecay.ts +2 -2
  355. package/src/memory/types.ts +3 -3
  356. package/src/messages/__tests__/contextPruning.test.ts +228 -0
  357. package/src/messages/cache.test.ts +62 -24
  358. package/src/messages/cache.ts +112 -0
  359. package/src/messages/contextPruning.ts +191 -0
  360. package/src/messages/contextPruningSettings.ts +90 -0
  361. package/src/messages/ensureThinkingBlock.test.ts +1 -1
  362. package/src/messages/format.ts +164 -12
  363. package/src/messages/formatAgentMessages.skills.test.ts +413 -0
  364. package/src/messages/formatAgentMessages.test.ts +1 -1
  365. package/src/messages/index.ts +2 -0
  366. package/src/messages/prune.ts +661 -4
  367. package/src/run.ts +155 -1
  368. package/src/scripts/multi-agent-chain.ts +2 -2
  369. package/src/scripts/multi-agent-document-review-chain.ts +2 -2
  370. package/src/scripts/multi-agent-hybrid-flow.ts +4 -4
  371. package/src/scripts/multi-agent-parallel.ts +3 -3
  372. package/src/scripts/multi-agent-sequence.ts +3 -3
  373. package/src/scripts/multi-agent-subagent.ts +246 -0
  374. package/src/scripts/multi-agent-supervisor.ts +5 -5
  375. package/src/scripts/poc-multi-agent-comprehensive.ts +8 -8
  376. package/src/scripts/sequential-full-metadata-test.ts +2 -2
  377. package/src/scripts/subagent-event-driven-debug.ts +190 -0
  378. package/src/scripts/subagent-tools-debug.ts +160 -0
  379. package/src/scripts/test-custom-prompt-key.ts +3 -3
  380. package/src/scripts/test-handoff-input.ts +1 -1
  381. package/src/scripts/test-handoff-steering.ts +3 -3
  382. package/src/scripts/test-multi-agent-list-handoff.ts +1 -1
  383. package/src/scripts/test-parallel-agent-labeling.ts +3 -3
  384. package/src/scripts/test-parallel-handoffs.ts +2 -2
  385. package/src/scripts/test-thinking-handoff-bedrock.ts +1 -1
  386. package/src/scripts/test-thinking-handoff.ts +1 -1
  387. package/src/scripts/test-thinking-to-thinking-handoff-bedrock.ts +1 -1
  388. package/src/scripts/test-tool-before-handoff-role-order.ts +1 -1
  389. package/src/scripts/test-tools-before-handoff.ts +1 -1
  390. package/src/specs/agent-handoffs.test.ts +26 -483
  391. package/src/specs/anthropic.simple.test.ts +61 -0
  392. package/src/specs/multi-agent-summarization.test.ts +396 -0
  393. package/src/specs/prune.orphans.test.ts +248 -0
  394. package/src/specs/prune.test.ts +104 -16
  395. package/src/specs/thinking-handoff.test.ts +19 -19
  396. package/src/tools/BashExecutor.ts +281 -0
  397. package/src/tools/BashProgrammaticToolCalling.ts +397 -0
  398. package/src/tools/CodeExecutor.ts +63 -54
  399. package/src/tools/ProgrammaticToolCalling.ts +29 -14
  400. package/src/tools/ReadFile.ts +39 -0
  401. package/src/tools/SkillTool.ts +46 -0
  402. package/src/tools/SubagentTool.ts +100 -0
  403. package/src/tools/ToolNode.ts +548 -26
  404. package/src/tools/__tests__/BashExecutor.test.ts +49 -0
  405. package/src/tools/__tests__/CodeExecutor.test.ts +37 -36
  406. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +60 -0
  407. package/src/tools/__tests__/ReadFile.test.ts +44 -0
  408. package/src/tools/__tests__/SkillTool.test.ts +442 -0
  409. package/src/tools/__tests__/SubagentExecutor.test.ts +1148 -0
  410. package/src/tools/__tests__/SubagentTool.test.ts +149 -0
  411. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +1438 -0
  412. package/src/tools/__tests__/annotateMessagesForLLM.test.ts +479 -0
  413. package/src/tools/__tests__/skillCatalog.test.ts +161 -0
  414. package/src/tools/__tests__/subagentHooks.test.ts +210 -0
  415. package/src/tools/__tests__/toolOutputReferences.test.ts +415 -0
  416. package/src/tools/memory/memoryAppendTool.ts +1 -1
  417. package/src/tools/memory/memoryGetTool.ts +2 -2
  418. package/src/tools/memory/memorySearchTool.ts +3 -3
  419. package/src/tools/memory/shared.ts +1 -1
  420. package/src/tools/search/search.ts +12 -2
  421. package/src/tools/search/tavily-scraper.ts +235 -0
  422. package/src/tools/search/tavily-search.ts +424 -0
  423. package/src/tools/search/tavily.test.ts +965 -0
  424. package/src/tools/search/tool.ts +36 -2
  425. package/src/tools/search/types.ts +133 -8
  426. package/src/tools/search/utils.ts +13 -5
  427. package/src/tools/skillCatalog.ts +126 -0
  428. package/src/tools/subagent/SubagentExecutor.ts +676 -0
  429. package/src/tools/subagent/index.ts +13 -0
  430. package/src/tools/subagent/types.test.ts +70 -0
  431. package/src/tools/subagent/types.ts +115 -0
  432. package/src/tools/toolOutputReferences.ts +825 -0
  433. package/src/types/agent-cache.ts +74 -0
  434. package/src/types/graph.ts +172 -20
  435. package/src/types/index.ts +3 -0
  436. package/src/types/messages.ts +27 -0
  437. package/src/types/run.ts +22 -0
  438. package/src/types/skill.ts +11 -0
  439. package/src/types/tools.ts +118 -0
  440. package/src/utils/__tests__/truncation.test.ts +66 -0
  441. package/src/utils/index.ts +1 -3
  442. package/src/utils/truncation.ts +154 -0
  443. package/dist/cjs/common/spawnPath.cjs +0 -104
  444. package/dist/cjs/common/spawnPath.cjs.map +0 -1
  445. package/dist/cjs/content/ArtifactStore.cjs +0 -579
  446. package/dist/cjs/content/ArtifactStore.cjs.map +0 -1
  447. package/dist/cjs/content/ContentStore.cjs +0 -638
  448. package/dist/cjs/content/ContentStore.cjs.map +0 -1
  449. package/dist/cjs/content/contentAnalyzer.cjs +0 -91
  450. package/dist/cjs/content/contentAnalyzer.cjs.map +0 -1
  451. package/dist/cjs/content/index.cjs +0 -20
  452. package/dist/cjs/content/index.cjs.map +0 -1
  453. package/dist/cjs/content/mcpAutoCache.cjs +0 -115
  454. package/dist/cjs/content/mcpAutoCache.cjs.map +0 -1
  455. package/dist/cjs/graphs/HandoffRegistry.cjs +0 -143
  456. package/dist/cjs/graphs/HandoffRegistry.cjs.map +0 -1
  457. package/dist/cjs/providers/a2a/A2ACapabilityProvider.cjs +0 -288
  458. package/dist/cjs/providers/a2a/A2ACapabilityProvider.cjs.map +0 -1
  459. package/dist/cjs/providers/a2a/client.cjs +0 -92
  460. package/dist/cjs/providers/a2a/client.cjs.map +0 -1
  461. package/dist/cjs/providers/a2a/config.cjs +0 -38
  462. package/dist/cjs/providers/a2a/config.cjs.map +0 -1
  463. package/dist/cjs/providers/capabilityNaming.cjs +0 -43
  464. package/dist/cjs/providers/capabilityNaming.cjs.map +0 -1
  465. package/dist/cjs/providers/mcp/MCPCapabilityProvider.cjs +0 -244
  466. package/dist/cjs/providers/mcp/MCPCapabilityProvider.cjs.map +0 -1
  467. package/dist/cjs/providers/mcp/config.cjs +0 -42
  468. package/dist/cjs/providers/mcp/config.cjs.map +0 -1
  469. package/dist/cjs/providers/mcp/transport.cjs +0 -65
  470. package/dist/cjs/providers/mcp/transport.cjs.map +0 -1
  471. package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs +0 -128
  472. package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs.map +0 -1
  473. package/dist/cjs/providers/types.cjs +0 -51
  474. package/dist/cjs/providers/types.cjs.map +0 -1
  475. package/dist/cjs/tools/artifacts/schema.cjs +0 -86
  476. package/dist/cjs/tools/artifacts/schema.cjs.map +0 -1
  477. package/dist/cjs/tools/artifacts/tool.cjs +0 -219
  478. package/dist/cjs/tools/artifacts/tool.cjs.map +0 -1
  479. package/dist/cjs/tools/fileSearch/formatter.cjs +0 -93
  480. package/dist/cjs/tools/fileSearch/formatter.cjs.map +0 -1
  481. package/dist/cjs/tools/fileSearch/ragClient.cjs +0 -102
  482. package/dist/cjs/tools/fileSearch/ragClient.cjs.map +0 -1
  483. package/dist/cjs/tools/fileSearch/schema.cjs +0 -18
  484. package/dist/cjs/tools/fileSearch/schema.cjs.map +0 -1
  485. package/dist/cjs/tools/fileSearch/tool.cjs +0 -155
  486. package/dist/cjs/tools/fileSearch/tool.cjs.map +0 -1
  487. package/dist/cjs/tools/proxyTool.cjs +0 -102
  488. package/dist/cjs/tools/proxyTool.cjs.map +0 -1
  489. package/dist/cjs/utils/childAgentContext.cjs +0 -242
  490. package/dist/cjs/utils/childAgentContext.cjs.map +0 -1
  491. package/dist/cjs/utils/credentials.cjs +0 -142
  492. package/dist/cjs/utils/credentials.cjs.map +0 -1
  493. package/dist/cjs/utils/httpClient.cjs +0 -74
  494. package/dist/cjs/utils/httpClient.cjs.map +0 -1
  495. package/dist/cjs/utils/toolManifest.cjs +0 -100
  496. package/dist/cjs/utils/toolManifest.cjs.map +0 -1
  497. package/dist/esm/common/spawnPath.mjs +0 -95
  498. package/dist/esm/common/spawnPath.mjs.map +0 -1
  499. package/dist/esm/content/ArtifactStore.mjs +0 -576
  500. package/dist/esm/content/ArtifactStore.mjs.map +0 -1
  501. package/dist/esm/content/ContentStore.mjs +0 -635
  502. package/dist/esm/content/ContentStore.mjs.map +0 -1
  503. package/dist/esm/content/contentAnalyzer.mjs +0 -87
  504. package/dist/esm/content/contentAnalyzer.mjs.map +0 -1
  505. package/dist/esm/content/index.mjs +0 -5
  506. package/dist/esm/content/mcpAutoCache.mjs +0 -111
  507. package/dist/esm/content/mcpAutoCache.mjs.map +0 -1
  508. package/dist/esm/graphs/HandoffRegistry.mjs +0 -141
  509. package/dist/esm/graphs/HandoffRegistry.mjs.map +0 -1
  510. package/dist/esm/providers/a2a/A2ACapabilityProvider.mjs +0 -281
  511. package/dist/esm/providers/a2a/A2ACapabilityProvider.mjs.map +0 -1
  512. package/dist/esm/providers/a2a/client.mjs +0 -88
  513. package/dist/esm/providers/a2a/client.mjs.map +0 -1
  514. package/dist/esm/providers/a2a/config.mjs +0 -35
  515. package/dist/esm/providers/a2a/config.mjs.map +0 -1
  516. package/dist/esm/providers/capabilityNaming.mjs +0 -39
  517. package/dist/esm/providers/capabilityNaming.mjs.map +0 -1
  518. package/dist/esm/providers/mcp/MCPCapabilityProvider.mjs +0 -240
  519. package/dist/esm/providers/mcp/MCPCapabilityProvider.mjs.map +0 -1
  520. package/dist/esm/providers/mcp/config.mjs +0 -39
  521. package/dist/esm/providers/mcp/config.mjs.map +0 -1
  522. package/dist/esm/providers/mcp/transport.mjs +0 -63
  523. package/dist/esm/providers/mcp/transport.mjs.map +0 -1
  524. package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs +0 -126
  525. package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs.map +0 -1
  526. package/dist/esm/providers/types.mjs +0 -51
  527. package/dist/esm/providers/types.mjs.map +0 -1
  528. package/dist/esm/tools/artifacts/schema.mjs +0 -79
  529. package/dist/esm/tools/artifacts/schema.mjs.map +0 -1
  530. package/dist/esm/tools/artifacts/tool.mjs +0 -213
  531. package/dist/esm/tools/artifacts/tool.mjs.map +0 -1
  532. package/dist/esm/tools/fileSearch/formatter.mjs +0 -90
  533. package/dist/esm/tools/fileSearch/formatter.mjs.map +0 -1
  534. package/dist/esm/tools/fileSearch/ragClient.mjs +0 -98
  535. package/dist/esm/tools/fileSearch/ragClient.mjs.map +0 -1
  536. package/dist/esm/tools/fileSearch/schema.mjs +0 -15
  537. package/dist/esm/tools/fileSearch/schema.mjs.map +0 -1
  538. package/dist/esm/tools/fileSearch/tool.mjs +0 -152
  539. package/dist/esm/tools/fileSearch/tool.mjs.map +0 -1
  540. package/dist/esm/tools/proxyTool.mjs +0 -100
  541. package/dist/esm/tools/proxyTool.mjs.map +0 -1
  542. package/dist/esm/utils/childAgentContext.mjs +0 -237
  543. package/dist/esm/utils/childAgentContext.mjs.map +0 -1
  544. package/dist/esm/utils/credentials.mjs +0 -135
  545. package/dist/esm/utils/credentials.mjs.map +0 -1
  546. package/dist/esm/utils/httpClient.mjs +0 -70
  547. package/dist/esm/utils/httpClient.mjs.map +0 -1
  548. package/dist/esm/utils/toolManifest.mjs +0 -96
  549. package/dist/esm/utils/toolManifest.mjs.map +0 -1
  550. package/dist/types/common/spawnPath.d.ts +0 -59
  551. package/dist/types/content/ArtifactStore.d.ts +0 -223
  552. package/dist/types/content/ContentStore.d.ts +0 -140
  553. package/dist/types/content/contentAnalyzer.d.ts +0 -38
  554. package/dist/types/content/index.d.ts +0 -24
  555. package/dist/types/content/mcpAutoCache.d.ts +0 -89
  556. package/dist/types/content/types.d.ts +0 -75
  557. package/dist/types/graphs/HandoffRegistry.d.ts +0 -97
  558. package/dist/types/providers/a2a/A2ACapabilityProvider.d.ts +0 -89
  559. package/dist/types/providers/a2a/client.d.ts +0 -47
  560. package/dist/types/providers/a2a/config.d.ts +0 -18
  561. package/dist/types/providers/a2a/index.d.ts +0 -6
  562. package/dist/types/providers/a2a/types.d.ts +0 -173
  563. package/dist/types/providers/capabilityNaming.d.ts +0 -25
  564. package/dist/types/providers/index.d.ts +0 -12
  565. package/dist/types/providers/mcp/MCPCapabilityProvider.d.ts +0 -54
  566. package/dist/types/providers/mcp/config.d.ts +0 -20
  567. package/dist/types/providers/mcp/index.d.ts +0 -5
  568. package/dist/types/providers/mcp/transport.d.ts +0 -18
  569. package/dist/types/providers/mcp/types.d.ts +0 -112
  570. package/dist/types/providers/tools-server/ToolsServerCapabilityProvider.d.ts +0 -59
  571. package/dist/types/providers/tools-server/index.d.ts +0 -1
  572. package/dist/types/providers/types.d.ts +0 -184
  573. package/dist/types/tools/artifacts/index.d.ts +0 -3
  574. package/dist/types/tools/artifacts/schema.d.ts +0 -63
  575. package/dist/types/tools/artifacts/tool.d.ts +0 -16
  576. package/dist/types/tools/artifacts/types.d.ts +0 -127
  577. package/dist/types/tools/fileSearch/formatter.d.ts +0 -25
  578. package/dist/types/tools/fileSearch/index.d.ts +0 -5
  579. package/dist/types/tools/fileSearch/ragClient.d.ts +0 -32
  580. package/dist/types/tools/fileSearch/schema.d.ts +0 -13
  581. package/dist/types/tools/fileSearch/tool.d.ts +0 -18
  582. package/dist/types/tools/fileSearch/types.d.ts +0 -139
  583. package/dist/types/tools/proxyTool.d.ts +0 -62
  584. package/dist/types/tools/search/test.d.ts +0 -1
  585. package/dist/types/utils/childAgentContext.d.ts +0 -99
  586. package/dist/types/utils/credentials.d.ts +0 -77
  587. package/dist/types/utils/httpClient.d.ts +0 -46
  588. package/dist/types/utils/toolManifest.d.ts +0 -49
  589. package/src/common/__tests__/spawnPath.test.ts +0 -110
  590. package/src/common/spawnPath.ts +0 -101
  591. package/src/content/ArtifactStore.ts +0 -782
  592. package/src/content/ContentStore.ts +0 -753
  593. package/src/content/contentAnalyzer.ts +0 -105
  594. package/src/content/index.ts +0 -51
  595. package/src/content/mcpAutoCache.ts +0 -185
  596. package/src/content/types.ts +0 -82
  597. package/src/graphs/HandoffRegistry.ts +0 -199
  598. package/src/graphs/__tests__/HandoffRegistry.test.ts +0 -410
  599. package/src/graphs/__tests__/multi-agent-delegate.test.ts +0 -458
  600. package/src/graphs/__tests__/multi-agent-edges.test.ts +0 -276
  601. package/src/graphs/__tests__/multi-agent-nested-subgraph.test.ts +0 -221
  602. package/src/graphs/handoffValidation.test.ts +0 -353
  603. package/src/providers/__tests__/ToolsServerCapabilityProvider.integration.spec.ts +0 -79
  604. package/src/providers/__tests__/ToolsServerCapabilityProvider.test.ts +0 -271
  605. package/src/providers/__tests__/types.test.ts +0 -64
  606. package/src/providers/a2a/A2ACapabilityProvider.ts +0 -384
  607. package/src/providers/a2a/__tests__/A2ACapabilityProvider.integration.spec.ts +0 -345
  608. package/src/providers/a2a/__tests__/A2ACapabilityProvider.test.ts +0 -460
  609. package/src/providers/a2a/client.ts +0 -115
  610. package/src/providers/a2a/config.ts +0 -40
  611. package/src/providers/a2a/index.ts +0 -29
  612. package/src/providers/a2a/types.ts +0 -191
  613. package/src/providers/capabilityNaming.ts +0 -42
  614. package/src/providers/index.ts +0 -68
  615. package/src/providers/mcp/MCPCapabilityProvider.ts +0 -345
  616. package/src/providers/mcp/__tests__/MCPCapabilityProvider.integration.spec.ts +0 -386
  617. package/src/providers/mcp/__tests__/MCPCapabilityProvider.test.ts +0 -371
  618. package/src/providers/mcp/config.ts +0 -45
  619. package/src/providers/mcp/index.ts +0 -21
  620. package/src/providers/mcp/transport.ts +0 -76
  621. package/src/providers/mcp/types.ts +0 -139
  622. package/src/providers/tools-server/ToolsServerCapabilityProvider.ts +0 -249
  623. package/src/providers/tools-server/index.ts +0 -1
  624. package/src/providers/types.ts +0 -204
  625. package/src/scripts/test-bedrock-handoff-autonomous.ts +0 -267
  626. package/src/scripts/test-handoff-preamble.ts +0 -278
  627. package/src/specs/agent-handoffs-bedrock.integration.test.ts +0 -415
  628. package/src/tools/artifacts/__tests__/tool.test.ts +0 -259
  629. package/src/tools/artifacts/index.ts +0 -33
  630. package/src/tools/artifacts/schema.ts +0 -99
  631. package/src/tools/artifacts/tool.ts +0 -289
  632. package/src/tools/artifacts/types.ts +0 -162
  633. package/src/tools/fileSearch/__tests__/tool.test.ts +0 -261
  634. package/src/tools/fileSearch/formatter.ts +0 -129
  635. package/src/tools/fileSearch/index.ts +0 -23
  636. package/src/tools/fileSearch/ragClient.ts +0 -137
  637. package/src/tools/fileSearch/schema.ts +0 -19
  638. package/src/tools/fileSearch/tool.ts +0 -207
  639. package/src/tools/fileSearch/types.ts +0 -149
  640. package/src/tools/proxyTool.ts +0 -166
  641. package/src/tools/search/output.md +0 -2775
  642. package/src/tools/search/test.html +0 -884
  643. package/src/tools/search/test.md +0 -643
  644. package/src/tools/search/test.ts +0 -159
  645. package/src/utils/__tests__/childAgentContext.test.ts +0 -217
  646. package/src/utils/__tests__/credentials.test.ts +0 -130
  647. package/src/utils/__tests__/httpClient.test.ts +0 -75
  648. package/src/utils/__tests__/toolManifest.test.ts +0 -116
  649. package/src/utils/childAgentContext.ts +0 -259
  650. package/src/utils/credentials.ts +0 -157
  651. package/src/utils/httpClient.ts +0 -92
  652. package/src/utils/toolManifest.ts +0 -109
  653. /package/dist/esm/{content → langchain}/index.mjs.map +0 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Autonomous memory — core types.
3
3
  *
4
- * Ported from upstream's memory-core pattern, adapted for Postgres + pgvector
4
+ * Ported from the reference implementation's memory-core pattern, adapted for Postgres + pgvector
5
5
  * and shaped so a future graph-backend layer (Graphiti, Neo4j agent-memory, etc.)
6
6
  * can be added alongside the vector store without changing the tool contracts.
7
7
  *
@@ -79,7 +79,7 @@ export interface MemorySearchOptions {
79
79
  minScore?: number;
80
80
  /**
81
81
  * Phase 2 toggles — when the backend supports them. Each is independently
82
- * opt-in; all false = upstream Phase 1 behavior.
82
+ * opt-in; all false = Phase 1 behavior.
83
83
  */
84
84
  mmr?: { enabled?: boolean; lambda?: number };
85
85
  temporalDecay?: { enabled?: boolean; halfLifeDays?: number };
@@ -156,7 +156,7 @@ export interface MemoryConfig {
156
156
  search?: {
157
157
  maxResults?: number;
158
158
  maxInjectedChars?: number;
159
- /** Phase 2 — enable MMR reranking (upstream-aligned defaults when true). */
159
+ /** Phase 2 — enable MMR reranking (defaults when true). */
160
160
  mmr?: { enabled?: boolean; lambda?: number };
161
161
  /** Phase 2 — enable temporal decay on dated memory files. */
162
162
  temporalDecay?: { enabled?: boolean; halfLifeDays?: number };
@@ -0,0 +1,228 @@
1
+ import {
2
+ AIMessage,
3
+ BaseMessage,
4
+ HumanMessage,
5
+ SystemMessage,
6
+ ToolMessage,
7
+ } from '@langchain/core/messages';
8
+ import type { TokenCounter } from '@/types/run';
9
+ import { applyContextPruning } from '../contextPruning';
10
+
11
+ const counter: TokenCounter = (msg: BaseMessage) => {
12
+ if (typeof msg.content === 'string') return Math.max(1, msg.content.length);
13
+ return 1;
14
+ };
15
+
16
+ describe('applyContextPruning', () => {
17
+ it('returns zero counts when disabled', () => {
18
+ const messages: BaseMessage[] = [
19
+ new HumanMessage('hi'),
20
+ new AIMessage('thinking'),
21
+ new ToolMessage({
22
+ content: 'a'.repeat(10000),
23
+ tool_call_id: 't1',
24
+ }),
25
+ ];
26
+ const map = { 0: 1, 1: 1, 2: 10000 };
27
+ const out = applyContextPruning({
28
+ messages,
29
+ indexTokenCountMap: map,
30
+ tokenCounter: counter,
31
+ config: { enabled: false },
32
+ });
33
+ expect(out.softTrimmed).toBe(0);
34
+ expect(out.hardCleared).toBe(0);
35
+ // Message untouched
36
+ expect((messages[2] as ToolMessage).content).toHaveLength(10000);
37
+ });
38
+
39
+ it('soft-trims an older tool result with content over the threshold', () => {
40
+ // Build a sequence with an old tool result outside the protected zone.
41
+ const messages: BaseMessage[] = [
42
+ new SystemMessage('sys'),
43
+ new HumanMessage('q1'),
44
+ new AIMessage({
45
+ content: 'thinking',
46
+ tool_calls: [{ name: 't', args: {}, id: 'old' }],
47
+ }),
48
+ new ToolMessage({
49
+ // Long string — should soft-trim.
50
+ content: 'OLD_TOOL_RESULT' + 'a'.repeat(20000),
51
+ tool_call_id: 'old',
52
+ }),
53
+ new HumanMessage('q2'),
54
+ new AIMessage('answer 1'),
55
+ new HumanMessage('q3'),
56
+ new AIMessage('answer 2'),
57
+ new HumanMessage('q4'),
58
+ new AIMessage('answer 3'),
59
+ new HumanMessage('q5'),
60
+ new AIMessage('answer 4'),
61
+ ];
62
+ const map: Record<string, number> = {};
63
+ for (let i = 0; i < messages.length; i++) {
64
+ map[i] = counter(messages[i]);
65
+ }
66
+
67
+ const result = applyContextPruning({
68
+ messages,
69
+ indexTokenCountMap: map,
70
+ tokenCounter: counter,
71
+ config: {
72
+ enabled: true,
73
+ keepLastAssistants: 2,
74
+ softTrimRatio: 0.3,
75
+ hardClearRatio: 0.95,
76
+ minPrunableToolChars: 100,
77
+ softTrim: { maxChars: 200, headChars: 50, tailChars: 50 },
78
+ hardClear: { enabled: true, placeholder: '[evicted]' },
79
+ },
80
+ });
81
+
82
+ // The old tool result at index 3 should be soft-trimmed.
83
+ expect(result.softTrimmed).toBeGreaterThan(0);
84
+ const oldToolMsg = messages[3] as ToolMessage;
85
+ expect((oldToolMsg.content as string).length).toBeLessThan(20000);
86
+ expect(oldToolMsg.content).toContain('soft-trimmed');
87
+ });
88
+
89
+ it('hard-clears very old tool results when above hardClearRatio', () => {
90
+ // Position-age = (totalMessages - i) / totalMessages; with totalMessages=12
91
+ // and i=0, ratio=1.0 (oldest). But system at i=0 is in the protected zone.
92
+ // i=2 (the AI/tool pair) gives ratio=10/12 ≈ 0.83.
93
+ const messages: BaseMessage[] = [
94
+ new SystemMessage('sys'),
95
+ new HumanMessage('q1'),
96
+ new AIMessage({
97
+ content: 'thinking',
98
+ tool_calls: [{ name: 't', args: {}, id: 'oldest' }],
99
+ }),
100
+ new ToolMessage({
101
+ content: 'OLDEST' + 'x'.repeat(5000),
102
+ tool_call_id: 'oldest',
103
+ }),
104
+ ];
105
+ for (let i = 0; i < 8; i++) {
106
+ messages.push(new HumanMessage(`recent ${i}`));
107
+ messages.push(new AIMessage(`reply ${i}`));
108
+ }
109
+ const map: Record<string, number> = {};
110
+ for (let i = 0; i < messages.length; i++) {
111
+ map[i] = counter(messages[i]);
112
+ }
113
+
114
+ const result = applyContextPruning({
115
+ messages,
116
+ indexTokenCountMap: map,
117
+ tokenCounter: counter,
118
+ config: {
119
+ enabled: true,
120
+ keepLastAssistants: 2,
121
+ softTrimRatio: 0.3,
122
+ hardClearRatio: 0.7,
123
+ minPrunableToolChars: 100,
124
+ softTrim: { maxChars: 200, headChars: 50, tailChars: 50 },
125
+ hardClear: { enabled: true, placeholder: '[evicted]' },
126
+ },
127
+ });
128
+
129
+ expect(result.hardCleared).toBeGreaterThan(0);
130
+ const oldToolMsg = messages[3] as ToolMessage;
131
+ expect(oldToolMsg.content).toBe('[evicted]');
132
+ });
133
+
134
+ it('does not touch tool results inside the protected zone (last N assistant turns)', () => {
135
+ const messages: BaseMessage[] = [
136
+ new SystemMessage('sys'),
137
+ new HumanMessage('q1'),
138
+ new AIMessage({
139
+ content: 'thinking',
140
+ tool_calls: [{ name: 't', args: {}, id: 'recent' }],
141
+ }),
142
+ new ToolMessage({
143
+ content: 'RECENT' + 'y'.repeat(20000),
144
+ tool_call_id: 'recent',
145
+ }),
146
+ ];
147
+ const map: Record<string, number> = {};
148
+ for (let i = 0; i < messages.length; i++) {
149
+ map[i] = counter(messages[i]);
150
+ }
151
+
152
+ const result = applyContextPruning({
153
+ messages,
154
+ indexTokenCountMap: map,
155
+ tokenCounter: counter,
156
+ config: {
157
+ enabled: true,
158
+ keepLastAssistants: 4,
159
+ softTrimRatio: 0.0,
160
+ hardClearRatio: 0.0,
161
+ minPrunableToolChars: 100,
162
+ softTrim: { maxChars: 200, headChars: 50, tailChars: 50 },
163
+ hardClear: { enabled: true, placeholder: '[evicted]' },
164
+ },
165
+ });
166
+
167
+ expect(result.softTrimmed).toBe(0);
168
+ expect(result.hardCleared).toBe(0);
169
+ expect((messages[3] as ToolMessage).content).toContain('y'.repeat(100));
170
+ });
171
+
172
+ it('skips messages with image content even when out of protected zone', () => {
173
+ const imageBlock = {
174
+ type: 'image_url',
175
+ image_url: { url: 'data:image/png;base64,iVBORw0K...' },
176
+ };
177
+ const messages: BaseMessage[] = [
178
+ new SystemMessage('sys'),
179
+ new HumanMessage('q'),
180
+ new AIMessage({
181
+ content: 'thinking',
182
+ tool_calls: [{ name: 't', args: {}, id: 'img' }],
183
+ }),
184
+ new ToolMessage({
185
+ content: [imageBlock] as never, // structured content not pruned
186
+ tool_call_id: 'img',
187
+ }),
188
+ ];
189
+ for (let i = 0; i < 8; i++) {
190
+ messages.push(new HumanMessage(`recent ${i}`));
191
+ messages.push(new AIMessage(`reply ${i}`));
192
+ }
193
+ const map: Record<string, number> = {};
194
+ for (let i = 0; i < messages.length; i++) {
195
+ map[i] = 1;
196
+ }
197
+
198
+ const result = applyContextPruning({
199
+ messages,
200
+ indexTokenCountMap: map,
201
+ tokenCounter: counter,
202
+ config: {
203
+ enabled: true,
204
+ keepLastAssistants: 2,
205
+ softTrimRatio: 0.0,
206
+ hardClearRatio: 0.0,
207
+ minPrunableToolChars: 0,
208
+ softTrim: { maxChars: 200, headChars: 50, tailChars: 50 },
209
+ hardClear: { enabled: true, placeholder: '[evicted]' },
210
+ },
211
+ });
212
+
213
+ // Image-containing tool message must not have been touched.
214
+ expect(result.softTrimmed).toBe(0);
215
+ expect(Array.isArray((messages[3] as ToolMessage).content)).toBe(true);
216
+ });
217
+
218
+ it('returns zero counts on empty message array', () => {
219
+ const out = applyContextPruning({
220
+ messages: [],
221
+ indexTokenCountMap: {},
222
+ tokenCounter: counter,
223
+ config: { enabled: true },
224
+ });
225
+ expect(out.softTrimmed).toBe(0);
226
+ expect(out.hardCleared).toBe(0);
227
+ });
228
+ });
@@ -412,7 +412,14 @@ describe('addBedrockCacheControl (Bedrock cache checkpoints)', () => {
412
412
  expect(first[1]).toEqual({ cachePoint: { type: 'default' } });
413
413
  });
414
414
 
415
- it('works with the example from the langchain pr (with multi-turn behavior)', () => {
415
+ it('skips system messages (cachePoint added inline by AgentContext.buildSystemRunnable)', () => {
416
+ /* Contract change (PR #128 alignment): system message
417
+ * cachePoint is now emitted inline in AgentContext.buildSystemRunnable.
418
+ * addBedrockCacheControl must therefore SKIP system messages so the
419
+ * 2-cachePoint per-request budget stays available for the conversation
420
+ * tail. Caching the system prefix is what unlocks cross-user cost
421
+ * savings — Bedrock's prompt cache is account-scoped, so a stable
422
+ * system prefix shared across users hits the same cache entry. */
416
423
  const messages: TestMsg[] = [
417
424
  {
418
425
  role: 'system',
@@ -430,15 +437,17 @@ describe('addBedrockCacheControl (Bedrock cache checkpoints)', () => {
430
437
 
431
438
  const result = addBedrockCacheControl(messages);
432
439
 
433
- let system = result[0].content as MessageContentComplex[];
434
- let user = result[1].content as MessageContentComplex[];
440
+ const system = result[0].content as MessageContentComplex[];
441
+ const user = result[1].content as MessageContentComplex[];
435
442
 
436
- // Both messages get cachePoints (last 2 eligible messages)
437
- expect(system[0]).toEqual({
438
- type: ContentTypes.TEXT,
439
- text: 'You\'re an advanced AI assistant.',
440
- });
441
- expect(system[1]).toEqual({ cachePoint: { type: 'default' } });
443
+ // System message is left untouched addBedrockCacheControl does NOT
444
+ // add a cachePoint here. The cachePoint is added in AgentContext when
445
+ // the system message is first built.
446
+ expect(system).toEqual([
447
+ { type: ContentTypes.TEXT, text: 'You\'re an advanced AI assistant.' },
448
+ ]);
449
+
450
+ // User message still gets a cachePoint (last eligible).
442
451
  expect(user[0]).toEqual({
443
452
  type: ContentTypes.TEXT,
444
453
  text: 'What is the capital of France?',
@@ -458,30 +467,59 @@ describe('addBedrockCacheControl (Bedrock cache checkpoints)', () => {
458
467
 
459
468
  const result2 = addBedrockCacheControl(result);
460
469
 
461
- system = result2[0].content as MessageContentComplex[];
462
- user = result2[1].content as MessageContentComplex[];
463
- const assistant = result2[2].content as MessageContentComplex[];
470
+ const system2 = result2[0].content as MessageContentComplex[];
471
+ const user2 = result2[1].content as MessageContentComplex[];
472
+ const assistant2 = result2[2].content as MessageContentComplex[];
464
473
 
465
- // System message no longer has cachePoint (it's not in the last 2 eligible)
466
- expect(system[0]).toEqual({
467
- type: ContentTypes.TEXT,
468
- text: 'You\'re an advanced AI assistant.',
469
- });
470
- expect(system.length).toBe(1);
474
+ // System message still untouched.
475
+ expect(system2).toEqual([
476
+ { type: ContentTypes.TEXT, text: 'You\'re an advanced AI assistant.' },
477
+ ]);
471
478
 
472
- // User message gets cachePoint (second-to-last eligible)
473
- expect(user[0]).toEqual({
479
+ // Both conversation messages now get cachePoints (last 2 eligible).
480
+ expect(user2[0]).toEqual({
474
481
  type: ContentTypes.TEXT,
475
482
  text: 'What is the capital of France?',
476
483
  });
477
- expect(user[1]).toEqual({ cachePoint: { type: 'default' } });
484
+ expect(user2[1]).toEqual({ cachePoint: { type: 'default' } });
478
485
 
479
- // Assistant message gets cachePoint (last eligible)
480
- expect(assistant[0]).toEqual({
486
+ expect(assistant2[0]).toEqual({
481
487
  type: ContentTypes.TEXT,
482
488
  text: 'Sure! The capital of France is Paris.',
483
489
  });
484
- expect(assistant[1]).toEqual({ cachePoint: { type: 'default' } });
490
+ expect(assistant2[1]).toEqual({ cachePoint: { type: 'default' } });
491
+ });
492
+
493
+ it('strips stray Anthropic cache_control from system message (cross-provider safety)', () => {
494
+ /* If a system message arrives with an Anthropic-style cache_control
495
+ * marker (e.g. cross-provider switch), addBedrockCacheControl should
496
+ * sanitize it — Bedrock ignores cache_control and stale markers can
497
+ * confuse provider validation. */
498
+ const messages: TestMsg[] = [
499
+ {
500
+ role: 'system',
501
+ content: [
502
+ {
503
+ type: ContentTypes.TEXT,
504
+ text: 'You\'re an advanced AI assistant.',
505
+ cache_control: { type: 'ephemeral' },
506
+ } as MessageContentComplex,
507
+ ],
508
+ },
509
+ {
510
+ role: 'user',
511
+ content: [{ type: ContentTypes.TEXT, text: 'Hi' }],
512
+ },
513
+ ];
514
+
515
+ const result = addBedrockCacheControl(messages);
516
+ const system = result[0].content as MessageContentComplex[];
517
+
518
+ expect(system).toHaveLength(1);
519
+ expect('cache_control' in system[0]).toBe(false);
520
+ expect((system[0] as { text?: string }).text).toBe(
521
+ 'You\'re an advanced AI assistant.'
522
+ );
485
523
  });
486
524
 
487
525
  it('is idempotent - calling multiple times does not add duplicate cache points', () => {
@@ -14,6 +14,61 @@ type MessageWithContent = {
14
14
  content?: string | MessageContentComplex[];
15
15
  };
16
16
 
17
+ type MessageContentWithCacheControl = MessageContentComplex & {
18
+ cache_control?: unknown;
19
+ };
20
+
21
+ /**
22
+ * Strips Anthropic-style `cache_control` markers from every block in the
23
+ * given content array. Returns the (possibly cloned) content and a flag
24
+ * indicating whether anything was actually modified — so callers can avoid
25
+ * unnecessary message clones.
26
+ *
27
+ * Used to sanitize a system message's content array before adding Bedrock
28
+ * cache points, so stray Anthropic markers (e.g. left over from a previous
29
+ * Anthropic-provider turn) don't prevent the Bedrock provider from caching
30
+ * the system prefix.
31
+ */
32
+ function stripAnthropicCacheControlFromBlocks(
33
+ content: MessageContentComplex[]
34
+ ): { content: MessageContentComplex[]; modified: boolean } {
35
+ let modified = false;
36
+ const strippedContent = content.map((block) => {
37
+ if (!('cache_control' in block)) {
38
+ return block;
39
+ }
40
+
41
+ const cloned: MessageContentWithCacheControl = { ...block };
42
+ delete cloned.cache_control;
43
+ modified = true;
44
+ return cloned;
45
+ });
46
+
47
+ return { content: strippedContent, modified };
48
+ }
49
+
50
+ /**
51
+ * For the Bedrock cache pass, system messages must NOT carry Anthropic
52
+ * cache_control markers (those are an Anthropic-specific encoding and
53
+ * Bedrock uses cachePoint blocks instead). Strip them before the cache
54
+ * point insertion logic walks the message.
55
+ */
56
+ function sanitizeBedrockSystemMessage<T extends MessageWithContent>(
57
+ message: T
58
+ ): T {
59
+ const content = message.content;
60
+ if (!Array.isArray(content)) {
61
+ return message;
62
+ }
63
+
64
+ const stripped = stripAnthropicCacheControlFromBlocks(content);
65
+ if (!stripped.modified) {
66
+ return message;
67
+ }
68
+
69
+ return cloneMessage(message, stripped.content);
70
+ }
71
+
17
72
  /** Debug logger for cache operations - set ILLUMA_DEBUG_CACHE=true to enable */
18
73
  const debugCache = (message: string, data?: unknown): void => {
19
74
  if (process.env.ILLUMA_DEBUG_CACHE === 'true') {
@@ -361,8 +416,36 @@ export function addBedrockCacheControl<
361
416
 
362
417
  // Clone messages to avoid mutating originals
363
418
  const updatedMessages: T[] = messages.map((msg) => {
419
+ /* PRESERVE cachePoint on system messages — they're added inline by
420
+ * AgentContext.buildSystemRunnable to mark the cacheable system prefix
421
+ * for account-level caching. Only strip stray Anthropic cache_control
422
+ * (which Bedrock ignores). For non-system messages keep the existing
423
+ * behaviour: full strip + repopulate based on last-2 strategy. */
424
+ const msgType =
425
+ 'getType' in msg && typeof msg.getType === 'function'
426
+ ? msg.getType()
427
+ : undefined;
428
+ const msgRole = (msg as Record<string, unknown>).role as string | undefined;
429
+ const isSystem = msgType === 'system' || msgRole === 'system';
430
+
364
431
  const content = msg.content;
365
432
  if (Array.isArray(content)) {
433
+ if (isSystem) {
434
+ /* System: keep cachePoint, only strip Anthropic cache_control. */
435
+ if (hasAnthropicCacheControl(content)) {
436
+ const stripped = content.map((block) => {
437
+ const rec = block as Record<string, unknown>;
438
+ if ('cache_control' in rec) {
439
+ const { cache_control: _, ...rest } = rec;
440
+ return rest as MessageContentComplex;
441
+ }
442
+ return block;
443
+ });
444
+ return cloneMessage(msg, stripped) as T;
445
+ }
446
+ return cloneMessage(msg, content) as T;
447
+ }
448
+
366
449
  // Strip existing cachePoint blocks and Anthropic-style cache_control
367
450
  const stripped = content
368
451
  .filter((block) => !isCachePoint(block))
@@ -456,6 +539,35 @@ export function addBedrockCacheControl<
456
539
  const message = updatedMessages[i];
457
540
  const msgContent = message.content;
458
541
 
542
+ /* System messages are handled by AgentContext.buildSystemRunnable —
543
+ * the cachePoint is emitted INLINE in the system message content array
544
+ * so the cacheable system prefix gets a marker every turn (not just
545
+ * when the system message happens to be in the last 2 eligible). The
546
+ * Bedrock converter (src/llm/bedrock/utils/message_inputs.ts:261-294)
547
+ * passes through cachePoint blocks in system content arrays.
548
+ *
549
+ * If we ALSO marked system here, we'd: (a) waste one of the two
550
+ * cachePoint budget slots that Bedrock honours per-request, starving
551
+ * the conversation tail, and (b) potentially double-mark a stable
552
+ * prefix that already has its own cachePoint.
553
+ *
554
+ * As a defensive cleanup, strip stray Anthropic-style cache_control
555
+ * markers from the system message — Bedrock ignores them and they
556
+ * can confuse provider validation. */
557
+ const _msgType =
558
+ 'getType' in message && typeof message.getType === 'function'
559
+ ? message.getType()
560
+ : undefined;
561
+ const _msgRole = (message as Record<string, unknown>).role as
562
+ | string
563
+ | undefined;
564
+ if (_msgType === 'system' || _msgRole === 'system') {
565
+ updatedMessages[i] = sanitizeBedrockSystemMessage(
566
+ message as MessageWithContent
567
+ ) as T;
568
+ continue;
569
+ }
570
+
459
571
  // Skip empty/whitespace-only content
460
572
  if (msgContent == null) continue;
461
573
  if (typeof msgContent === 'string' && msgContent.trim() === '') continue;
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Position-based context pruning for tool results.
3
+ *
4
+ * Uses position-based age: the distance of a message
5
+ * from the conversation end as a fraction of total messages.
6
+ *
7
+ * Two degradation levels:
8
+ * - Soft-trim: Keep head + tail of tool result content, drop middle.
9
+ * - Hard-clear: Replace entire content with a placeholder.
10
+ *
11
+ * Messages in the "protected zone" (recent assistant turns, system/pre-first-human
12
+ * messages, and messages with image content) are never pruned.
13
+ */
14
+
15
+ import { ToolMessage, type BaseMessage } from '@langchain/core/messages';
16
+ import type { ContextPruningConfig } from '@/types/graph';
17
+ import type { TokenCounter } from '@/types/run';
18
+ import type { ContextPruningSettings } from './contextPruningSettings';
19
+ import { resolveContextPruningSettings } from './contextPruningSettings';
20
+
21
+ /**
22
+ * Checks if a message contains image content blocks.
23
+ * Messages with images are skipped by position-based content degradation
24
+ * because images cannot be meaningfully soft-trimmed or replaced with placeholders.
25
+ */
26
+ function hasImageContent(message: BaseMessage): boolean {
27
+ if (!Array.isArray(message.content)) {
28
+ return false;
29
+ }
30
+ return message.content.some(
31
+ (block) =>
32
+ typeof block === 'object' &&
33
+ 'type' in block &&
34
+ (block.type === 'image_url' || block.type === 'image')
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Applies head+tail soft-trim to tool result content.
40
+ */
41
+ function softTrimContent(
42
+ content: string,
43
+ settings: ContextPruningSettings['softTrim']
44
+ ): string {
45
+ const { headChars, tailChars } = settings;
46
+ const indicator = `\n\n… [soft-trimmed: ${content.length} chars → ${headChars + tailChars} chars, middle removed] …\n\n`;
47
+ return content.slice(0, headChars) + indicator + content.slice(-tailChars);
48
+ }
49
+
50
+ export interface ContextPruningResult {
51
+ /** Number of messages that were soft-trimmed. */
52
+ softTrimmed: number;
53
+ /** Number of messages that were hard-cleared. */
54
+ hardCleared: number;
55
+ }
56
+
57
+ /**
58
+ * Applies position-based context pruning to tool result messages.
59
+ *
60
+ * Modifies messages in-place and updates indexTokenCountMap with recounted
61
+ * token values for modified messages.
62
+ *
63
+ * @param params.messages - The full message array (modified in-place).
64
+ * @param params.indexTokenCountMap - Token count map (updated in-place).
65
+ * @param params.tokenCounter - Function to recount tokens after modification.
66
+ * @param params.config - Partial context pruning config (merged with defaults).
67
+ * @returns Counts of soft-trimmed and hard-cleared messages.
68
+ */
69
+ export function applyContextPruning(params: {
70
+ messages: BaseMessage[];
71
+ indexTokenCountMap: Record<string, number | undefined>;
72
+ tokenCounter: TokenCounter;
73
+ config?: ContextPruningConfig;
74
+ resolvedSettings?: ContextPruningSettings;
75
+ }): ContextPruningResult {
76
+ const {
77
+ messages,
78
+ indexTokenCountMap,
79
+ tokenCounter,
80
+ config,
81
+ resolvedSettings,
82
+ } = params;
83
+ const settings = resolvedSettings ?? resolveContextPruningSettings(config);
84
+
85
+ if (!settings.enabled || messages.length === 0) {
86
+ return { softTrimmed: 0, hardCleared: 0 };
87
+ }
88
+
89
+ const totalMessages = messages.length;
90
+ let softTrimmed = 0;
91
+ let hardCleared = 0;
92
+
93
+ // Find the protected zone: last N assistant turns from the end.
94
+ // An "assistant turn" is a contiguous sequence of AI + Tool messages.
95
+ const protectedIndices = new Set<number>();
96
+
97
+ // Always protect the system message (index 0 if present)
98
+ if (messages[0]?.getType() === 'system') {
99
+ protectedIndices.add(0);
100
+ }
101
+
102
+ // Protect messages before the first human message
103
+ for (let i = 0; i < totalMessages; i++) {
104
+ if (messages[i].getType() === 'human') {
105
+ break;
106
+ }
107
+ protectedIndices.add(i);
108
+ }
109
+
110
+ // Protect the last N assistant turns (walking backwards)
111
+ let assistantTurnsFound = 0;
112
+ let inAssistantSequence = false;
113
+ for (let i = totalMessages - 1; i >= 0; i--) {
114
+ const type = messages[i].getType();
115
+ if (type === 'ai' || type === 'tool') {
116
+ protectedIndices.add(i);
117
+ if (!inAssistantSequence) {
118
+ inAssistantSequence = true;
119
+ }
120
+ } else {
121
+ if (inAssistantSequence) {
122
+ assistantTurnsFound++;
123
+ inAssistantSequence = false;
124
+ if (assistantTurnsFound >= settings.keepLastAssistants) {
125
+ break;
126
+ }
127
+ }
128
+ // Protect the human message between assistant turns in the protected zone
129
+ if (assistantTurnsFound < settings.keepLastAssistants) {
130
+ protectedIndices.add(i);
131
+ }
132
+ }
133
+ }
134
+
135
+ // Process each tool message outside the protected zone
136
+ for (let i = 0; i < totalMessages; i++) {
137
+ const message = messages[i];
138
+ if (message.getType() !== 'tool') {
139
+ continue;
140
+ }
141
+ if (protectedIndices.has(i)) {
142
+ continue;
143
+ }
144
+ if (hasImageContent(message)) {
145
+ continue;
146
+ }
147
+
148
+ const content = message.content;
149
+ if (typeof content !== 'string') {
150
+ continue;
151
+ }
152
+ if (content.length < settings.minPrunableToolChars) {
153
+ continue;
154
+ }
155
+
156
+ // Compute age ratio: how far back from the end (0 = latest, 1 = oldest)
157
+ const ageRatio = (totalMessages - i) / totalMessages;
158
+
159
+ if (ageRatio >= settings.hardClearRatio && settings.hardClear.enabled) {
160
+ // Hard-clear: replace with placeholder
161
+ const cloned = new ToolMessage({
162
+ content: settings.hardClear.placeholder,
163
+ tool_call_id: (message as ToolMessage).tool_call_id,
164
+ name: message.name,
165
+ id: message.id,
166
+ additional_kwargs: message.additional_kwargs,
167
+ response_metadata: message.response_metadata,
168
+ });
169
+ messages[i] = cloned;
170
+ indexTokenCountMap[i] = tokenCounter(cloned);
171
+ hardCleared++;
172
+ } else if (ageRatio >= settings.softTrimRatio) {
173
+ // Soft-trim: keep head + tail
174
+ if (content.length > settings.softTrim.maxChars) {
175
+ const cloned = new ToolMessage({
176
+ content: softTrimContent(content, settings.softTrim),
177
+ tool_call_id: (message as ToolMessage).tool_call_id,
178
+ name: message.name,
179
+ id: message.id,
180
+ additional_kwargs: message.additional_kwargs,
181
+ response_metadata: message.response_metadata,
182
+ });
183
+ messages[i] = cloned;
184
+ indexTokenCountMap[i] = tokenCounter(cloned);
185
+ softTrimmed++;
186
+ }
187
+ }
188
+ }
189
+
190
+ return { softTrimmed, hardCleared };
191
+ }