@illuma-ai/agents 1.5.1 → 2.1.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 (319) hide show
  1. package/README.md +0 -62
  2. package/dist/cjs/agents/AgentContext.cjs +160 -259
  3. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  4. package/dist/cjs/common/enum.cjs +12 -12
  5. package/dist/cjs/common/enum.cjs.map +1 -1
  6. package/dist/cjs/graphs/Graph.cjs +30 -13
  7. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
  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 +1 -1
  13. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  14. package/dist/cjs/hooks/matchers.cjs +2 -2
  15. package/dist/cjs/hooks/matchers.cjs.map +1 -1
  16. package/dist/cjs/hooks/types.cjs +1 -1
  17. package/dist/cjs/hooks/types.cjs.map +1 -1
  18. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +1 -5
  19. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  20. package/dist/cjs/llm/bedrock/index.cjs +33 -61
  21. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  22. package/dist/cjs/llm/openai/index.cjs +1 -1
  23. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  24. package/dist/cjs/llm/openai/utils/index.cjs +10 -27
  25. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  26. package/dist/cjs/main.cjs +3 -84
  27. package/dist/cjs/main.cjs.map +1 -1
  28. package/dist/cjs/memory/citations.cjs +4 -4
  29. package/dist/cjs/memory/citations.cjs.map +1 -1
  30. package/dist/cjs/memory/constants.cjs +17 -17
  31. package/dist/cjs/memory/constants.cjs.map +1 -1
  32. package/dist/cjs/memory/mmr.cjs +1 -1
  33. package/dist/cjs/memory/mmr.cjs.map +1 -1
  34. package/dist/cjs/memory/paths.cjs +1 -1
  35. package/dist/cjs/memory/paths.cjs.map +1 -1
  36. package/dist/cjs/memory/recallTracking.cjs +3 -3
  37. package/dist/cjs/memory/recallTracking.cjs.map +1 -1
  38. package/dist/cjs/memory/temporalDecay.cjs +2 -2
  39. package/dist/cjs/memory/temporalDecay.cjs.map +1 -1
  40. package/dist/cjs/messages/cache.cjs +0 -89
  41. package/dist/cjs/messages/cache.cjs.map +1 -1
  42. package/dist/cjs/messages/format.cjs +13 -71
  43. package/dist/cjs/messages/format.cjs.map +1 -1
  44. package/dist/cjs/tools/BashExecutor.cjs +11 -21
  45. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  46. package/dist/cjs/tools/CodeExecutor.cjs +13 -41
  47. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  48. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +11 -16
  49. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  50. package/dist/cjs/tools/ToolNode.cjs +78 -13
  51. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  52. package/dist/cjs/tools/memory/memoryAppendTool.cjs +1 -1
  53. package/dist/cjs/tools/memory/memoryAppendTool.cjs.map +1 -1
  54. package/dist/cjs/tools/memory/memoryGetTool.cjs +2 -2
  55. package/dist/cjs/tools/memory/memoryGetTool.cjs.map +1 -1
  56. package/dist/cjs/tools/memory/memorySearchTool.cjs +3 -3
  57. package/dist/cjs/tools/memory/memorySearchTool.cjs.map +1 -1
  58. package/dist/cjs/tools/memory/shared.cjs +1 -1
  59. package/dist/cjs/tools/memory/shared.cjs.map +1 -1
  60. package/dist/cjs/tools/search/search.cjs +3 -11
  61. package/dist/cjs/tools/search/search.cjs.map +1 -1
  62. package/dist/cjs/tools/search/tool.cjs +4 -28
  63. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  64. package/dist/cjs/tools/search/utils.cjs +3 -10
  65. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  66. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +48 -0
  67. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  68. package/dist/cjs/types/graph.cjs.map +1 -1
  69. package/dist/esm/agents/AgentContext.mjs +160 -259
  70. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  71. package/dist/esm/common/enum.mjs +12 -12
  72. package/dist/esm/common/enum.mjs.map +1 -1
  73. package/dist/esm/graphs/Graph.mjs +30 -13
  74. package/dist/esm/graphs/Graph.mjs.map +1 -1
  75. package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
  76. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  77. package/dist/esm/graphs/phases/memoryFlushPhase.mjs +1 -1
  78. package/dist/esm/graphs/phases/memoryFlushPhase.mjs.map +1 -1
  79. package/dist/esm/hooks/HookRegistry.mjs +1 -1
  80. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  81. package/dist/esm/hooks/matchers.mjs +2 -2
  82. package/dist/esm/hooks/matchers.mjs.map +1 -1
  83. package/dist/esm/hooks/types.mjs +1 -1
  84. package/dist/esm/hooks/types.mjs.map +1 -1
  85. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +1 -5
  86. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  87. package/dist/esm/llm/bedrock/index.mjs +34 -61
  88. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  89. package/dist/esm/llm/openai/index.mjs +1 -1
  90. package/dist/esm/llm/openai/index.mjs.map +1 -1
  91. package/dist/esm/llm/openai/utils/index.mjs +10 -27
  92. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  93. package/dist/esm/main.mjs +1 -5
  94. package/dist/esm/main.mjs.map +1 -1
  95. package/dist/esm/memory/citations.mjs +4 -4
  96. package/dist/esm/memory/citations.mjs.map +1 -1
  97. package/dist/esm/memory/constants.mjs +17 -17
  98. package/dist/esm/memory/constants.mjs.map +1 -1
  99. package/dist/esm/memory/mmr.mjs +1 -1
  100. package/dist/esm/memory/mmr.mjs.map +1 -1
  101. package/dist/esm/memory/paths.mjs +1 -1
  102. package/dist/esm/memory/paths.mjs.map +1 -1
  103. package/dist/esm/memory/recallTracking.mjs +3 -3
  104. package/dist/esm/memory/recallTracking.mjs.map +1 -1
  105. package/dist/esm/memory/temporalDecay.mjs +2 -2
  106. package/dist/esm/memory/temporalDecay.mjs.map +1 -1
  107. package/dist/esm/messages/cache.mjs +0 -89
  108. package/dist/esm/messages/cache.mjs.map +1 -1
  109. package/dist/esm/messages/format.mjs +13 -71
  110. package/dist/esm/messages/format.mjs.map +1 -1
  111. package/dist/esm/tools/BashExecutor.mjs +12 -22
  112. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  113. package/dist/esm/tools/CodeExecutor.mjs +14 -41
  114. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  115. package/dist/esm/tools/ProgrammaticToolCalling.mjs +12 -17
  116. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  117. package/dist/esm/tools/ToolNode.mjs +78 -13
  118. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  119. package/dist/esm/tools/memory/memoryAppendTool.mjs +1 -1
  120. package/dist/esm/tools/memory/memoryAppendTool.mjs.map +1 -1
  121. package/dist/esm/tools/memory/memoryGetTool.mjs +2 -2
  122. package/dist/esm/tools/memory/memoryGetTool.mjs.map +1 -1
  123. package/dist/esm/tools/memory/memorySearchTool.mjs +3 -3
  124. package/dist/esm/tools/memory/memorySearchTool.mjs.map +1 -1
  125. package/dist/esm/tools/memory/shared.mjs +1 -1
  126. package/dist/esm/tools/memory/shared.mjs.map +1 -1
  127. package/dist/esm/tools/search/search.mjs +3 -11
  128. package/dist/esm/tools/search/search.mjs.map +1 -1
  129. package/dist/esm/tools/search/tool.mjs +4 -28
  130. package/dist/esm/tools/search/tool.mjs.map +1 -1
  131. package/dist/esm/tools/search/utils.mjs +3 -10
  132. package/dist/esm/tools/search/utils.mjs.map +1 -1
  133. package/dist/esm/tools/subagent/SubagentExecutor.mjs +48 -0
  134. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  135. package/dist/esm/types/graph.mjs.map +1 -1
  136. package/dist/types/agents/AgentContext.d.ts +25 -95
  137. package/dist/types/common/enum.d.ts +12 -12
  138. package/dist/types/graphs/Graph.d.ts +2 -2
  139. package/dist/types/graphs/phases/memoryFlushPhase.d.ts +2 -2
  140. package/dist/types/hooks/HookRegistry.d.ts +1 -1
  141. package/dist/types/hooks/matchers.d.ts +2 -2
  142. package/dist/types/hooks/types.d.ts +1 -1
  143. package/dist/types/index.d.ts +0 -1
  144. package/dist/types/llm/bedrock/index.d.ts +1 -54
  145. package/dist/types/llm/openai/index.d.ts +1 -1
  146. package/dist/types/memory/citations.d.ts +4 -4
  147. package/dist/types/memory/constants.d.ts +17 -17
  148. package/dist/types/memory/mmr.d.ts +3 -3
  149. package/dist/types/memory/paths.d.ts +1 -1
  150. package/dist/types/memory/temporalDecay.d.ts +2 -2
  151. package/dist/types/memory/types.d.ts +3 -3
  152. package/dist/types/messages/format.d.ts +2 -5
  153. package/dist/types/tools/CodeExecutor.d.ts +0 -6
  154. package/dist/types/tools/ToolNode.d.ts +3 -3
  155. package/dist/types/tools/memory/shared.d.ts +1 -1
  156. package/dist/types/tools/search/test.d.ts +1 -0
  157. package/dist/types/tools/search/types.d.ts +5 -99
  158. package/dist/types/tools/search/utils.d.ts +2 -2
  159. package/dist/types/tools/subagent/SubagentExecutor.d.ts +29 -0
  160. package/dist/types/types/graph.d.ts +30 -34
  161. package/dist/types/types/index.d.ts +0 -1
  162. package/dist/types/types/messages.d.ts +1 -1
  163. package/dist/types/types/run.d.ts +1 -3
  164. package/dist/types/types/tools.d.ts +5 -14
  165. package/package.json +1 -61
  166. package/src/agents/AgentContext.test.ts +176 -0
  167. package/src/agents/AgentContext.ts +179 -305
  168. package/src/agents/__tests__/AgentContext.test.ts +0 -632
  169. package/src/common/__tests__/enum.test.ts +1 -1
  170. package/src/common/enum.ts +12 -12
  171. package/src/graphs/Graph.ts +32 -13
  172. package/src/graphs/MultiAgentGraph.ts +1 -1
  173. package/src/graphs/gapFeatures.test.ts +1 -1
  174. package/src/graphs/phases/__tests__/memoryFlushPhase.test.ts +1 -1
  175. package/src/graphs/phases/memoryFlushPhase.ts +2 -2
  176. package/src/hooks/HookRegistry.ts +1 -1
  177. package/src/hooks/index.ts +1 -1
  178. package/src/hooks/matchers.ts +2 -2
  179. package/src/hooks/types.ts +1 -1
  180. package/src/index.ts +0 -6
  181. package/src/llm/anthropic/utils/message_inputs.ts +1 -10
  182. package/src/llm/bedrock/__tests__/bedrock-caching.test.ts +18 -166
  183. package/src/llm/bedrock/index.ts +41 -116
  184. package/src/llm/openai/index.ts +2 -2
  185. package/src/llm/openai/utils/index.ts +14 -31
  186. package/src/memory/citations.ts +4 -4
  187. package/src/memory/constants.ts +17 -17
  188. package/src/memory/mmr.ts +3 -3
  189. package/src/memory/paths.ts +1 -1
  190. package/src/memory/recallTracking.ts +3 -3
  191. package/src/memory/temporalDecay.ts +2 -2
  192. package/src/memory/types.ts +3 -3
  193. package/src/messages/cache.test.ts +24 -62
  194. package/src/messages/cache.ts +0 -112
  195. package/src/messages/ensureThinkingBlock.test.ts +1 -1
  196. package/src/messages/format.ts +13 -92
  197. package/src/messages/formatAgentMessages.test.ts +1 -1
  198. package/src/scripts/subagent-configurable-inheritance.ts +263 -0
  199. package/src/scripts/subagent-event-driven-debug.ts +2 -2
  200. package/src/specs/anthropic.simple.test.ts +0 -61
  201. package/src/specs/prune.orphans.test.ts +1 -1
  202. package/src/tools/BashExecutor.ts +13 -37
  203. package/src/tools/CodeExecutor.ts +14 -59
  204. package/src/tools/ProgrammaticToolCalling.ts +14 -29
  205. package/src/tools/ToolNode.ts +75 -14
  206. package/src/tools/__tests__/CodeExecutor.test.ts +3 -3
  207. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -60
  208. package/src/tools/__tests__/SubagentExecutor.test.ts +157 -0
  209. package/src/tools/memory/memoryAppendTool.ts +1 -1
  210. package/src/tools/memory/memoryGetTool.ts +2 -2
  211. package/src/tools/memory/memorySearchTool.ts +3 -3
  212. package/src/tools/memory/shared.ts +1 -1
  213. package/src/tools/search/output.md +2775 -0
  214. package/src/tools/search/search.ts +2 -12
  215. package/src/tools/search/test.html +884 -0
  216. package/src/tools/search/test.md +643 -0
  217. package/src/tools/search/test.ts +159 -0
  218. package/src/tools/search/tool.ts +2 -36
  219. package/src/tools/search/types.ts +8 -133
  220. package/src/tools/search/utils.ts +5 -13
  221. package/src/tools/subagent/SubagentExecutor.ts +78 -0
  222. package/src/types/graph.ts +27 -34
  223. package/src/types/index.ts +0 -1
  224. package/src/types/messages.ts +1 -1
  225. package/src/types/run.ts +1 -3
  226. package/src/types/tools.ts +5 -14
  227. package/dist/cjs/langchain/google-common.cjs +0 -3
  228. package/dist/cjs/langchain/google-common.cjs.map +0 -1
  229. package/dist/cjs/langchain/index.cjs +0 -86
  230. package/dist/cjs/langchain/index.cjs.map +0 -1
  231. package/dist/cjs/langchain/language_models/chat_models.cjs +0 -3
  232. package/dist/cjs/langchain/language_models/chat_models.cjs.map +0 -1
  233. package/dist/cjs/langchain/messages/tool.cjs +0 -3
  234. package/dist/cjs/langchain/messages/tool.cjs.map +0 -1
  235. package/dist/cjs/langchain/messages.cjs +0 -51
  236. package/dist/cjs/langchain/messages.cjs.map +0 -1
  237. package/dist/cjs/langchain/openai.cjs +0 -3
  238. package/dist/cjs/langchain/openai.cjs.map +0 -1
  239. package/dist/cjs/langchain/prompts.cjs +0 -11
  240. package/dist/cjs/langchain/prompts.cjs.map +0 -1
  241. package/dist/cjs/langchain/runnables.cjs +0 -19
  242. package/dist/cjs/langchain/runnables.cjs.map +0 -1
  243. package/dist/cjs/langchain/tools.cjs +0 -23
  244. package/dist/cjs/langchain/tools.cjs.map +0 -1
  245. package/dist/cjs/langchain/utils/env.cjs +0 -11
  246. package/dist/cjs/langchain/utils/env.cjs.map +0 -1
  247. package/dist/cjs/llm/bedrock/cacheSupport.cjs +0 -55
  248. package/dist/cjs/llm/bedrock/cacheSupport.cjs.map +0 -1
  249. package/dist/cjs/tools/search/tavily-scraper.cjs +0 -189
  250. package/dist/cjs/tools/search/tavily-scraper.cjs.map +0 -1
  251. package/dist/cjs/tools/search/tavily-search.cjs +0 -372
  252. package/dist/cjs/tools/search/tavily-search.cjs.map +0 -1
  253. package/dist/cjs/types/agent-cache.cjs +0 -54
  254. package/dist/cjs/types/agent-cache.cjs.map +0 -1
  255. package/dist/esm/langchain/google-common.mjs +0 -2
  256. package/dist/esm/langchain/google-common.mjs.map +0 -1
  257. package/dist/esm/langchain/index.mjs +0 -5
  258. package/dist/esm/langchain/index.mjs.map +0 -1
  259. package/dist/esm/langchain/language_models/chat_models.mjs +0 -2
  260. package/dist/esm/langchain/language_models/chat_models.mjs.map +0 -1
  261. package/dist/esm/langchain/messages/tool.mjs +0 -2
  262. package/dist/esm/langchain/messages/tool.mjs.map +0 -1
  263. package/dist/esm/langchain/messages.mjs +0 -2
  264. package/dist/esm/langchain/messages.mjs.map +0 -1
  265. package/dist/esm/langchain/openai.mjs +0 -2
  266. package/dist/esm/langchain/openai.mjs.map +0 -1
  267. package/dist/esm/langchain/prompts.mjs +0 -2
  268. package/dist/esm/langchain/prompts.mjs.map +0 -1
  269. package/dist/esm/langchain/runnables.mjs +0 -2
  270. package/dist/esm/langchain/runnables.mjs.map +0 -1
  271. package/dist/esm/langchain/tools.mjs +0 -2
  272. package/dist/esm/langchain/tools.mjs.map +0 -1
  273. package/dist/esm/langchain/utils/env.mjs +0 -2
  274. package/dist/esm/langchain/utils/env.mjs.map +0 -1
  275. package/dist/esm/llm/bedrock/cacheSupport.mjs +0 -52
  276. package/dist/esm/llm/bedrock/cacheSupport.mjs.map +0 -1
  277. package/dist/esm/tools/search/tavily-scraper.mjs +0 -186
  278. package/dist/esm/tools/search/tavily-scraper.mjs.map +0 -1
  279. package/dist/esm/tools/search/tavily-search.mjs +0 -370
  280. package/dist/esm/tools/search/tavily-search.mjs.map +0 -1
  281. package/dist/esm/types/agent-cache.mjs +0 -52
  282. package/dist/esm/types/agent-cache.mjs.map +0 -1
  283. package/dist/types/langchain/google-common.d.ts +0 -1
  284. package/dist/types/langchain/index.d.ts +0 -8
  285. package/dist/types/langchain/language_models/chat_models.d.ts +0 -1
  286. package/dist/types/langchain/messages/tool.d.ts +0 -1
  287. package/dist/types/langchain/messages.d.ts +0 -2
  288. package/dist/types/langchain/openai.d.ts +0 -1
  289. package/dist/types/langchain/prompts.d.ts +0 -1
  290. package/dist/types/langchain/runnables.d.ts +0 -2
  291. package/dist/types/langchain/tools.d.ts +0 -2
  292. package/dist/types/langchain/utils/env.d.ts +0 -1
  293. package/dist/types/llm/bedrock/cacheSupport.d.ts +0 -35
  294. package/dist/types/tools/search/tavily-scraper.d.ts +0 -19
  295. package/dist/types/tools/search/tavily-search.d.ts +0 -4
  296. package/dist/types/tools/subagent/types.d.ts +0 -84
  297. package/dist/types/types/agent-cache.d.ts +0 -71
  298. package/src/agents/__tests__/AgentContext.cacheTtl.live.test.ts +0 -259
  299. package/src/agents/__tests__/AgentContext.crossAgentTier1.live.test.ts +0 -266
  300. package/src/agents/__tests__/AgentContext.crossUserCache.live.test.ts +0 -342
  301. package/src/langchain/google-common.ts +0 -1
  302. package/src/langchain/index.ts +0 -8
  303. package/src/langchain/language_models/chat_models.ts +0 -1
  304. package/src/langchain/messages/tool.ts +0 -5
  305. package/src/langchain/messages.ts +0 -21
  306. package/src/langchain/openai.ts +0 -1
  307. package/src/langchain/prompts.ts +0 -1
  308. package/src/langchain/runnables.ts +0 -7
  309. package/src/langchain/tools.ts +0 -8
  310. package/src/langchain/utils/env.ts +0 -1
  311. package/src/llm/anthropic/utils/server-tool-inputs.test.ts +0 -436
  312. package/src/llm/bedrock/cacheSupport.test.ts +0 -99
  313. package/src/llm/bedrock/cacheSupport.ts +0 -53
  314. package/src/tools/search/tavily-scraper.ts +0 -235
  315. package/src/tools/search/tavily-search.ts +0 -424
  316. package/src/tools/search/tavily.test.ts +0 -965
  317. package/src/tools/subagent/types.test.ts +0 -70
  318. package/src/tools/subagent/types.ts +0 -115
  319. package/src/types/agent-cache.ts +0 -74
@@ -1 +1 @@
1
- {"version":3,"file":"paths.mjs","sources":["../../../src/memory/paths.ts"],"sourcesContent":["/**\n * Autonomous memory — canonical path whitelist + tier utilities.\n *\n * Single source of truth for the 8 stable memory documents. Every write\n * goes through {@link assertWritablePath}; every reader can ask\n * {@link getTierForPath} what a row belongs to. Adding or removing a\n * path means editing this file — and exactly this file.\n *\n * ## Why a whitelist?\n *\n * Earlier historical designs used date-keyed files\n * (`memory/YYYY-MM-DD.md`), which have three problems for a persistent\n * multi-user agent:\n *\n * 1. **Unbounded growth** — one row per day per agent per user, forever.\n * 2. **LLM routing ambiguity** — \"which date file do I read?\" has no\n * good answer, so the model reads (and ranks against) all of them.\n * 3. **No natural deduplication** — the same fact written across three\n * days returns three near-identical hits.\n *\n * With 8 stable canonical documents the LLM knows *exactly* where to\n * write (\"is this a preference? → `memory/user/preferences.md`\") and\n * each row accumulates via UPSERT instead of piling up new rows.\n *\n * ## Two tiers\n *\n * - **Agent tier** (`memory/agent/*`) — operational knowledge that\n * every caller of the agent benefits from. Stored with `user_id = NULL`.\n * Shared across all users of a collaborative agent; the only tier that\n * exists for isolated/autonomous agents with no invoker.\n *\n * - **User tier** (`memory/user/*`) — per-invoker personalization.\n * Stored with `user_id = <caller>`. Read path filters so User A never\n * sees User B's rows, even for the same agent. Not writable unless the\n * flush scope carries a non-empty `userId`.\n *\n * The tier of a path is a function of its prefix (`memory/agent/` vs\n * `memory/user/`), so adding a new document is one line here + the\n * constant array entry; no store or route code has to change.\n */\n\nimport { MEMORY_PATH_PREFIX } from './constants';\nimport type { MemoryScope } from './types';\n\n/** Tier discriminator — the two kinds of memory a row can belong to. */\nexport type MemoryTier = 'agent' | 'user';\n\n/** Every canonical document the flush phase is allowed to write. */\nexport interface MemoryPathDescriptor {\n /** Full path including the `memory/` prefix. */\n path: string;\n /** Which tier the path lives under. */\n tier: MemoryTier;\n /** Short label shown in UI tier badges and the flush prompt rubric. */\n label: string;\n /** One-line description — fed to the LLM to make routing unambiguous. */\n description: string;\n}\n\n/** Agent-tier documents — shared across all users of the agent. */\nexport const MEMORY_AGENT_PATHS: readonly MemoryPathDescriptor[] =\n Object.freeze([\n Object.freeze({\n path: 'memory/agent/playbook.md',\n tier: 'agent' as const,\n label: 'Playbook',\n description:\n 'Successful task patterns and workflows that have worked for this agent — ' +\n 'reusable recipes, known-good tool sequences, approaches that consistently ' +\n 'produce the right result.',\n }),\n Object.freeze({\n path: 'memory/agent/pitfalls.md',\n tier: 'agent' as const,\n label: 'Pitfalls',\n description:\n 'Tool failures, error recoveries, schema mistakes, and invalid argument ' +\n 'shapes the agent has seen — so the same mistake is not repeated on future ' +\n 'turns. One note per distinct failure mode.',\n }),\n Object.freeze({\n path: 'memory/agent/domain.md',\n tier: 'agent' as const,\n label: 'Domain',\n description:\n 'Durable facts about the systems, APIs, data models, and business rules ' +\n 'this agent operates on — things that are true independent of any one user ' +\n 'and that the agent repeatedly needs to know.',\n }),\n Object.freeze({\n path: 'memory/agent/style.md',\n tier: 'agent' as const,\n label: 'Style',\n description:\n 'Tone, formatting, and response-shape conventions the agent has converged ' +\n 'on across the whole user base. Do NOT write user-specific preferences here ' +\n '(those belong under the user tier).',\n }),\n ]);\n\n/** User-tier documents — per-invoker, never shared across users. */\nexport const MEMORY_USER_PATHS: readonly MemoryPathDescriptor[] = Object.freeze(\n [\n Object.freeze({\n path: 'memory/user/profile.md',\n tier: 'user' as const,\n label: 'Profile',\n description:\n \"This specific user's identity — name, role, team, sign-off name, \" +\n 'responsibilities, stable facts about who they are. Only facts ' +\n 'volunteered by the user themselves.',\n }),\n Object.freeze({\n path: 'memory/user/preferences.md',\n tier: 'user' as const,\n label: 'Preferences',\n description:\n 'How THIS user wants things done — preferred formats, verbosity, tone, ' +\n 'cadence, output length, language conventions. Explicit corrections they ' +\n 'have given count as preferences.',\n }),\n Object.freeze({\n path: 'memory/user/projects.md',\n tier: 'user' as const,\n label: 'Projects',\n description:\n \"This user's current initiatives, ongoing work, deadlines, and active \" +\n 'project context. Include dates in the prose when relevant. Retire entries ' +\n 'by overwriting them on future flushes when the user indicates a project is done.',\n }),\n Object.freeze({\n path: 'memory/user/references.md',\n tier: 'user' as const,\n label: 'References',\n description:\n 'External systems, dashboards, accounts, links, channels, and resources ' +\n \"THIS user has pointed the agent at — the 'where to look' pointers that \" +\n 'make later turns more efficient.',\n }),\n ]\n);\n\n/** All 8 canonical documents in display order: agent tier first, user tier second. */\nexport const MEMORY_ALL_PATHS: readonly MemoryPathDescriptor[] = Object.freeze([\n ...MEMORY_AGENT_PATHS,\n ...MEMORY_USER_PATHS,\n]);\n\n/** Fast O(1) lookup map: path → descriptor. */\nconst PATH_INDEX: ReadonlyMap<string, MemoryPathDescriptor> = new Map(\n MEMORY_ALL_PATHS.map((p) => [p.path, p])\n);\n\n/** Set of all whitelisted paths — for `has()` lookups. */\nexport const MEMORY_WRITABLE_PATHS: ReadonlySet<string> = new Set(\n MEMORY_ALL_PATHS.map((p) => p.path)\n);\n\n/** Returns the descriptor for a canonical path, or `undefined` if not on the whitelist. */\nexport function getPathDescriptor(\n path: string\n): MemoryPathDescriptor | undefined {\n return PATH_INDEX.get(path);\n}\n\n/**\n * Returns the tier a given path belongs to.\n *\n * For unknown paths (e.g. legacy date-keyed rows that predate this schema),\n * falls back to inspecting the `memory/agent/` or `memory/user/` prefix.\n * Anything that matches neither is classified as `agent` — it will surface\n * in the admin UI under the agent-tier header and stay read-only there.\n */\nexport function getTierForPath(path: string): MemoryTier {\n const descriptor = PATH_INDEX.get(path);\n if (descriptor) return descriptor.tier;\n if (path.startsWith('memory/user/')) return 'user';\n return 'agent';\n}\n\n/**\n * Paths that are legal to write **for this caller's scope**.\n *\n * If `scope.userId` is absent (isolated/autonomous agent), the user-tier\n * paths are dropped. This is the list the flush prompt renders into its\n * rubric — the LLM never sees paths it cannot write to.\n */\nexport function getWritablePathsForScope(\n scope: Pick<MemoryScope, 'userId'>\n): readonly MemoryPathDescriptor[] {\n if (scope.userId == null || scope.userId === '') {\n return MEMORY_AGENT_PATHS;\n }\n return MEMORY_ALL_PATHS;\n}\n\n/**\n * Validates a write path against the whitelist AND the caller's scope.\n *\n * Throws with an actionable message when:\n * - the path is missing the `memory/` prefix\n * - the path is not on the 8-doc whitelist\n * - the path is user-tier but `scope.userId` is missing\n *\n * The store's append() is the single call site; other callers should\n * never hit this directly. Kept as a standalone function for testability.\n */\nexport function assertWritablePath(\n path: string,\n scope: Pick<MemoryScope, 'userId'>\n): MemoryPathDescriptor {\n if (!path || !path.startsWith(MEMORY_PATH_PREFIX)) {\n throw new Error(\n `memory_append path must start with \"${MEMORY_PATH_PREFIX}\" (received \"${path}\")`\n );\n }\n const descriptor = PATH_INDEX.get(path);\n if (!descriptor) {\n throw new Error(\n `memory_append path \"${path}\" is not on the canonical whitelist. ` +\n `Allowed: ${MEMORY_ALL_PATHS.map((p) => p.path).join(', ')}`\n );\n }\n if (\n descriptor.tier === 'user' &&\n (scope.userId == null || scope.userId === '')\n ) {\n throw new Error(\n `memory_append to user-tier path \"${path}\" requires a caller userId in scope, ` +\n `but scope.userId is missing. This path is private to a specific user and ` +\n `cannot be written from an isolated/autonomous context.`\n );\n }\n return descriptor;\n}\n\n/**\n * Renders the whitelist as a compact bullet list, used inside the flush\n * prompt so the LLM sees the authoritative rubric at inference time.\n *\n * Output shape:\n * - memory/agent/playbook.md [Playbook, shared] — <description>\n * - memory/agent/pitfalls.md [Pitfalls, shared] — <description>\n * ...\n *\n * The `[Label, shared|private]` tag tells the model whether the doc is\n * visible to all users (agent tier) or only to the caller (user tier).\n * Filtered to only the paths writable for the given scope.\n */\nexport function renderPathsRubric(scope: Pick<MemoryScope, 'userId'>): string {\n const writable = getWritablePathsForScope(scope);\n return writable\n .map((p) => {\n const visibility = p.tier === 'agent' ? 'shared' : 'private';\n return `- ${p.path} [${p.label}, ${visibility}] — ${p.description}`;\n })\n .join('\\n');\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"paths.mjs","sources":["../../../src/memory/paths.ts"],"sourcesContent":["/**\n * Autonomous memory — canonical path whitelist + tier utilities.\n *\n * Single source of truth for the 8 stable memory documents. Every write\n * goes through {@link assertWritablePath}; every reader can ask\n * {@link getTierForPath} what a row belongs to. Adding or removing a\n * path means editing this file — and exactly this file.\n *\n * ## Why a whitelist?\n *\n * Earlier upstream-faithful designs used date-keyed files\n * (`memory/YYYY-MM-DD.md`), which have three problems for a persistent\n * multi-user agent:\n *\n * 1. **Unbounded growth** — one row per day per agent per user, forever.\n * 2. **LLM routing ambiguity** — \"which date file do I read?\" has no\n * good answer, so the model reads (and ranks against) all of them.\n * 3. **No natural deduplication** — the same fact written across three\n * days returns three near-identical hits.\n *\n * With 8 stable canonical documents the LLM knows *exactly* where to\n * write (\"is this a preference? → `memory/user/preferences.md`\") and\n * each row accumulates via UPSERT instead of piling up new rows.\n *\n * ## Two tiers\n *\n * - **Agent tier** (`memory/agent/*`) — operational knowledge that\n * every caller of the agent benefits from. Stored with `user_id = NULL`.\n * Shared across all users of a collaborative agent; the only tier that\n * exists for isolated/autonomous agents with no invoker.\n *\n * - **User tier** (`memory/user/*`) — per-invoker personalization.\n * Stored with `user_id = <caller>`. Read path filters so User A never\n * sees User B's rows, even for the same agent. Not writable unless the\n * flush scope carries a non-empty `userId`.\n *\n * The tier of a path is a function of its prefix (`memory/agent/` vs\n * `memory/user/`), so adding a new document is one line here + the\n * constant array entry; no store or route code has to change.\n */\n\nimport { MEMORY_PATH_PREFIX } from './constants';\nimport type { MemoryScope } from './types';\n\n/** Tier discriminator — the two kinds of memory a row can belong to. */\nexport type MemoryTier = 'agent' | 'user';\n\n/** Every canonical document the flush phase is allowed to write. */\nexport interface MemoryPathDescriptor {\n /** Full path including the `memory/` prefix. */\n path: string;\n /** Which tier the path lives under. */\n tier: MemoryTier;\n /** Short label shown in UI tier badges and the flush prompt rubric. */\n label: string;\n /** One-line description — fed to the LLM to make routing unambiguous. */\n description: string;\n}\n\n/** Agent-tier documents — shared across all users of the agent. */\nexport const MEMORY_AGENT_PATHS: readonly MemoryPathDescriptor[] =\n Object.freeze([\n Object.freeze({\n path: 'memory/agent/playbook.md',\n tier: 'agent' as const,\n label: 'Playbook',\n description:\n 'Successful task patterns and workflows that have worked for this agent — ' +\n 'reusable recipes, known-good tool sequences, approaches that consistently ' +\n 'produce the right result.',\n }),\n Object.freeze({\n path: 'memory/agent/pitfalls.md',\n tier: 'agent' as const,\n label: 'Pitfalls',\n description:\n 'Tool failures, error recoveries, schema mistakes, and invalid argument ' +\n 'shapes the agent has seen — so the same mistake is not repeated on future ' +\n 'turns. One note per distinct failure mode.',\n }),\n Object.freeze({\n path: 'memory/agent/domain.md',\n tier: 'agent' as const,\n label: 'Domain',\n description:\n 'Durable facts about the systems, APIs, data models, and business rules ' +\n 'this agent operates on — things that are true independent of any one user ' +\n 'and that the agent repeatedly needs to know.',\n }),\n Object.freeze({\n path: 'memory/agent/style.md',\n tier: 'agent' as const,\n label: 'Style',\n description:\n 'Tone, formatting, and response-shape conventions the agent has converged ' +\n 'on across the whole user base. Do NOT write user-specific preferences here ' +\n '(those belong under the user tier).',\n }),\n ]);\n\n/** User-tier documents — per-invoker, never shared across users. */\nexport const MEMORY_USER_PATHS: readonly MemoryPathDescriptor[] = Object.freeze(\n [\n Object.freeze({\n path: 'memory/user/profile.md',\n tier: 'user' as const,\n label: 'Profile',\n description:\n \"This specific user's identity — name, role, team, sign-off name, \" +\n 'responsibilities, stable facts about who they are. Only facts ' +\n 'volunteered by the user themselves.',\n }),\n Object.freeze({\n path: 'memory/user/preferences.md',\n tier: 'user' as const,\n label: 'Preferences',\n description:\n 'How THIS user wants things done — preferred formats, verbosity, tone, ' +\n 'cadence, output length, language conventions. Explicit corrections they ' +\n 'have given count as preferences.',\n }),\n Object.freeze({\n path: 'memory/user/projects.md',\n tier: 'user' as const,\n label: 'Projects',\n description:\n \"This user's current initiatives, ongoing work, deadlines, and active \" +\n 'project context. Include dates in the prose when relevant. Retire entries ' +\n 'by overwriting them on future flushes when the user indicates a project is done.',\n }),\n Object.freeze({\n path: 'memory/user/references.md',\n tier: 'user' as const,\n label: 'References',\n description:\n 'External systems, dashboards, accounts, links, channels, and resources ' +\n \"THIS user has pointed the agent at — the 'where to look' pointers that \" +\n 'make later turns more efficient.',\n }),\n ]\n);\n\n/** All 8 canonical documents in display order: agent tier first, user tier second. */\nexport const MEMORY_ALL_PATHS: readonly MemoryPathDescriptor[] = Object.freeze([\n ...MEMORY_AGENT_PATHS,\n ...MEMORY_USER_PATHS,\n]);\n\n/** Fast O(1) lookup map: path → descriptor. */\nconst PATH_INDEX: ReadonlyMap<string, MemoryPathDescriptor> = new Map(\n MEMORY_ALL_PATHS.map((p) => [p.path, p])\n);\n\n/** Set of all whitelisted paths — for `has()` lookups. */\nexport const MEMORY_WRITABLE_PATHS: ReadonlySet<string> = new Set(\n MEMORY_ALL_PATHS.map((p) => p.path)\n);\n\n/** Returns the descriptor for a canonical path, or `undefined` if not on the whitelist. */\nexport function getPathDescriptor(\n path: string\n): MemoryPathDescriptor | undefined {\n return PATH_INDEX.get(path);\n}\n\n/**\n * Returns the tier a given path belongs to.\n *\n * For unknown paths (e.g. legacy date-keyed rows that predate this schema),\n * falls back to inspecting the `memory/agent/` or `memory/user/` prefix.\n * Anything that matches neither is classified as `agent` — it will surface\n * in the admin UI under the agent-tier header and stay read-only there.\n */\nexport function getTierForPath(path: string): MemoryTier {\n const descriptor = PATH_INDEX.get(path);\n if (descriptor) return descriptor.tier;\n if (path.startsWith('memory/user/')) return 'user';\n return 'agent';\n}\n\n/**\n * Paths that are legal to write **for this caller's scope**.\n *\n * If `scope.userId` is absent (isolated/autonomous agent), the user-tier\n * paths are dropped. This is the list the flush prompt renders into its\n * rubric — the LLM never sees paths it cannot write to.\n */\nexport function getWritablePathsForScope(\n scope: Pick<MemoryScope, 'userId'>\n): readonly MemoryPathDescriptor[] {\n if (scope.userId == null || scope.userId === '') {\n return MEMORY_AGENT_PATHS;\n }\n return MEMORY_ALL_PATHS;\n}\n\n/**\n * Validates a write path against the whitelist AND the caller's scope.\n *\n * Throws with an actionable message when:\n * - the path is missing the `memory/` prefix\n * - the path is not on the 8-doc whitelist\n * - the path is user-tier but `scope.userId` is missing\n *\n * The store's append() is the single call site; other callers should\n * never hit this directly. Kept as a standalone function for testability.\n */\nexport function assertWritablePath(\n path: string,\n scope: Pick<MemoryScope, 'userId'>\n): MemoryPathDescriptor {\n if (!path || !path.startsWith(MEMORY_PATH_PREFIX)) {\n throw new Error(\n `memory_append path must start with \"${MEMORY_PATH_PREFIX}\" (received \"${path}\")`\n );\n }\n const descriptor = PATH_INDEX.get(path);\n if (!descriptor) {\n throw new Error(\n `memory_append path \"${path}\" is not on the canonical whitelist. ` +\n `Allowed: ${MEMORY_ALL_PATHS.map((p) => p.path).join(', ')}`\n );\n }\n if (\n descriptor.tier === 'user' &&\n (scope.userId == null || scope.userId === '')\n ) {\n throw new Error(\n `memory_append to user-tier path \"${path}\" requires a caller userId in scope, ` +\n `but scope.userId is missing. This path is private to a specific user and ` +\n `cannot be written from an isolated/autonomous context.`\n );\n }\n return descriptor;\n}\n\n/**\n * Renders the whitelist as a compact bullet list, used inside the flush\n * prompt so the LLM sees the authoritative rubric at inference time.\n *\n * Output shape:\n * - memory/agent/playbook.md [Playbook, shared] — <description>\n * - memory/agent/pitfalls.md [Pitfalls, shared] — <description>\n * ...\n *\n * The `[Label, shared|private]` tag tells the model whether the doc is\n * visible to all users (agent tier) or only to the caller (user tier).\n * Filtered to only the paths writable for the given scope.\n */\nexport function renderPathsRubric(scope: Pick<MemoryScope, 'userId'>): string {\n const writable = getWritablePathsForScope(scope);\n return writable\n .map((p) => {\n const visibility = p.tier === 'agent' ? 'shared' : 'private';\n return `- ${p.path} [${p.label}, ${visibility}] — ${p.description}`;\n })\n .join('\\n');\n}\n"],"names":[],"mappings":"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"}
@@ -3,9 +3,9 @@ import { createHash } from 'crypto';
3
3
  /**
4
4
  * Recall tracking — Phase 2.
5
5
  *
6
- * Lightweight adaptation of an external recall-tracking module
6
+ * Lightweight adaptation of upstream
7
7
  * `extensions/memory-core/src/short-term-promotion.ts::recordShortTermRecalls`.
8
- * The reference implementation stores recalls in a JSON file under `memory/.dreams/`; we store
8
+ * Upstream stores recalls in a JSON file under `memory/.dreams/`; we store
9
9
  * them in a Postgres table `agent_memory_recalls`. Schema captures what the
10
10
  * future Phase 3 dreaming/promotion algorithm will need:
11
11
  * - which memory row was surfaced (`memory_id`)
@@ -65,7 +65,7 @@ class PgvectorRecallTracker {
65
65
  const qhash = hashQuery(params.query);
66
66
  const bucket = dayBucket(nowMs);
67
67
  // [recall-tracking] debug: upsert one row per (agent, memory, query, day)
68
- // The reference implementation dedupes per-day per-query so repeated searches don't inflate counts.
68
+ // Upstream dedupes per-day per-query so repeated searches don't inflate counts.
69
69
  const values = [];
70
70
  const args = [];
71
71
  let i = 1;
@@ -1 +1 @@
1
- {"version":3,"file":"recallTracking.mjs","sources":["../../../src/memory/recallTracking.ts"],"sourcesContent":["/**\n * Recall tracking — Phase 2.\n *\n * Lightweight adaptation of an external recall-tracking module\n * `extensions/memory-core/src/short-term-promotion.ts::recordShortTermRecalls`.\n * The reference implementation stores recalls in a JSON file under `memory/.dreams/`; we store\n * them in a Postgres table `agent_memory_recalls`. Schema captures what the\n * future Phase 3 dreaming/promotion algorithm will need:\n * - which memory row was surfaced (`memory_id`)\n * - the query that surfaced it (raw + SHA-256 hash for dedupe)\n * - hybrid score at the time of recall\n * - the day bucket (for per-day dedupe / frequency counting)\n * - the recorded timestamp\n *\n * Best-effort: failures never block memory_search. The caller fires\n * {@link RecallTracker.record} without awaiting the result and ignores errors.\n */\nimport { createHash } from 'crypto';\nimport type { Pool } from 'pg';\n\nexport interface RecallTracker {\n /** Record that the given memory ids were surfaced to the model for a query. */\n record(params: RecallRecordParams): Promise<void>;\n /** Backend-specific schema migration. Idempotent. */\n migrate(): Promise<void>;\n}\n\nexport interface RecallRecordParams {\n agentId: string;\n query: string;\n hits: Array<{ id: string; path: string; score: number }>;\n nowMs?: number;\n}\n\nexport const RECALL_TABLE = 'agent_memory_recalls';\n\nfunction hashQuery(query: string): string {\n return createHash('sha256')\n .update(query.trim().toLowerCase())\n .digest('hex')\n .slice(0, 32);\n}\n\nfunction dayBucket(nowMs: number): string {\n const d = new Date(nowMs);\n const y = d.getUTCFullYear();\n const m = String(d.getUTCMonth() + 1).padStart(2, '0');\n const day = String(d.getUTCDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\nexport class PgvectorRecallTracker implements RecallTracker {\n constructor(\n private readonly pool: Pool,\n private readonly table: string = RECALL_TABLE\n ) {}\n\n async migrate(): Promise<void> {\n // [recall-tracking] debug: create table + indexes if missing\n await this.pool.query(`\n CREATE TABLE IF NOT EXISTS ${this.table} (\n id BIGSERIAL PRIMARY KEY,\n agent_id TEXT NOT NULL,\n memory_id TEXT NOT NULL,\n memory_path TEXT NOT NULL,\n query TEXT NOT NULL,\n query_hash TEXT NOT NULL,\n score DOUBLE PRECISION NOT NULL,\n day_bucket TEXT NOT NULL,\n recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n )\n `);\n await this.pool.query(\n `CREATE INDEX IF NOT EXISTS ${this.table}_agent_day_idx ON ${this.table} (agent_id, day_bucket)`\n );\n await this.pool.query(\n `CREATE INDEX IF NOT EXISTS ${this.table}_memory_idx ON ${this.table} (agent_id, memory_id)`\n );\n await this.pool.query(\n `CREATE UNIQUE INDEX IF NOT EXISTS ${this.table}_dedupe_idx\n ON ${this.table} (agent_id, memory_id, query_hash, day_bucket)`\n );\n }\n\n async record(params: RecallRecordParams): Promise<void> {\n if (!params.agentId || !params.query.trim() || params.hits.length === 0)\n return;\n const nowMs = params.nowMs ?? Date.now();\n const qhash = hashQuery(params.query);\n const bucket = dayBucket(nowMs);\n\n // [recall-tracking] debug: upsert one row per (agent, memory, query, day)\n // The reference implementation dedupes per-day per-query so repeated searches don't inflate counts.\n const values: string[] = [];\n const args: unknown[] = [];\n let i = 1;\n for (const hit of params.hits) {\n values.push(\n `($${i++}, $${i++}, $${i++}, $${i++}, $${i++}, $${i++}, $${i++}, NOW())`\n );\n args.push(\n params.agentId,\n hit.id,\n hit.path,\n params.query,\n qhash,\n hit.score,\n bucket\n );\n }\n const sql = `\n INSERT INTO ${this.table}\n (agent_id, memory_id, memory_path, query, query_hash, score, day_bucket, recorded_at)\n VALUES ${values.join(', ')}\n ON CONFLICT (agent_id, memory_id, query_hash, day_bucket) DO UPDATE\n SET score = GREATEST(${this.table}.score, EXCLUDED.score),\n recorded_at = NOW()\n `;\n await this.pool.query(sql, args);\n }\n}\n\n/** No-op tracker — used when recall tracking is disabled or the backend isn't pgvector. */\nexport class NullRecallTracker implements RecallTracker {\n async record(): Promise<void> {}\n async migrate(): Promise<void> {}\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recallTracking.mjs","sources":["../../../src/memory/recallTracking.ts"],"sourcesContent":["/**\n * Recall tracking — Phase 2.\n *\n * Lightweight adaptation of upstream\n * `extensions/memory-core/src/short-term-promotion.ts::recordShortTermRecalls`.\n * Upstream stores recalls in a JSON file under `memory/.dreams/`; we store\n * them in a Postgres table `agent_memory_recalls`. Schema captures what the\n * future Phase 3 dreaming/promotion algorithm will need:\n * - which memory row was surfaced (`memory_id`)\n * - the query that surfaced it (raw + SHA-256 hash for dedupe)\n * - hybrid score at the time of recall\n * - the day bucket (for per-day dedupe / frequency counting)\n * - the recorded timestamp\n *\n * Best-effort: failures never block memory_search. The caller fires\n * {@link RecallTracker.record} without awaiting the result and ignores errors.\n */\nimport { createHash } from 'crypto';\nimport type { Pool } from 'pg';\n\nexport interface RecallTracker {\n /** Record that the given memory ids were surfaced to the model for a query. */\n record(params: RecallRecordParams): Promise<void>;\n /** Backend-specific schema migration. Idempotent. */\n migrate(): Promise<void>;\n}\n\nexport interface RecallRecordParams {\n agentId: string;\n query: string;\n hits: Array<{ id: string; path: string; score: number }>;\n nowMs?: number;\n}\n\nexport const RECALL_TABLE = 'agent_memory_recalls';\n\nfunction hashQuery(query: string): string {\n return createHash('sha256')\n .update(query.trim().toLowerCase())\n .digest('hex')\n .slice(0, 32);\n}\n\nfunction dayBucket(nowMs: number): string {\n const d = new Date(nowMs);\n const y = d.getUTCFullYear();\n const m = String(d.getUTCMonth() + 1).padStart(2, '0');\n const day = String(d.getUTCDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\nexport class PgvectorRecallTracker implements RecallTracker {\n constructor(\n private readonly pool: Pool,\n private readonly table: string = RECALL_TABLE\n ) {}\n\n async migrate(): Promise<void> {\n // [recall-tracking] debug: create table + indexes if missing\n await this.pool.query(`\n CREATE TABLE IF NOT EXISTS ${this.table} (\n id BIGSERIAL PRIMARY KEY,\n agent_id TEXT NOT NULL,\n memory_id TEXT NOT NULL,\n memory_path TEXT NOT NULL,\n query TEXT NOT NULL,\n query_hash TEXT NOT NULL,\n score DOUBLE PRECISION NOT NULL,\n day_bucket TEXT NOT NULL,\n recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n )\n `);\n await this.pool.query(\n `CREATE INDEX IF NOT EXISTS ${this.table}_agent_day_idx ON ${this.table} (agent_id, day_bucket)`\n );\n await this.pool.query(\n `CREATE INDEX IF NOT EXISTS ${this.table}_memory_idx ON ${this.table} (agent_id, memory_id)`\n );\n await this.pool.query(\n `CREATE UNIQUE INDEX IF NOT EXISTS ${this.table}_dedupe_idx\n ON ${this.table} (agent_id, memory_id, query_hash, day_bucket)`\n );\n }\n\n async record(params: RecallRecordParams): Promise<void> {\n if (!params.agentId || !params.query.trim() || params.hits.length === 0)\n return;\n const nowMs = params.nowMs ?? Date.now();\n const qhash = hashQuery(params.query);\n const bucket = dayBucket(nowMs);\n\n // [recall-tracking] debug: upsert one row per (agent, memory, query, day)\n // Upstream dedupes per-day per-query so repeated searches don't inflate counts.\n const values: string[] = [];\n const args: unknown[] = [];\n let i = 1;\n for (const hit of params.hits) {\n values.push(\n `($${i++}, $${i++}, $${i++}, $${i++}, $${i++}, $${i++}, $${i++}, NOW())`\n );\n args.push(\n params.agentId,\n hit.id,\n hit.path,\n params.query,\n qhash,\n hit.score,\n bucket\n );\n }\n const sql = `\n INSERT INTO ${this.table}\n (agent_id, memory_id, memory_path, query, query_hash, score, day_bucket, recorded_at)\n VALUES ${values.join(', ')}\n ON CONFLICT (agent_id, memory_id, query_hash, day_bucket) DO UPDATE\n SET score = GREATEST(${this.table}.score, EXCLUDED.score),\n recorded_at = NOW()\n `;\n await this.pool.query(sql, args);\n }\n}\n\n/** No-op tracker — used when recall tracking is disabled or the backend isn't pgvector. */\nexport class NullRecallTracker implements RecallTracker {\n async record(): Promise<void> {}\n async migrate(): Promise<void> {}\n}\n"],"names":[],"mappings":"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"}
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * Temporal decay — Phase 2.
3
3
  *
4
- * Ported from a reference implementation `extensions/memory-core/src/memory/temporal-decay.ts`.
4
+ * Ported from upstream `extensions/memory-core/src/memory/temporal-decay.ts`.
5
5
  * Ages dated memory files (`memory/YYYY-MM-DD.md`) using exponential decay
6
6
  * `multiplier = exp(-ln(2) / halfLifeDays * ageInDays)`. At half-life, the
7
7
  * score is exactly halved.
8
8
  *
9
9
  * Evergreen files (MEMORY.md, memory/topics.md, any non-dated file inside
10
10
  * memory/) do NOT decay — they represent durable knowledge and should stay
11
- * hot regardless of age. This mirrors the standard `isEvergreenMemoryPath`.
11
+ * hot regardless of age. This mirrors upstream's `isEvergreenMemoryPath`.
12
12
  *
13
13
  * Since our pgvector rows carry `createdAt`, we don't need filesystem stat
14
14
  * fallback — the row timestamp is authoritative for any file without a
@@ -1 +1 @@
1
- {"version":3,"file":"temporalDecay.mjs","sources":["../../../src/memory/temporalDecay.ts"],"sourcesContent":["/**\n * Temporal decay — Phase 2.\n *\n * Ported from a reference implementation `extensions/memory-core/src/memory/temporal-decay.ts`.\n * Ages dated memory files (`memory/YYYY-MM-DD.md`) using exponential decay\n * `multiplier = exp(-ln(2) / halfLifeDays * ageInDays)`. At half-life, the\n * score is exactly halved.\n *\n * Evergreen files (MEMORY.md, memory/topics.md, any non-dated file inside\n * memory/) do NOT decay — they represent durable knowledge and should stay\n * hot regardless of age. This mirrors the standard `isEvergreenMemoryPath`.\n *\n * Since our pgvector rows carry `createdAt`, we don't need filesystem stat\n * fallback — the row timestamp is authoritative for any file without a\n * date in the path.\n */\n\nexport interface TemporalDecayConfig {\n enabled: boolean;\n halfLifeDays: number;\n}\n\nexport const DEFAULT_TEMPORAL_DECAY_CONFIG: TemporalDecayConfig = {\n enabled: false,\n halfLifeDays: 30,\n};\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst DATED_MEMORY_PATH_RE = /(?:^|\\/)memory\\/(\\d{4})-(\\d{2})-(\\d{2})\\.md$/;\n\nexport function toDecayLambda(halfLifeDays: number): number {\n if (!Number.isFinite(halfLifeDays) || halfLifeDays <= 0) return 0;\n return Math.LN2 / halfLifeDays;\n}\n\nexport function calculateTemporalDecayMultiplier(params: {\n ageInDays: number;\n halfLifeDays: number;\n}): number {\n const lambda = toDecayLambda(params.halfLifeDays);\n const age = Math.max(0, params.ageInDays);\n if (lambda <= 0 || !Number.isFinite(age)) return 1;\n return Math.exp(-lambda * age);\n}\n\nexport function applyTemporalDecayToScore(params: {\n score: number;\n ageInDays: number;\n halfLifeDays: number;\n}): number {\n return params.score * calculateTemporalDecayMultiplier(params);\n}\n\nfunction normalizePath(p: string): string {\n return (p ?? '').replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\n/** Parse a date out of `memory/YYYY-MM-DD.md` — returns null on non-match or invalid date. */\nexport function parseMemoryDateFromPath(filePath: string): Date | null {\n const m = DATED_MEMORY_PATH_RE.exec(normalizePath(filePath));\n if (!m) return null;\n const y = Number(m[1]);\n const mo = Number(m[2]);\n const d = Number(m[3]);\n if (!Number.isInteger(y) || !Number.isInteger(mo) || !Number.isInteger(d))\n return null;\n const ts = Date.UTC(y, mo - 1, d);\n const parsed = new Date(ts);\n if (\n parsed.getUTCFullYear() !== y ||\n parsed.getUTCMonth() !== mo - 1 ||\n parsed.getUTCDate() !== d\n ) {\n return null;\n }\n return parsed;\n}\n\n/**\n * Evergreen = durable knowledge file that should not decay.\n * - `MEMORY.md` / `memory.md` at root\n * - anything inside `memory/` that is NOT a dated `YYYY-MM-DD.md` file\n */\nexport function isEvergreenMemoryPath(filePath: string): boolean {\n const n = normalizePath(filePath);\n if (n === 'MEMORY.md' || n === 'memory.md') return true;\n if (!n.startsWith('memory/')) return false;\n return !DATED_MEMORY_PATH_RE.test(n);\n}\n\nfunction ageInDays(timestamp: Date, nowMs: number): number {\n return Math.max(0, nowMs - timestamp.getTime()) / DAY_MS;\n}\n\nexport interface DecayCandidate {\n path: string;\n score: number;\n createdAt?: Date;\n}\n\n/**\n * Apply temporal decay to a list of memory hits.\n *\n * Priority for the effective timestamp:\n * 1. Dated path (`memory/YYYY-MM-DD.md`) — use the date in the path\n * 2. Otherwise, if the path is evergreen — NO decay\n * 3. Otherwise, use the row's `createdAt`\n */\nexport function applyTemporalDecayToHits<T extends DecayCandidate>(\n hits: T[],\n config: Partial<TemporalDecayConfig> = {},\n nowMs: number = Date.now()\n): T[] {\n const merged = { ...DEFAULT_TEMPORAL_DECAY_CONFIG, ...config };\n if (!merged.enabled) return [...hits];\n\n return hits.map((h) => {\n const datedTs = parseMemoryDateFromPath(h.path);\n let ts: Date | null = datedTs;\n if (!ts) {\n if (isEvergreenMemoryPath(h.path)) return h;\n ts = h.createdAt ?? null;\n }\n if (!ts) return h;\n return {\n ...h,\n score: applyTemporalDecayToScore({\n score: h.score,\n ageInDays: ageInDays(ts, nowMs),\n halfLifeDays: merged.halfLifeDays,\n }),\n };\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"temporalDecay.mjs","sources":["../../../src/memory/temporalDecay.ts"],"sourcesContent":["/**\n * Temporal decay — Phase 2.\n *\n * Ported from upstream `extensions/memory-core/src/memory/temporal-decay.ts`.\n * Ages dated memory files (`memory/YYYY-MM-DD.md`) using exponential decay\n * `multiplier = exp(-ln(2) / halfLifeDays * ageInDays)`. At half-life, the\n * score is exactly halved.\n *\n * Evergreen files (MEMORY.md, memory/topics.md, any non-dated file inside\n * memory/) do NOT decay — they represent durable knowledge and should stay\n * hot regardless of age. This mirrors upstream's `isEvergreenMemoryPath`.\n *\n * Since our pgvector rows carry `createdAt`, we don't need filesystem stat\n * fallback — the row timestamp is authoritative for any file without a\n * date in the path.\n */\n\nexport interface TemporalDecayConfig {\n enabled: boolean;\n halfLifeDays: number;\n}\n\nexport const DEFAULT_TEMPORAL_DECAY_CONFIG: TemporalDecayConfig = {\n enabled: false,\n halfLifeDays: 30,\n};\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst DATED_MEMORY_PATH_RE = /(?:^|\\/)memory\\/(\\d{4})-(\\d{2})-(\\d{2})\\.md$/;\n\nexport function toDecayLambda(halfLifeDays: number): number {\n if (!Number.isFinite(halfLifeDays) || halfLifeDays <= 0) return 0;\n return Math.LN2 / halfLifeDays;\n}\n\nexport function calculateTemporalDecayMultiplier(params: {\n ageInDays: number;\n halfLifeDays: number;\n}): number {\n const lambda = toDecayLambda(params.halfLifeDays);\n const age = Math.max(0, params.ageInDays);\n if (lambda <= 0 || !Number.isFinite(age)) return 1;\n return Math.exp(-lambda * age);\n}\n\nexport function applyTemporalDecayToScore(params: {\n score: number;\n ageInDays: number;\n halfLifeDays: number;\n}): number {\n return params.score * calculateTemporalDecayMultiplier(params);\n}\n\nfunction normalizePath(p: string): string {\n return (p ?? '').replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\n/** Parse a date out of `memory/YYYY-MM-DD.md` — returns null on non-match or invalid date. */\nexport function parseMemoryDateFromPath(filePath: string): Date | null {\n const m = DATED_MEMORY_PATH_RE.exec(normalizePath(filePath));\n if (!m) return null;\n const y = Number(m[1]);\n const mo = Number(m[2]);\n const d = Number(m[3]);\n if (!Number.isInteger(y) || !Number.isInteger(mo) || !Number.isInteger(d))\n return null;\n const ts = Date.UTC(y, mo - 1, d);\n const parsed = new Date(ts);\n if (\n parsed.getUTCFullYear() !== y ||\n parsed.getUTCMonth() !== mo - 1 ||\n parsed.getUTCDate() !== d\n ) {\n return null;\n }\n return parsed;\n}\n\n/**\n * Evergreen = durable knowledge file that should not decay.\n * - `MEMORY.md` / `memory.md` at root\n * - anything inside `memory/` that is NOT a dated `YYYY-MM-DD.md` file\n */\nexport function isEvergreenMemoryPath(filePath: string): boolean {\n const n = normalizePath(filePath);\n if (n === 'MEMORY.md' || n === 'memory.md') return true;\n if (!n.startsWith('memory/')) return false;\n return !DATED_MEMORY_PATH_RE.test(n);\n}\n\nfunction ageInDays(timestamp: Date, nowMs: number): number {\n return Math.max(0, nowMs - timestamp.getTime()) / DAY_MS;\n}\n\nexport interface DecayCandidate {\n path: string;\n score: number;\n createdAt?: Date;\n}\n\n/**\n * Apply temporal decay to a list of memory hits.\n *\n * Priority for the effective timestamp:\n * 1. Dated path (`memory/YYYY-MM-DD.md`) — use the date in the path\n * 2. Otherwise, if the path is evergreen — NO decay\n * 3. Otherwise, use the row's `createdAt`\n */\nexport function applyTemporalDecayToHits<T extends DecayCandidate>(\n hits: T[],\n config: Partial<TemporalDecayConfig> = {},\n nowMs: number = Date.now()\n): T[] {\n const merged = { ...DEFAULT_TEMPORAL_DECAY_CONFIG, ...config };\n if (!merged.enabled) return [...hits];\n\n return hits.map((h) => {\n const datedTs = parseMemoryDateFromPath(h.path);\n let ts: Date | null = datedTs;\n if (!ts) {\n if (isEvergreenMemoryPath(h.path)) return h;\n ts = h.createdAt ?? null;\n }\n if (!ts) return h;\n return {\n ...h,\n score: applyTemporalDecayToScore({\n score: h.score,\n ageInDays: ageInDays(ts, nowMs),\n halfLifeDays: merged.halfLifeDays,\n }),\n };\n });\n}\n"],"names":[],"mappings":"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"}
@@ -1,47 +1,6 @@
1
1
  import { BaseMessage, ToolMessage, SystemMessage, HumanMessage, AIMessage } from '@langchain/core/messages';
2
2
  import { ContentTypes } from '../common/enum.mjs';
3
3
 
4
- /**
5
- * Strips Anthropic-style `cache_control` markers from every block in the
6
- * given content array. Returns the (possibly cloned) content and a flag
7
- * indicating whether anything was actually modified — so callers can avoid
8
- * unnecessary message clones.
9
- *
10
- * Used to sanitize a system message's content array before adding Bedrock
11
- * cache points, so stray Anthropic markers (e.g. left over from a previous
12
- * Anthropic-provider turn) don't prevent the Bedrock provider from caching
13
- * the system prefix.
14
- */
15
- function stripAnthropicCacheControlFromBlocks(content) {
16
- let modified = false;
17
- const strippedContent = content.map((block) => {
18
- if (!('cache_control' in block)) {
19
- return block;
20
- }
21
- const cloned = { ...block };
22
- delete cloned.cache_control;
23
- modified = true;
24
- return cloned;
25
- });
26
- return { content: strippedContent, modified };
27
- }
28
- /**
29
- * For the Bedrock cache pass, system messages must NOT carry Anthropic
30
- * cache_control markers (those are an Anthropic-specific encoding and
31
- * Bedrock uses cachePoint blocks instead). Strip them before the cache
32
- * point insertion logic walks the message.
33
- */
34
- function sanitizeBedrockSystemMessage(message) {
35
- const content = message.content;
36
- if (!Array.isArray(content)) {
37
- return message;
38
- }
39
- const stripped = stripAnthropicCacheControlFromBlocks(content);
40
- if (!stripped.modified) {
41
- return message;
42
- }
43
- return cloneMessage(message, stripped.content);
44
- }
45
4
  /** Debug logger for cache operations - set ILLUMA_DEBUG_CACHE=true to enable */
46
5
  const debugCache = (message, data) => {
47
6
  if (process.env.ILLUMA_DEBUG_CACHE === 'true') {
@@ -309,33 +268,8 @@ function addBedrockCacheControl(messages) {
309
268
  });
310
269
  // Clone messages to avoid mutating originals
311
270
  const updatedMessages = messages.map((msg) => {
312
- /* PRESERVE cachePoint on system messages — they're added inline by
313
- * AgentContext.buildSystemRunnable to mark the cacheable system prefix
314
- * for account-level caching. Only strip stray Anthropic cache_control
315
- * (which Bedrock ignores). For non-system messages keep the existing
316
- * behaviour: full strip + repopulate based on last-2 strategy. */
317
- const msgType = 'getType' in msg && typeof msg.getType === 'function'
318
- ? msg.getType()
319
- : undefined;
320
- const msgRole = msg.role;
321
- const isSystem = msgType === 'system' || msgRole === 'system';
322
271
  const content = msg.content;
323
272
  if (Array.isArray(content)) {
324
- if (isSystem) {
325
- /* System: keep cachePoint, only strip Anthropic cache_control. */
326
- if (hasAnthropicCacheControl(content)) {
327
- const stripped = content.map((block) => {
328
- const rec = block;
329
- if ('cache_control' in rec) {
330
- const { cache_control: _, ...rest } = rec;
331
- return rest;
332
- }
333
- return block;
334
- });
335
- return cloneMessage(msg, stripped);
336
- }
337
- return cloneMessage(msg, content);
338
- }
339
273
  // Strip existing cachePoint blocks and Anthropic-style cache_control
340
274
  const stripped = content
341
275
  .filter((block) => !isCachePoint(block))
@@ -415,29 +349,6 @@ function addBedrockCacheControl(messages) {
415
349
  for (let i = updatedMessages.length - 1; i >= 0 && applied < MAX_CACHE_POINTS; i--) {
416
350
  const message = updatedMessages[i];
417
351
  const msgContent = message.content;
418
- /* System messages are handled by AgentContext.buildSystemRunnable —
419
- * the cachePoint is emitted INLINE in the system message content array
420
- * so the cacheable system prefix gets a marker every turn (not just
421
- * when the system message happens to be in the last 2 eligible). The
422
- * Bedrock converter (src/llm/bedrock/utils/message_inputs.ts:261-294)
423
- * passes through cachePoint blocks in system content arrays.
424
- *
425
- * If we ALSO marked system here, we'd: (a) waste one of the two
426
- * cachePoint budget slots that Bedrock honours per-request, starving
427
- * the conversation tail, and (b) potentially double-mark a stable
428
- * prefix that already has its own cachePoint.
429
- *
430
- * As a defensive cleanup, strip stray Anthropic-style cache_control
431
- * markers from the system message — Bedrock ignores them and they
432
- * can confuse provider validation. */
433
- const _msgType = 'getType' in message && typeof message.getType === 'function'
434
- ? message.getType()
435
- : undefined;
436
- const _msgRole = message.role;
437
- if (_msgType === 'system' || _msgRole === 'system') {
438
- updatedMessages[i] = sanitizeBedrockSystemMessage(message);
439
- continue;
440
- }
441
352
  // Skip empty/whitespace-only content
442
353
  if (msgContent == null)
443
354
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"cache.mjs","sources":["../../../src/messages/cache.ts"],"sourcesContent":["import {\n AIMessage,\n BaseMessage,\n ToolMessage,\n HumanMessage,\n SystemMessage,\n MessageContentComplex,\n} from '@langchain/core/messages';\nimport type { AnthropicMessage } from '@/types/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport { ContentTypes } from '@/common/enum';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\ntype MessageContentWithCacheControl = MessageContentComplex & {\n cache_control?: unknown;\n};\n\n/**\n * Strips Anthropic-style `cache_control` markers from every block in the\n * given content array. Returns the (possibly cloned) content and a flag\n * indicating whether anything was actually modified — so callers can avoid\n * unnecessary message clones.\n *\n * Used to sanitize a system message's content array before adding Bedrock\n * cache points, so stray Anthropic markers (e.g. left over from a previous\n * Anthropic-provider turn) don't prevent the Bedrock provider from caching\n * the system prefix.\n */\nfunction stripAnthropicCacheControlFromBlocks(\n content: MessageContentComplex[]\n): { content: MessageContentComplex[]; modified: boolean } {\n let modified = false;\n const strippedContent = content.map((block) => {\n if (!('cache_control' in block)) {\n return block;\n }\n\n const cloned: MessageContentWithCacheControl = { ...block };\n delete cloned.cache_control;\n modified = true;\n return cloned;\n });\n\n return { content: strippedContent, modified };\n}\n\n/**\n * For the Bedrock cache pass, system messages must NOT carry Anthropic\n * cache_control markers (those are an Anthropic-specific encoding and\n * Bedrock uses cachePoint blocks instead). Strip them before the cache\n * point insertion logic walks the message.\n */\nfunction sanitizeBedrockSystemMessage<T extends MessageWithContent>(\n message: T\n): T {\n const content = message.content;\n if (!Array.isArray(content)) {\n return message;\n }\n\n const stripped = stripAnthropicCacheControlFromBlocks(content);\n if (!stripped.modified) {\n return message;\n }\n\n return cloneMessage(message, stripped.content);\n}\n\n/** Debug logger for cache operations - set ILLUMA_DEBUG_CACHE=true to enable */\nconst debugCache = (message: string, data?: unknown): void => {\n if (process.env.ILLUMA_DEBUG_CACHE === 'true') {\n // eslint-disable-next-line no-console\n console.log(\n `[Cache] ${message}`,\n data !== undefined ? JSON.stringify(data, null, 2) : ''\n );\n }\n};\n\n/**\n * Deep clones a message's content to prevent mutation of the original.\n */\nfunction deepCloneContent<T extends string | MessageContentComplex[]>(\n content: T\n): T {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content.map((block) => ({ ...block })) as T;\n }\n return content;\n}\n\n/**\n * Clones a message with new content. For LangChain BaseMessage instances,\n * constructs a proper class instance so that `instanceof` checks are preserved\n * in downstream code (e.g., ensureThinkingBlockInMessages).\n * For plain objects (AnthropicMessage), uses object spread.\n */\nfunction cloneMessage<T extends MessageWithContent>(\n message: T,\n content: string | MessageContentComplex[]\n): T {\n if (message instanceof BaseMessage) {\n const baseParams = {\n content,\n additional_kwargs: { ...message.additional_kwargs },\n response_metadata: { ...message.response_metadata },\n id: message.id,\n name: message.name,\n };\n\n const msgType = message.getType();\n switch (msgType) {\n case 'ai':\n return new AIMessage({\n ...baseParams,\n tool_calls: (message as unknown as AIMessage).tool_calls,\n }) as unknown as T;\n case 'human':\n return new HumanMessage(baseParams) as unknown as T;\n case 'system':\n return new SystemMessage(baseParams) as unknown as T;\n case 'tool':\n return new ToolMessage({\n ...baseParams,\n tool_call_id: (message as unknown as ToolMessage).tool_call_id,\n }) as unknown as T;\n default:\n break;\n }\n }\n\n const {\n lc_kwargs: _lc_kwargs,\n lc_serializable: _lc_serializable,\n lc_namespace: _lc_namespace,\n ...rest\n } = message as T & {\n lc_kwargs?: unknown;\n lc_serializable?: unknown;\n lc_namespace?: unknown;\n };\n\n const cloned = { ...rest, content } as T;\n\n // Sync lc_kwargs.content with the new content to prevent LangChain coercion issues\n const lcKwargs = (message as Record<string, unknown>).lc_kwargs as\n | Record<string, unknown>\n | undefined;\n if (lcKwargs != null) {\n (cloned as Record<string, unknown>).lc_kwargs = {\n ...lcKwargs,\n content: content,\n };\n }\n\n // LangChain messages don't have a direct 'role' property - derive it from getType()\n if (\n 'getType' in message &&\n typeof message.getType === 'function' &&\n !('role' in cloned)\n ) {\n const msgType = (message as unknown as BaseMessage).getType();\n const roleMap: Record<string, string> = {\n human: 'user',\n ai: 'assistant',\n system: 'system',\n tool: 'tool',\n };\n (cloned as Record<string, unknown>).role = roleMap[msgType] || msgType;\n }\n\n return cloned;\n}\n\n/**\n * Checks if a content block is a cache point\n */\nfunction isCachePoint(block: MessageContentComplex): boolean {\n return 'cachePoint' in block && !('type' in block);\n}\n\n/**\n * Checks if a message's content needs cache control stripping.\n * Returns true if content has cachePoint blocks or cache_control fields.\n */\nfunction needsCacheStripping(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n const block = content[i];\n if (isCachePoint(block)) return true;\n if ('cache_control' in block) return true;\n }\n return false;\n}\n\n/**\n * Checks if a message's content has Anthropic cache_control fields.\n */\nfunction hasAnthropicCacheControl(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if ('cache_control' in content[i]) return true;\n }\n return false;\n}\n\n/**\n * Checks if a message's content has Bedrock cachePoint blocks.\n */\nfunction hasBedrockCachePoint(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if (isCachePoint(content[i])) return true;\n }\n return false;\n}\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,\n * then adds fresh cache control to the last 2 user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let userMessagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const isUserMessage =\n ('getType' in originalMessage && originalMessage.getType() === 'human') ||\n ('role' in originalMessage && originalMessage.role === 'user');\n\n const hasArrayContent = Array.isArray(content);\n const needsStripping =\n hasArrayContent &&\n needsCacheStripping(content as MessageContentComplex[]);\n const needsCacheAdd =\n userMessagesModified < 2 &&\n isUserMessage &&\n (typeof content === 'string' || hasArrayContent);\n\n if (!needsStripping && !needsCacheAdd) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n\n if (hasArrayContent) {\n workingContent = deepCloneContent(\n content as MessageContentComplex[]\n ).filter((block) => !isCachePoint(block as MessageContentComplex));\n\n for (let j = 0; j < workingContent.length; j++) {\n const block = workingContent[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n } else if (typeof content === 'string') {\n workingContent = [\n { type: 'text', text: content },\n ] as MessageContentComplex[];\n } else {\n workingContent = [];\n }\n\n if (userMessagesModified >= 2 || !isUserMessage) {\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n continue;\n }\n\n for (let j = workingContent.length - 1; j >= 0; j--) {\n const contentPart = workingContent[j];\n if ('type' in contentPart && contentPart.type === 'text') {\n (contentPart as Anthropic.TextBlockParam).cache_control = {\n type: 'ephemeral',\n };\n userMessagesModified++;\n break;\n }\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Removes all Anthropic cache_control fields from messages\n * Used when switching from Anthropic to Bedrock provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripAnthropicCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasAnthropicCacheControl(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content);\n for (let j = 0; j < clonedContent.length; j++) {\n const block = clonedContent[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Removes all Bedrock cachePoint blocks from messages\n * Used when switching from Bedrock to Anthropic provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripBedrockCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasBedrockCachePoint(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content).filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n );\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points using \"Stable Prefix Caching\" strategy.\n *\n * STRATEGY: Place cache point after the LAST ASSISTANT message only.\n * This ensures the prefix (everything before the cache point) remains STABLE\n * as the conversation grows, maximizing cache hits.\n *\n * Why this works:\n * - System message has its own cachePoint (added in AgentContext)\n * - Tools have their own cachePoint (added in IllumaBedrockConverse)\n * - Conversation history grows, but the PREFIX stays the same\n * - Only the NEW user message is uncached (it's always different)\n *\n * Example conversation flow:\n * Request 1: [System+cachePoint][Tools+cachePoint][User1] → No conversation cache yet\n * Request 2: [System][Tools][User1][Assistant1+cachePoint][User2] → Cache User1+Assistant1\n * Request 3: [System][Tools][User1][Assistant1][User2][Assistant2+cachePoint][User3]\n * → Cache reads User1+A1+User2+A2, cache writes new portion\n *\n * Claude's \"Simplified Cache Management\" automatically looks back up to 20 content\n * blocks from the cache checkpoint to find the longest matching prefix.\n *\n * @param messages - The array of message objects (excluding system message).\n * @returns - The updated array with a single cache point after the last assistant message.\n */\nexport function addBedrockCacheControl<\n T extends Partial<BaseMessage> & MessageWithContent,\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length < 1) {\n debugCache('addBedrockCacheControl: Skipping - no messages', {\n count: messages.length,\n });\n return messages;\n }\n\n debugCache(\n 'addBedrockCacheControl: Processing messages with stable prefix strategy',\n {\n count: messages.length,\n }\n );\n\n // Clone messages to avoid mutating originals\n const updatedMessages: T[] = messages.map((msg) => {\n /* PRESERVE cachePoint on system messages — they're added inline by\n * AgentContext.buildSystemRunnable to mark the cacheable system prefix\n * for account-level caching. Only strip stray Anthropic cache_control\n * (which Bedrock ignores). For non-system messages keep the existing\n * behaviour: full strip + repopulate based on last-2 strategy. */\n const msgType =\n 'getType' in msg && typeof msg.getType === 'function'\n ? msg.getType()\n : undefined;\n const msgRole = (msg as Record<string, unknown>).role as string | undefined;\n const isSystem = msgType === 'system' || msgRole === 'system';\n\n const content = msg.content;\n if (Array.isArray(content)) {\n if (isSystem) {\n /* System: keep cachePoint, only strip Anthropic cache_control. */\n if (hasAnthropicCacheControl(content)) {\n const stripped = content.map((block) => {\n const rec = block as Record<string, unknown>;\n if ('cache_control' in rec) {\n const { cache_control: _, ...rest } = rec;\n return rest as MessageContentComplex;\n }\n return block;\n });\n return cloneMessage(msg, stripped) as T;\n }\n return cloneMessage(msg, content) as T;\n }\n\n // Strip existing cachePoint blocks and Anthropic-style cache_control\n const stripped = content\n .filter((block) => !isCachePoint(block))\n .map((block) => {\n const rec = block as Record<string, unknown>;\n if ('cache_control' in rec) {\n const { cache_control: _, ...rest } = rec;\n return rest as MessageContentComplex;\n }\n return block;\n });\n if (needsCacheStripping(content) || hasAnthropicCacheControl(content)) {\n return cloneMessage(msg, stripped) as T;\n }\n }\n return cloneMessage(msg, content as string | MessageContentComplex[]) as T;\n });\n\n // Helper function to check if a message contains reasoning/thinking blocks\n const hasReasoningBlock = (message: T): boolean => {\n const content = message.content;\n if (!Array.isArray(content)) {\n return false;\n }\n for (const block of content) {\n const type = (block as { type?: string }).type;\n if (\n type === 'reasoning_content' ||\n type === 'reasoning' ||\n type === 'thinking' ||\n type === 'redacted_thinking'\n ) {\n return true;\n }\n }\n return false;\n };\n\n /**\n * Helper to add a cachePoint to a message's content.\n * For strings: wraps into [{type: 'text', text}, {cachePoint}]\n * For arrays: inserts cachePoint after the last non-whitespace text block\n * Returns the new message or null if no suitable insertion point.\n */\n const addCachePointToMessage = (message: T): T | null => {\n const msgContent = message.content;\n\n if (typeof msgContent === 'string' && msgContent !== '') {\n const newContent = [\n { type: ContentTypes.TEXT, text: msgContent },\n { cachePoint: { type: 'default' } },\n ] as MessageContentComplex[];\n return cloneMessage(message, newContent) as T;\n }\n\n if (Array.isArray(msgContent) && msgContent.length > 0) {\n if (hasReasoningBlock(message)) {\n return null;\n }\n // Find the last text block and insert cache point after it\n for (let j = msgContent.length - 1; j >= 0; j--) {\n const type = (msgContent[j] as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (msgContent[j] as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n const newContent = [\n ...msgContent.slice(0, j + 1),\n { cachePoint: { type: 'default' } } as MessageContentComplex,\n ...msgContent.slice(j + 1),\n ];\n return cloneMessage(message, newContent) as T;\n }\n }\n }\n }\n\n return null;\n };\n\n // Add cache points to the last 2 messages (from the end) that have eligible content.\n // This mirrors Anthropic's two-breakpoint strategy for Bedrock's cache API.\n // Skip messages with only whitespace, empty content, or reasoning blocks.\n const MAX_CACHE_POINTS = 2;\n let applied = 0;\n\n for (\n let i = updatedMessages.length - 1;\n i >= 0 && applied < MAX_CACHE_POINTS;\n i--\n ) {\n const message = updatedMessages[i];\n const msgContent = message.content;\n\n /* System messages are handled by AgentContext.buildSystemRunnable —\n * the cachePoint is emitted INLINE in the system message content array\n * so the cacheable system prefix gets a marker every turn (not just\n * when the system message happens to be in the last 2 eligible). The\n * Bedrock converter (src/llm/bedrock/utils/message_inputs.ts:261-294)\n * passes through cachePoint blocks in system content arrays.\n *\n * If we ALSO marked system here, we'd: (a) waste one of the two\n * cachePoint budget slots that Bedrock honours per-request, starving\n * the conversation tail, and (b) potentially double-mark a stable\n * prefix that already has its own cachePoint.\n *\n * As a defensive cleanup, strip stray Anthropic-style cache_control\n * markers from the system message — Bedrock ignores them and they\n * can confuse provider validation. */\n const _msgType =\n 'getType' in message && typeof message.getType === 'function'\n ? message.getType()\n : undefined;\n const _msgRole = (message as Record<string, unknown>).role as\n | string\n | undefined;\n if (_msgType === 'system' || _msgRole === 'system') {\n updatedMessages[i] = sanitizeBedrockSystemMessage(\n message as MessageWithContent\n ) as T;\n continue;\n }\n\n // Skip empty/whitespace-only content\n if (msgContent == null) continue;\n if (typeof msgContent === 'string' && msgContent.trim() === '') continue;\n if (Array.isArray(msgContent) && msgContent.length === 0) continue;\n\n // Skip non-string, non-array content (e.g., number, object without type)\n if (typeof msgContent !== 'string' && !Array.isArray(msgContent)) continue;\n\n // Skip AI messages with only whitespace text and reasoning blocks (tool-call scenario)\n const messageType =\n 'getType' in message && typeof message.getType === 'function'\n ? message.getType()\n : 'unknown';\n const role = (message as Record<string, unknown>).role as\n | string\n | undefined;\n const isAi = messageType === 'ai' || role === 'assistant';\n if (isAi && hasReasoningBlock(message)) {\n // Check if all text blocks are whitespace-only\n if (Array.isArray(msgContent)) {\n const hasNonWhitespaceText = msgContent.some((block) => {\n const type = (block as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (block as { text?: string }).text;\n return text != null && text.trim() !== '';\n }\n return false;\n });\n if (!hasNonWhitespaceText) {\n debugCache(\n `⚠️ Message cachePoint SKIPPED at index ${i} (AI with whitespace-only text + reasoning)`\n );\n continue;\n }\n }\n }\n\n // Skip messages that already have cache points (multi-agent scenarios)\n if (\n Array.isArray(msgContent) &&\n msgContent.some((b) => isCachePoint(b as MessageContentComplex))\n ) {\n continue;\n }\n\n const updated = addCachePointToMessage(message);\n if (updated != null) {\n updatedMessages[i] = updated;\n applied++;\n debugCache(\n `📍 Message cachePoint at index ${i} (${typeof message.content === 'string' ? 'string' : 'array'})`\n );\n }\n }\n\n debugCache(\n 'addBedrockCacheControl: Complete - stable prefix caching applied',\n {\n appliedCachePoints: applied,\n totalMessages: updatedMessages.length,\n }\n );\n\n return updatedMessages;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.mjs","sources":["../../../src/messages/cache.ts"],"sourcesContent":["import {\n AIMessage,\n BaseMessage,\n ToolMessage,\n HumanMessage,\n SystemMessage,\n MessageContentComplex,\n} from '@langchain/core/messages';\nimport type { AnthropicMessage } from '@/types/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport { ContentTypes } from '@/common/enum';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\n/** Debug logger for cache operations - set ILLUMA_DEBUG_CACHE=true to enable */\nconst debugCache = (message: string, data?: unknown): void => {\n if (process.env.ILLUMA_DEBUG_CACHE === 'true') {\n // eslint-disable-next-line no-console\n console.log(\n `[Cache] ${message}`,\n data !== undefined ? JSON.stringify(data, null, 2) : ''\n );\n }\n};\n\n/**\n * Deep clones a message's content to prevent mutation of the original.\n */\nfunction deepCloneContent<T extends string | MessageContentComplex[]>(\n content: T\n): T {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content.map((block) => ({ ...block })) as T;\n }\n return content;\n}\n\n/**\n * Clones a message with new content. For LangChain BaseMessage instances,\n * constructs a proper class instance so that `instanceof` checks are preserved\n * in downstream code (e.g., ensureThinkingBlockInMessages).\n * For plain objects (AnthropicMessage), uses object spread.\n */\nfunction cloneMessage<T extends MessageWithContent>(\n message: T,\n content: string | MessageContentComplex[]\n): T {\n if (message instanceof BaseMessage) {\n const baseParams = {\n content,\n additional_kwargs: { ...message.additional_kwargs },\n response_metadata: { ...message.response_metadata },\n id: message.id,\n name: message.name,\n };\n\n const msgType = message.getType();\n switch (msgType) {\n case 'ai':\n return new AIMessage({\n ...baseParams,\n tool_calls: (message as unknown as AIMessage).tool_calls,\n }) as unknown as T;\n case 'human':\n return new HumanMessage(baseParams) as unknown as T;\n case 'system':\n return new SystemMessage(baseParams) as unknown as T;\n case 'tool':\n return new ToolMessage({\n ...baseParams,\n tool_call_id: (message as unknown as ToolMessage).tool_call_id,\n }) as unknown as T;\n default:\n break;\n }\n }\n\n const {\n lc_kwargs: _lc_kwargs,\n lc_serializable: _lc_serializable,\n lc_namespace: _lc_namespace,\n ...rest\n } = message as T & {\n lc_kwargs?: unknown;\n lc_serializable?: unknown;\n lc_namespace?: unknown;\n };\n\n const cloned = { ...rest, content } as T;\n\n // Sync lc_kwargs.content with the new content to prevent LangChain coercion issues\n const lcKwargs = (message as Record<string, unknown>).lc_kwargs as\n | Record<string, unknown>\n | undefined;\n if (lcKwargs != null) {\n (cloned as Record<string, unknown>).lc_kwargs = {\n ...lcKwargs,\n content: content,\n };\n }\n\n // LangChain messages don't have a direct 'role' property - derive it from getType()\n if (\n 'getType' in message &&\n typeof message.getType === 'function' &&\n !('role' in cloned)\n ) {\n const msgType = (message as unknown as BaseMessage).getType();\n const roleMap: Record<string, string> = {\n human: 'user',\n ai: 'assistant',\n system: 'system',\n tool: 'tool',\n };\n (cloned as Record<string, unknown>).role = roleMap[msgType] || msgType;\n }\n\n return cloned;\n}\n\n/**\n * Checks if a content block is a cache point\n */\nfunction isCachePoint(block: MessageContentComplex): boolean {\n return 'cachePoint' in block && !('type' in block);\n}\n\n/**\n * Checks if a message's content needs cache control stripping.\n * Returns true if content has cachePoint blocks or cache_control fields.\n */\nfunction needsCacheStripping(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n const block = content[i];\n if (isCachePoint(block)) return true;\n if ('cache_control' in block) return true;\n }\n return false;\n}\n\n/**\n * Checks if a message's content has Anthropic cache_control fields.\n */\nfunction hasAnthropicCacheControl(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if ('cache_control' in content[i]) return true;\n }\n return false;\n}\n\n/**\n * Checks if a message's content has Bedrock cachePoint blocks.\n */\nfunction hasBedrockCachePoint(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if (isCachePoint(content[i])) return true;\n }\n return false;\n}\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,\n * then adds fresh cache control to the last 2 user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let userMessagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const isUserMessage =\n ('getType' in originalMessage && originalMessage.getType() === 'human') ||\n ('role' in originalMessage && originalMessage.role === 'user');\n\n const hasArrayContent = Array.isArray(content);\n const needsStripping =\n hasArrayContent &&\n needsCacheStripping(content as MessageContentComplex[]);\n const needsCacheAdd =\n userMessagesModified < 2 &&\n isUserMessage &&\n (typeof content === 'string' || hasArrayContent);\n\n if (!needsStripping && !needsCacheAdd) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n\n if (hasArrayContent) {\n workingContent = deepCloneContent(\n content as MessageContentComplex[]\n ).filter((block) => !isCachePoint(block as MessageContentComplex));\n\n for (let j = 0; j < workingContent.length; j++) {\n const block = workingContent[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n } else if (typeof content === 'string') {\n workingContent = [\n { type: 'text', text: content },\n ] as MessageContentComplex[];\n } else {\n workingContent = [];\n }\n\n if (userMessagesModified >= 2 || !isUserMessage) {\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n continue;\n }\n\n for (let j = workingContent.length - 1; j >= 0; j--) {\n const contentPart = workingContent[j];\n if ('type' in contentPart && contentPart.type === 'text') {\n (contentPart as Anthropic.TextBlockParam).cache_control = {\n type: 'ephemeral',\n };\n userMessagesModified++;\n break;\n }\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Removes all Anthropic cache_control fields from messages\n * Used when switching from Anthropic to Bedrock provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripAnthropicCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasAnthropicCacheControl(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content);\n for (let j = 0; j < clonedContent.length; j++) {\n const block = clonedContent[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Removes all Bedrock cachePoint blocks from messages\n * Used when switching from Bedrock to Anthropic provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripBedrockCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasBedrockCachePoint(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content).filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n );\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points using \"Stable Prefix Caching\" strategy.\n *\n * STRATEGY: Place cache point after the LAST ASSISTANT message only.\n * This ensures the prefix (everything before the cache point) remains STABLE\n * as the conversation grows, maximizing cache hits.\n *\n * Why this works:\n * - System message has its own cachePoint (added in AgentContext)\n * - Tools have their own cachePoint (added in IllumaBedrockConverse)\n * - Conversation history grows, but the PREFIX stays the same\n * - Only the NEW user message is uncached (it's always different)\n *\n * Example conversation flow:\n * Request 1: [System+cachePoint][Tools+cachePoint][User1] → No conversation cache yet\n * Request 2: [System][Tools][User1][Assistant1+cachePoint][User2] → Cache User1+Assistant1\n * Request 3: [System][Tools][User1][Assistant1][User2][Assistant2+cachePoint][User3]\n * → Cache reads User1+A1+User2+A2, cache writes new portion\n *\n * Claude's \"Simplified Cache Management\" automatically looks back up to 20 content\n * blocks from the cache checkpoint to find the longest matching prefix.\n *\n * @param messages - The array of message objects (excluding system message).\n * @returns - The updated array with a single cache point after the last assistant message.\n */\nexport function addBedrockCacheControl<\n T extends Partial<BaseMessage> & MessageWithContent,\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length < 1) {\n debugCache('addBedrockCacheControl: Skipping - no messages', {\n count: messages.length,\n });\n return messages;\n }\n\n debugCache(\n 'addBedrockCacheControl: Processing messages with stable prefix strategy',\n {\n count: messages.length,\n }\n );\n\n // Clone messages to avoid mutating originals\n const updatedMessages: T[] = messages.map((msg) => {\n const content = msg.content;\n if (Array.isArray(content)) {\n // Strip existing cachePoint blocks and Anthropic-style cache_control\n const stripped = content\n .filter((block) => !isCachePoint(block))\n .map((block) => {\n const rec = block as Record<string, unknown>;\n if ('cache_control' in rec) {\n const { cache_control: _, ...rest } = rec;\n return rest as MessageContentComplex;\n }\n return block;\n });\n if (needsCacheStripping(content) || hasAnthropicCacheControl(content)) {\n return cloneMessage(msg, stripped) as T;\n }\n }\n return cloneMessage(msg, content as string | MessageContentComplex[]) as T;\n });\n\n // Helper function to check if a message contains reasoning/thinking blocks\n const hasReasoningBlock = (message: T): boolean => {\n const content = message.content;\n if (!Array.isArray(content)) {\n return false;\n }\n for (const block of content) {\n const type = (block as { type?: string }).type;\n if (\n type === 'reasoning_content' ||\n type === 'reasoning' ||\n type === 'thinking' ||\n type === 'redacted_thinking'\n ) {\n return true;\n }\n }\n return false;\n };\n\n /**\n * Helper to add a cachePoint to a message's content.\n * For strings: wraps into [{type: 'text', text}, {cachePoint}]\n * For arrays: inserts cachePoint after the last non-whitespace text block\n * Returns the new message or null if no suitable insertion point.\n */\n const addCachePointToMessage = (message: T): T | null => {\n const msgContent = message.content;\n\n if (typeof msgContent === 'string' && msgContent !== '') {\n const newContent = [\n { type: ContentTypes.TEXT, text: msgContent },\n { cachePoint: { type: 'default' } },\n ] as MessageContentComplex[];\n return cloneMessage(message, newContent) as T;\n }\n\n if (Array.isArray(msgContent) && msgContent.length > 0) {\n if (hasReasoningBlock(message)) {\n return null;\n }\n // Find the last text block and insert cache point after it\n for (let j = msgContent.length - 1; j >= 0; j--) {\n const type = (msgContent[j] as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (msgContent[j] as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n const newContent = [\n ...msgContent.slice(0, j + 1),\n { cachePoint: { type: 'default' } } as MessageContentComplex,\n ...msgContent.slice(j + 1),\n ];\n return cloneMessage(message, newContent) as T;\n }\n }\n }\n }\n\n return null;\n };\n\n // Add cache points to the last 2 messages (from the end) that have eligible content.\n // This mirrors Anthropic's two-breakpoint strategy for Bedrock's cache API.\n // Skip messages with only whitespace, empty content, or reasoning blocks.\n const MAX_CACHE_POINTS = 2;\n let applied = 0;\n\n for (\n let i = updatedMessages.length - 1;\n i >= 0 && applied < MAX_CACHE_POINTS;\n i--\n ) {\n const message = updatedMessages[i];\n const msgContent = message.content;\n\n // Skip empty/whitespace-only content\n if (msgContent == null) continue;\n if (typeof msgContent === 'string' && msgContent.trim() === '') continue;\n if (Array.isArray(msgContent) && msgContent.length === 0) continue;\n\n // Skip non-string, non-array content (e.g., number, object without type)\n if (typeof msgContent !== 'string' && !Array.isArray(msgContent)) continue;\n\n // Skip AI messages with only whitespace text and reasoning blocks (tool-call scenario)\n const messageType =\n 'getType' in message && typeof message.getType === 'function'\n ? message.getType()\n : 'unknown';\n const role = (message as Record<string, unknown>).role as\n | string\n | undefined;\n const isAi = messageType === 'ai' || role === 'assistant';\n if (isAi && hasReasoningBlock(message)) {\n // Check if all text blocks are whitespace-only\n if (Array.isArray(msgContent)) {\n const hasNonWhitespaceText = msgContent.some((block) => {\n const type = (block as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (block as { text?: string }).text;\n return text != null && text.trim() !== '';\n }\n return false;\n });\n if (!hasNonWhitespaceText) {\n debugCache(\n `⚠️ Message cachePoint SKIPPED at index ${i} (AI with whitespace-only text + reasoning)`\n );\n continue;\n }\n }\n }\n\n // Skip messages that already have cache points (multi-agent scenarios)\n if (\n Array.isArray(msgContent) &&\n msgContent.some((b) => isCachePoint(b as MessageContentComplex))\n ) {\n continue;\n }\n\n const updated = addCachePointToMessage(message);\n if (updated != null) {\n updatedMessages[i] = updated;\n applied++;\n debugCache(\n `📍 Message cachePoint at index ${i} (${typeof message.content === 'string' ? 'string' : 'array'})`\n );\n }\n }\n\n debugCache(\n 'addBedrockCacheControl: Complete - stable prefix caching applied',\n {\n appliedCachePoints: applied,\n totalMessages: updatedMessages.length,\n }\n );\n\n return updatedMessages;\n}\n"],"names":[],"mappings":"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"}
@@ -149,69 +149,16 @@ const formatFromLangChain = (message) => {
149
149
  ...additional_kwargs,
150
150
  };
151
151
  };
152
- function extractReasoningContent(part) {
153
- if (part == null || typeof part !== 'object')
154
- return '';
155
- if (part.type === ContentTypes.THINK) {
156
- const think = part.think;
157
- return typeof think === 'string' ? think : '';
158
- }
159
- if (part.type === ContentTypes.THINKING) {
160
- const thinking = part.thinking;
161
- return typeof thinking === 'string' ? thinking : '';
162
- }
163
- if (part.type === ContentTypes.REASONING) {
164
- const reasoning = part.reasoning;
165
- return typeof reasoning === 'string' ? reasoning : '';
166
- }
167
- if (part.type === ContentTypes.REASONING_CONTENT) {
168
- /* Bedrock/Google nested shape: { reasoningContent: { text: ... } } */
169
- const nested = part
170
- .reasoningContent;
171
- if (nested && typeof nested.text === 'string')
172
- return nested.text;
173
- }
174
- return '';
175
- }
176
152
  /**
177
153
  * Helper function to format an assistant message
178
154
  * @param message The message to format
179
- * @param options Optional formatting options (e.g. DeepSeek reasoning roundtrip)
180
155
  * @returns Array of formatted messages
181
156
  */
182
- function formatAssistantMessage(message, options) {
157
+ function formatAssistantMessage(message) {
183
158
  const formattedMessages = [];
184
159
  let currentContent = [];
185
160
  let lastAIMessage = null;
186
161
  let hasReasoning = false;
187
- let pendingReasoningContent = '';
188
- const shouldPreserveReasoningContent = options?.preserveReasoningContent === true;
189
- const takePendingReasoningContent = () => {
190
- if (!shouldPreserveReasoningContent || !pendingReasoningContent) {
191
- return undefined;
192
- }
193
- const reasoningContent = pendingReasoningContent;
194
- pendingReasoningContent = '';
195
- return reasoningContent;
196
- };
197
- const createAIMessage = (content) => {
198
- const reasoningContent = takePendingReasoningContent();
199
- return new AIMessage({
200
- content,
201
- ...(reasoningContent != null && {
202
- additional_kwargs: { reasoning_content: reasoningContent },
203
- }),
204
- });
205
- };
206
- const attachPendingReasoningContent = (aiMessage) => {
207
- const reasoningContent = takePendingReasoningContent();
208
- if (reasoningContent == null)
209
- return;
210
- aiMessage.additional_kwargs.reasoning_content =
211
- typeof aiMessage.additional_kwargs.reasoning_content === 'string'
212
- ? `${aiMessage.additional_kwargs.reasoning_content}${reasoningContent}`
213
- : reasoningContent;
214
- };
215
162
  if (Array.isArray(message.content)) {
216
163
  for (const part of message.content) {
217
164
  if (part == null) {
@@ -231,13 +178,15 @@ function formatAssistantMessage(message, options) {
231
178
  }, '');
232
179
  content =
233
180
  `${content}\n${part[ContentTypes.TEXT] ?? part.text ?? ''}`.trim();
234
- lastAIMessage = createAIMessage(content);
181
+ lastAIMessage = new AIMessage({ content });
235
182
  formattedMessages.push(lastAIMessage);
236
183
  currentContent = [];
237
184
  continue;
238
185
  }
239
186
  // Create a new AIMessage with this text and prepare for tool calls
240
- lastAIMessage = createAIMessage(part.text != null ? part.text : '');
187
+ lastAIMessage = new AIMessage({
188
+ content: part.text != null ? part.text : '',
189
+ });
241
190
  formattedMessages.push(lastAIMessage);
242
191
  }
243
192
  else if (part.type === ContentTypes.TOOL_CALL) {
@@ -254,12 +203,9 @@ function formatAssistantMessage(message, options) {
254
203
  }
255
204
  if (!lastAIMessage) {
256
205
  // "Heal" the payload by creating an AIMessage to precede the tool call
257
- lastAIMessage = createAIMessage('');
206
+ lastAIMessage = new AIMessage({ content: '' });
258
207
  formattedMessages.push(lastAIMessage);
259
208
  }
260
- else {
261
- attachPendingReasoningContent(lastAIMessage);
262
- }
263
209
  const tool_call = _tool_call;
264
210
  // TODO: investigate; args as dictionary may need to be providers-or-tool-specific
265
211
  let args = _args;
@@ -295,11 +241,9 @@ function formatAssistantMessage(message, options) {
295
241
  }
296
242
  else if (part.type === ContentTypes.THINK ||
297
243
  part.type === ContentTypes.THINKING ||
298
- part.type === ContentTypes.REASONING ||
299
244
  part.type === ContentTypes.REASONING_CONTENT ||
300
245
  part.type === 'redacted_thinking') {
301
246
  hasReasoning = true;
302
- pendingReasoningContent += extractReasoningContent(part);
303
247
  continue;
304
248
  }
305
249
  else if (part.type === ContentTypes.ERROR ||
@@ -325,11 +269,11 @@ function formatAssistantMessage(message, options) {
325
269
  }, '')
326
270
  .trim();
327
271
  if (content) {
328
- formattedMessages.push(createAIMessage(content));
272
+ formattedMessages.push(new AIMessage({ content }));
329
273
  }
330
274
  }
331
275
  else if (currentContent.length > 0) {
332
- formattedMessages.push(createAIMessage(currentContent));
276
+ formattedMessages.push(new AIMessage({ content: currentContent }));
333
277
  }
334
278
  return formattedMessages;
335
279
  }
@@ -564,10 +508,10 @@ function extractSkillName(args) {
564
508
  * When SkillTool is invoked, the body is injected as a HumanMessage into LangGraph state
565
509
  * but NOT persisted to conversation history. On follow-up runs the skill body is lost.
566
510
  * Pass `skills` to reconstruct the HumanMessage at the right position after each skill
567
- * ToolMessage. PRs #96 / #97.
511
+ * ToolMessage. Upstream PRs #96 / #97.
568
512
  * @returns - Object containing formatted messages and updated indexTokenCountMap if provided.
569
513
  */
570
- const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options) => {
514
+ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills) => {
571
515
  const messages = [];
572
516
  // If indexTokenCountMap is provided, create a new map to track the updated indices
573
517
  const updatedIndexTokenCountMap = {};
@@ -645,7 +589,7 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
645
589
  if (discoveredTools.has(toolName)) {
646
590
  /** Valid tool - keep it */
647
591
  filteredContent.push(part);
648
- // Skill body reconstruction (PRs #96/#97): when a
592
+ // Skill body reconstruction (upstream PRs #96/#97): when a
649
593
  // SkillTool tool_call is present in this assistant turn, mark
650
594
  // the skill name so we can reconstruct the body HumanMessage
651
595
  // after the assistant turn has been pushed.
@@ -731,13 +675,11 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
731
675
  }
732
676
  }
733
677
  // Process the assistant message using the helper function
734
- const formattedMessages = formatAssistantMessage(processedMessage, {
735
- preserveReasoningContent: options?.provider === Providers.DEEPSEEK,
736
- });
678
+ const formattedMessages = formatAssistantMessage(processedMessage);
737
679
  messages.push(...formattedMessages);
738
680
  // Capture index range BEFORE skill body injection so injected
739
681
  // HumanMessages are excluded from the assistant's token distribution
740
- // (PR #97 fix).
682
+ // (upstream PR #97 fix).
741
683
  const endMessageIndex = messages.length;
742
684
  if (pendingSkillNames?.size) {
743
685
  for (const skillName of pendingSkillNames) {