@namzu/sdk 30.1.0 → 31.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (714) hide show
  1. package/CHANGELOG.md +576 -0
  2. package/README.md +43 -2
  3. package/dist/advisory/executor.d.ts +2 -1
  4. package/dist/advisory/executor.d.ts.map +1 -1
  5. package/dist/advisory/executor.js +4 -1
  6. package/dist/advisory/executor.js.map +1 -1
  7. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  8. package/dist/agents/ReactiveAgent.js +12 -0
  9. package/dist/agents/ReactiveAgent.js.map +1 -1
  10. package/dist/agents/RouterAgent.d.ts.map +1 -1
  11. package/dist/agents/RouterAgent.js +21 -3
  12. package/dist/agents/RouterAgent.js.map +1 -1
  13. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  14. package/dist/agents/SupervisorAgent.js +34 -11
  15. package/dist/agents/SupervisorAgent.js.map +1 -1
  16. package/dist/agents/runAgent.d.ts +12 -0
  17. package/dist/agents/runAgent.d.ts.map +1 -1
  18. package/dist/agents/runAgent.js +26 -2
  19. package/dist/agents/runAgent.js.map +1 -1
  20. package/dist/authorization/command-line.d.ts +81 -0
  21. package/dist/authorization/command-line.d.ts.map +1 -0
  22. package/dist/authorization/command-line.js +347 -0
  23. package/dist/authorization/command-line.js.map +1 -0
  24. package/dist/authorization/rules.d.ts.map +1 -1
  25. package/dist/authorization/rules.js +22 -1
  26. package/dist/authorization/rules.js.map +1 -1
  27. package/dist/bridge/a2a/client.d.ts +23 -5
  28. package/dist/bridge/a2a/client.d.ts.map +1 -1
  29. package/dist/bridge/a2a/client.js +320 -73
  30. package/dist/bridge/a2a/client.js.map +1 -1
  31. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  32. package/dist/bridge/a2a/mapper.js +4 -0
  33. package/dist/bridge/a2a/mapper.js.map +1 -1
  34. package/dist/bridge/acp/server.d.ts +13 -1
  35. package/dist/bridge/acp/server.d.ts.map +1 -1
  36. package/dist/bridge/acp/server.js +150 -44
  37. package/dist/bridge/acp/server.js.map +1 -1
  38. package/dist/bridge/mcp/connector/adapter.d.ts +3 -2
  39. package/dist/bridge/mcp/connector/adapter.d.ts.map +1 -1
  40. package/dist/bridge/mcp/connector/adapter.js +17 -21
  41. package/dist/bridge/mcp/connector/adapter.js.map +1 -1
  42. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  43. package/dist/bridge/sse/mapper.js +3 -0
  44. package/dist/bridge/sse/mapper.js.map +1 -1
  45. package/dist/compaction/dangling.d.ts +29 -2
  46. package/dist/compaction/dangling.d.ts.map +1 -1
  47. package/dist/compaction/dangling.js +172 -35
  48. package/dist/compaction/dangling.js.map +1 -1
  49. package/dist/compaction/index.d.ts +3 -2
  50. package/dist/compaction/index.d.ts.map +1 -1
  51. package/dist/compaction/index.js +1 -1
  52. package/dist/compaction/index.js.map +1 -1
  53. package/dist/compaction/manual.d.ts +11 -1
  54. package/dist/compaction/manual.d.ts.map +1 -1
  55. package/dist/compaction/manual.js +89 -14
  56. package/dist/compaction/manual.js.map +1 -1
  57. package/dist/compaction/plan.d.ts +19 -6
  58. package/dist/compaction/plan.d.ts.map +1 -1
  59. package/dist/compaction/plan.js +18 -4
  60. package/dist/compaction/plan.js.map +1 -1
  61. package/dist/compaction/retention.d.ts +10 -7
  62. package/dist/compaction/retention.d.ts.map +1 -1
  63. package/dist/compaction/retention.js +26 -8
  64. package/dist/compaction/retention.js.map +1 -1
  65. package/dist/compaction/summary.d.ts +4 -4
  66. package/dist/compaction/summary.js +4 -4
  67. package/dist/compaction/verifier.d.ts +19 -1
  68. package/dist/compaction/verifier.d.ts.map +1 -1
  69. package/dist/compaction/verifier.js +20 -1
  70. package/dist/compaction/verifier.js.map +1 -1
  71. package/dist/config/runtime.d.ts +6 -6
  72. package/dist/connector/BaseConnector.d.ts +6 -4
  73. package/dist/connector/BaseConnector.d.ts.map +1 -1
  74. package/dist/connector/BaseConnector.js +8 -2
  75. package/dist/connector/BaseConnector.js.map +1 -1
  76. package/dist/connector/builtins/http.d.ts +16 -4
  77. package/dist/connector/builtins/http.d.ts.map +1 -1
  78. package/dist/connector/builtins/http.js +177 -44
  79. package/dist/connector/builtins/http.js.map +1 -1
  80. package/dist/connector/builtins/webhook.d.ts +15 -4
  81. package/dist/connector/builtins/webhook.d.ts.map +1 -1
  82. package/dist/connector/builtins/webhook.js +165 -45
  83. package/dist/connector/builtins/webhook.js.map +1 -1
  84. package/dist/connector/execution-contract.d.ts +11 -0
  85. package/dist/connector/execution-contract.d.ts.map +1 -0
  86. package/dist/connector/execution-contract.js +14 -0
  87. package/dist/connector/execution-contract.js.map +1 -0
  88. package/dist/connector/http-operation.d.ts +48 -0
  89. package/dist/connector/http-operation.d.ts.map +1 -0
  90. package/dist/connector/http-operation.js +205 -0
  91. package/dist/connector/http-operation.js.map +1 -0
  92. package/dist/connector/mcp/adapter.d.ts.map +1 -1
  93. package/dist/connector/mcp/adapter.js +8 -3
  94. package/dist/connector/mcp/adapter.js.map +1 -1
  95. package/dist/connector/mcp/client.d.ts +14 -8
  96. package/dist/connector/mcp/client.d.ts.map +1 -1
  97. package/dist/connector/mcp/client.js +160 -44
  98. package/dist/connector/mcp/client.js.map +1 -1
  99. package/dist/connector/mcp/http-sse.d.ts +8 -2
  100. package/dist/connector/mcp/http-sse.d.ts.map +1 -1
  101. package/dist/connector/mcp/http-sse.js +70 -17
  102. package/dist/connector/mcp/http-sse.js.map +1 -1
  103. package/dist/connector/mcp/prompt-adapter.d.ts.map +1 -1
  104. package/dist/connector/mcp/prompt-adapter.js +9 -2
  105. package/dist/connector/mcp/prompt-adapter.js.map +1 -1
  106. package/dist/connector/mcp/stdio.d.ts +2 -2
  107. package/dist/connector/mcp/stdio.d.ts.map +1 -1
  108. package/dist/connector/mcp/stdio.js +14 -50
  109. package/dist/connector/mcp/stdio.js.map +1 -1
  110. package/dist/connector/mcp/streamable-http.d.ts +7 -2
  111. package/dist/connector/mcp/streamable-http.d.ts.map +1 -1
  112. package/dist/connector/mcp/streamable-http.js +66 -18
  113. package/dist/connector/mcp/streamable-http.js.map +1 -1
  114. package/dist/connector/tools/adapter.d.ts +2 -2
  115. package/dist/connector/tools/adapter.d.ts.map +1 -1
  116. package/dist/connector/tools/adapter.js +23 -14
  117. package/dist/connector/tools/adapter.js.map +1 -1
  118. package/dist/connector/tools/definitions.d.ts.map +1 -1
  119. package/dist/connector/tools/definitions.js +5 -2
  120. package/dist/connector/tools/definitions.js.map +1 -1
  121. package/dist/connector/tools/result.d.ts +4 -0
  122. package/dist/connector/tools/result.d.ts.map +1 -0
  123. package/dist/connector/tools/result.js +20 -0
  124. package/dist/connector/tools/result.js.map +1 -0
  125. package/dist/connector/web/guarded-fetch.d.ts +4 -2
  126. package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
  127. package/dist/connector/web/guarded-fetch.js +157 -43
  128. package/dist/connector/web/guarded-fetch.js.map +1 -1
  129. package/dist/constants/sandbox/index.d.ts +3 -2
  130. package/dist/constants/sandbox/index.d.ts.map +1 -1
  131. package/dist/constants/sandbox/index.js +3 -2
  132. package/dist/constants/sandbox/index.js.map +1 -1
  133. package/dist/contracts/api.d.ts +2 -0
  134. package/dist/contracts/api.d.ts.map +1 -1
  135. package/dist/contracts/schemas.d.ts +38 -12
  136. package/dist/contracts/schemas.d.ts.map +1 -1
  137. package/dist/contracts/schemas.js +7 -0
  138. package/dist/contracts/schemas.js.map +1 -1
  139. package/dist/directory/derive-supervisor.d.ts.map +1 -1
  140. package/dist/directory/derive-supervisor.js +8 -0
  141. package/dist/directory/derive-supervisor.js.map +1 -1
  142. package/dist/directory/derive.d.ts.map +1 -1
  143. package/dist/directory/derive.js +8 -0
  144. package/dist/directory/derive.js.map +1 -1
  145. package/dist/directory/load.d.ts.map +1 -1
  146. package/dist/directory/load.js +2 -0
  147. package/dist/directory/load.js.map +1 -1
  148. package/dist/directory/types.d.ts +4 -0
  149. package/dist/directory/types.d.ts.map +1 -1
  150. package/dist/eval/experiment.d.ts +10 -10
  151. package/dist/eval/experiment.d.ts.map +1 -1
  152. package/dist/eval/experiment.js +121 -91
  153. package/dist/eval/experiment.js.map +1 -1
  154. package/dist/eval/judge.d.ts +6 -0
  155. package/dist/eval/judge.d.ts.map +1 -1
  156. package/dist/eval/judge.js +7 -2
  157. package/dist/eval/judge.js.map +1 -1
  158. package/dist/eval/types.d.ts +7 -1
  159. package/dist/eval/types.d.ts.map +1 -1
  160. package/dist/manager/agent/lifecycle.d.ts +12 -10
  161. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  162. package/dist/manager/agent/lifecycle.js +16 -3
  163. package/dist/manager/agent/lifecycle.js.map +1 -1
  164. package/dist/manager/connector/environment.d.ts +2 -2
  165. package/dist/manager/connector/environment.d.ts.map +1 -1
  166. package/dist/manager/connector/environment.js +2 -2
  167. package/dist/manager/connector/environment.js.map +1 -1
  168. package/dist/manager/connector/lifecycle.d.ts +10 -2
  169. package/dist/manager/connector/lifecycle.d.ts.map +1 -1
  170. package/dist/manager/connector/lifecycle.js +273 -24
  171. package/dist/manager/connector/lifecycle.js.map +1 -1
  172. package/dist/manager/connector/tenant.d.ts +5 -4
  173. package/dist/manager/connector/tenant.d.ts.map +1 -1
  174. package/dist/manager/connector/tenant.js +47 -22
  175. package/dist/manager/connector/tenant.js.map +1 -1
  176. package/dist/manager/goal/activation.d.ts +23 -0
  177. package/dist/manager/goal/activation.d.ts.map +1 -0
  178. package/dist/manager/goal/activation.js +41 -0
  179. package/dist/manager/goal/activation.js.map +1 -0
  180. package/dist/manager/index.d.ts +3 -2
  181. package/dist/manager/index.d.ts.map +1 -1
  182. package/dist/manager/index.js.map +1 -1
  183. package/dist/manager/run/persistence.d.ts +2 -0
  184. package/dist/manager/run/persistence.d.ts.map +1 -1
  185. package/dist/manager/run/persistence.js +5 -1
  186. package/dist/manager/run/persistence.js.map +1 -1
  187. package/dist/manager/topic/dependency.d.ts +24 -0
  188. package/dist/manager/topic/dependency.d.ts.map +1 -0
  189. package/dist/manager/topic/dependency.js +13 -0
  190. package/dist/manager/topic/dependency.js.map +1 -0
  191. package/dist/manager/topic/lifecycle.d.ts +9 -21
  192. package/dist/manager/topic/lifecycle.d.ts.map +1 -1
  193. package/dist/manager/topic/lifecycle.js +17 -29
  194. package/dist/manager/topic/lifecycle.js.map +1 -1
  195. package/dist/plugin/index.d.ts +1 -0
  196. package/dist/plugin/index.d.ts.map +1 -1
  197. package/dist/plugin/index.js.map +1 -1
  198. package/dist/plugin/lifecycle.d.ts +15 -0
  199. package/dist/plugin/lifecycle.d.ts.map +1 -1
  200. package/dist/plugin/lifecycle.js +144 -38
  201. package/dist/plugin/lifecycle.js.map +1 -1
  202. package/dist/plugin/loader.d.ts +3 -3
  203. package/dist/plugin/loader.d.ts.map +1 -1
  204. package/dist/plugin/loader.js +58 -12
  205. package/dist/plugin/loader.js.map +1 -1
  206. package/dist/provider/collect-chat-completion.d.ts +7 -0
  207. package/dist/provider/collect-chat-completion.d.ts.map +1 -1
  208. package/dist/provider/collect-chat-completion.js +35 -0
  209. package/dist/provider/collect-chat-completion.js.map +1 -1
  210. package/dist/provider/fallback.d.ts +3 -1
  211. package/dist/provider/fallback.d.ts.map +1 -1
  212. package/dist/provider/fallback.js +62 -10
  213. package/dist/provider/fallback.js.map +1 -1
  214. package/dist/provider/idle-timeout.d.ts +4 -0
  215. package/dist/provider/idle-timeout.d.ts.map +1 -1
  216. package/dist/provider/idle-timeout.js +131 -15
  217. package/dist/provider/idle-timeout.js.map +1 -1
  218. package/dist/provider/index.d.ts +2 -0
  219. package/dist/provider/index.d.ts.map +1 -1
  220. package/dist/provider/index.js +1 -0
  221. package/dist/provider/index.js.map +1 -1
  222. package/dist/provider/instrumentation.d.ts.map +1 -1
  223. package/dist/provider/instrumentation.js +1 -0
  224. package/dist/provider/instrumentation.js.map +1 -1
  225. package/dist/provider/retry.d.ts.map +1 -1
  226. package/dist/provider/retry.js +13 -1
  227. package/dist/provider/retry.js.map +1 -1
  228. package/dist/public-runtime.d.ts +57 -24
  229. package/dist/public-runtime.d.ts.map +1 -1
  230. package/dist/public-runtime.js +42 -35
  231. package/dist/public-runtime.js.map +1 -1
  232. package/dist/public-tools.d.ts +1 -0
  233. package/dist/public-tools.d.ts.map +1 -1
  234. package/dist/public-tools.js +5 -0
  235. package/dist/public-tools.js.map +1 -1
  236. package/dist/public-types.d.ts +33 -12
  237. package/dist/public-types.d.ts.map +1 -1
  238. package/dist/rag/embedding.d.ts +8 -3
  239. package/dist/rag/embedding.d.ts.map +1 -1
  240. package/dist/rag/embedding.js +120 -9
  241. package/dist/rag/embedding.js.map +1 -1
  242. package/dist/rag/index.d.ts +1 -1
  243. package/dist/rag/index.d.ts.map +1 -1
  244. package/dist/rag/index.js +1 -1
  245. package/dist/rag/index.js.map +1 -1
  246. package/dist/rag/ingestion.d.ts +2 -2
  247. package/dist/rag/ingestion.d.ts.map +1 -1
  248. package/dist/rag/ingestion.js +15 -3
  249. package/dist/rag/ingestion.js.map +1 -1
  250. package/dist/rag/knowledge-base.d.ts +3 -3
  251. package/dist/rag/knowledge-base.d.ts.map +1 -1
  252. package/dist/rag/knowledge-base.js +8 -4
  253. package/dist/rag/knowledge-base.js.map +1 -1
  254. package/dist/rag/operation.d.ts +10 -0
  255. package/dist/rag/operation.d.ts.map +1 -0
  256. package/dist/rag/operation.js +26 -0
  257. package/dist/rag/operation.js.map +1 -0
  258. package/dist/rag/rag-tool.d.ts.map +1 -1
  259. package/dist/rag/rag-tool.js +6 -3
  260. package/dist/rag/rag-tool.js.map +1 -1
  261. package/dist/rag/retriever.d.ts +2 -2
  262. package/dist/rag/retriever.d.ts.map +1 -1
  263. package/dist/rag/retriever.js +31 -15
  264. package/dist/rag/retriever.js.map +1 -1
  265. package/dist/rag/vector-store.d.ts +3 -3
  266. package/dist/rag/vector-store.d.ts.map +1 -1
  267. package/dist/rag/vector-store.js +4 -2
  268. package/dist/rag/vector-store.js.map +1 -1
  269. package/dist/registry/command/kernel-commands.d.ts +16 -4
  270. package/dist/registry/command/kernel-commands.d.ts.map +1 -1
  271. package/dist/registry/command/kernel-commands.js +171 -0
  272. package/dist/registry/command/kernel-commands.js.map +1 -1
  273. package/dist/run/reporter.d.ts.map +1 -1
  274. package/dist/run/reporter.js +9 -0
  275. package/dist/run/reporter.js.map +1 -1
  276. package/dist/run-query/index.d.ts +15 -1
  277. package/dist/run-query/index.d.ts.map +1 -1
  278. package/dist/run-query/index.js +69 -15
  279. package/dist/run-query/index.js.map +1 -1
  280. package/dist/runtime/query/context.d.ts.map +1 -1
  281. package/dist/runtime/query/context.js +10 -3
  282. package/dist/runtime/query/context.js.map +1 -1
  283. package/dist/runtime/query/executor.d.ts +3 -0
  284. package/dist/runtime/query/executor.d.ts.map +1 -1
  285. package/dist/runtime/query/executor.js +80 -18
  286. package/dist/runtime/query/executor.js.map +1 -1
  287. package/dist/runtime/query/guard.d.ts +9 -0
  288. package/dist/runtime/query/guard.d.ts.map +1 -1
  289. package/dist/runtime/query/guard.js +11 -0
  290. package/dist/runtime/query/guard.js.map +1 -1
  291. package/dist/runtime/query/index.d.ts +30 -1
  292. package/dist/runtime/query/index.d.ts.map +1 -1
  293. package/dist/runtime/query/index.js +548 -95
  294. package/dist/runtime/query/index.js.map +1 -1
  295. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  296. package/dist/runtime/query/iteration/index.js +74 -14
  297. package/dist/runtime/query/iteration/index.js.map +1 -1
  298. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  299. package/dist/runtime/query/iteration/phases/compaction.js +94 -53
  300. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  301. package/dist/runtime/query/iteration/phases/context.d.ts +3 -0
  302. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  303. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  304. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  305. package/dist/runtime/query/iteration/phases/tool-review.js +21 -0
  306. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  307. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  308. package/dist/runtime/query/iteration/stream-turn.js +4 -0
  309. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  310. package/dist/runtime/query/project-instructions.d.ts +69 -0
  311. package/dist/runtime/query/project-instructions.d.ts.map +1 -0
  312. package/dist/runtime/query/project-instructions.js +137 -0
  313. package/dist/runtime/query/project-instructions.js.map +1 -0
  314. package/dist/runtime/query/request-rich-content.d.ts +22 -0
  315. package/dist/runtime/query/request-rich-content.d.ts.map +1 -0
  316. package/dist/runtime/query/request-rich-content.js +122 -0
  317. package/dist/runtime/query/request-rich-content.js.map +1 -0
  318. package/dist/runtime/query/result.d.ts.map +1 -1
  319. package/dist/runtime/query/result.js +12 -1
  320. package/dist/runtime/query/result.js.map +1 -1
  321. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  322. package/dist/runtime/query/resume-pending.js +19 -20
  323. package/dist/runtime/query/resume-pending.js.map +1 -1
  324. package/dist/runtime/query/resume-run.d.ts.map +1 -1
  325. package/dist/runtime/query/resume-run.js +54 -8
  326. package/dist/runtime/query/resume-run.js.map +1 -1
  327. package/dist/runtime/query/run-state.d.ts +5 -0
  328. package/dist/runtime/query/run-state.d.ts.map +1 -1
  329. package/dist/runtime/query/run-state.js +6 -1
  330. package/dist/runtime/query/run-state.js.map +1 -1
  331. package/dist/runtime/query/sandbox-lifecycle.d.ts +54 -0
  332. package/dist/runtime/query/sandbox-lifecycle.d.ts.map +1 -0
  333. package/dist/runtime/query/sandbox-lifecycle.js +167 -0
  334. package/dist/runtime/query/sandbox-lifecycle.js.map +1 -0
  335. package/dist/sandbox/isolation.d.ts.map +1 -1
  336. package/dist/sandbox/isolation.js +9 -0
  337. package/dist/sandbox/isolation.js.map +1 -1
  338. package/dist/sandbox/provider/local.d.ts +56 -6
  339. package/dist/sandbox/provider/local.d.ts.map +1 -1
  340. package/dist/sandbox/provider/local.js +181 -76
  341. package/dist/sandbox/provider/local.js.map +1 -1
  342. package/dist/sandbox/terminal.d.ts +13 -5
  343. package/dist/sandbox/terminal.d.ts.map +1 -1
  344. package/dist/sandbox/terminal.js +11 -3
  345. package/dist/sandbox/terminal.js.map +1 -1
  346. package/dist/session/errors.d.ts +28 -29
  347. package/dist/session/errors.d.ts.map +1 -1
  348. package/dist/session/errors.js +28 -29
  349. package/dist/session/errors.js.map +1 -1
  350. package/dist/session/handoff/broadcast.d.ts +5 -8
  351. package/dist/session/handoff/broadcast.d.ts.map +1 -1
  352. package/dist/session/handoff/broadcast.js +16 -6
  353. package/dist/session/handoff/broadcast.js.map +1 -1
  354. package/dist/session/handoff/single.d.ts +5 -8
  355. package/dist/session/handoff/single.d.ts.map +1 -1
  356. package/dist/session/handoff/single.js +7 -4
  357. package/dist/session/handoff/single.js.map +1 -1
  358. package/dist/store/attachment/index.d.ts +11 -3
  359. package/dist/store/attachment/index.d.ts.map +1 -1
  360. package/dist/store/attachment/index.js +49 -4
  361. package/dist/store/attachment/index.js.map +1 -1
  362. package/dist/store/feedback/conformance.d.ts.map +1 -1
  363. package/dist/store/feedback/conformance.js +32 -0
  364. package/dist/store/feedback/conformance.js.map +1 -1
  365. package/dist/store/feedback/disk.d.ts +8 -6
  366. package/dist/store/feedback/disk.d.ts.map +1 -1
  367. package/dist/store/feedback/disk.js +111 -76
  368. package/dist/store/feedback/disk.js.map +1 -1
  369. package/dist/store/goal/index.d.ts +111 -0
  370. package/dist/store/goal/index.d.ts.map +1 -0
  371. package/dist/store/goal/index.js +436 -0
  372. package/dist/store/goal/index.js.map +1 -0
  373. package/dist/store/index.d.ts +1 -1
  374. package/dist/store/index.d.ts.map +1 -1
  375. package/dist/store/index.js +1 -1
  376. package/dist/store/index.js.map +1 -1
  377. package/dist/store/kv/revision-record-store.d.ts +70 -0
  378. package/dist/store/kv/revision-record-store.d.ts.map +1 -0
  379. package/dist/store/kv/revision-record-store.js +290 -0
  380. package/dist/store/kv/revision-record-store.js.map +1 -0
  381. package/dist/store/memory/disk.d.ts +9 -7
  382. package/dist/store/memory/disk.d.ts.map +1 -1
  383. package/dist/store/memory/disk.js +256 -98
  384. package/dist/store/memory/disk.js.map +1 -1
  385. package/dist/store/run/disk.d.ts +10 -2
  386. package/dist/store/run/disk.d.ts.map +1 -1
  387. package/dist/store/run/disk.js +70 -4
  388. package/dist/store/run/disk.js.map +1 -1
  389. package/dist/store/run/memory.d.ts +4 -2
  390. package/dist/store/run/memory.d.ts.map +1 -1
  391. package/dist/store/run/memory.js +15 -1
  392. package/dist/store/run/memory.js.map +1 -1
  393. package/dist/store/session/disk.d.ts +9 -0
  394. package/dist/store/session/disk.d.ts.map +1 -1
  395. package/dist/store/session/disk.js +57 -5
  396. package/dist/store/session/disk.js.map +1 -1
  397. package/dist/store/session/memory.d.ts +1 -0
  398. package/dist/store/session/memory.d.ts.map +1 -1
  399. package/dist/store/session/memory.js +14 -0
  400. package/dist/store/session/memory.js.map +1 -1
  401. package/dist/store/topic/memory.d.ts +1 -2
  402. package/dist/store/topic/memory.d.ts.map +1 -1
  403. package/dist/store/topic/memory.js +4 -5
  404. package/dist/store/topic/memory.js.map +1 -1
  405. package/dist/store/topic/objective.d.ts +8 -4
  406. package/dist/store/topic/objective.d.ts.map +1 -1
  407. package/dist/store/topic/objective.js +185 -100
  408. package/dist/store/topic/objective.js.map +1 -1
  409. package/dist/store/topic/state.d.ts +1 -2
  410. package/dist/store/topic/state.d.ts.map +1 -1
  411. package/dist/store/topic/state.js +50 -62
  412. package/dist/store/topic/state.js.map +1 -1
  413. package/dist/tools/builtins/bash.d.ts.map +1 -1
  414. package/dist/tools/builtins/bash.js +20 -0
  415. package/dist/tools/builtins/bash.js.map +1 -1
  416. package/dist/tools/builtins/computer-use.d.ts +9 -1
  417. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  418. package/dist/tools/builtins/computer-use.js.map +1 -1
  419. package/dist/tools/builtins/content-fingerprint.d.ts +8 -7
  420. package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
  421. package/dist/tools/builtins/content-fingerprint.js +9 -10
  422. package/dist/tools/builtins/content-fingerprint.js.map +1 -1
  423. package/dist/tools/builtins/edit.d.ts +6 -1
  424. package/dist/tools/builtins/edit.d.ts.map +1 -1
  425. package/dist/tools/builtins/edit.js +166 -27
  426. package/dist/tools/builtins/edit.js.map +1 -1
  427. package/dist/tools/builtins/write-file.d.ts.map +1 -1
  428. package/dist/tools/builtins/write-file.js +45 -19
  429. package/dist/tools/builtins/write-file.js.map +1 -1
  430. package/dist/tools/command-arguments.d.ts +43 -0
  431. package/dist/tools/command-arguments.d.ts.map +1 -0
  432. package/dist/tools/command-arguments.js +52 -0
  433. package/dist/tools/command-arguments.js.map +1 -0
  434. package/dist/tools/coordinator/index.d.ts +2 -0
  435. package/dist/tools/coordinator/index.d.ts.map +1 -1
  436. package/dist/tools/coordinator/index.js +3 -1
  437. package/dist/tools/coordinator/index.js.map +1 -1
  438. package/dist/tools/defineTool.d.ts +10 -0
  439. package/dist/tools/defineTool.d.ts.map +1 -1
  440. package/dist/tools/defineTool.js +1 -0
  441. package/dist/tools/defineTool.js.map +1 -1
  442. package/dist/tools/env-scrub.d.ts +7 -5
  443. package/dist/tools/env-scrub.d.ts.map +1 -1
  444. package/dist/tools/env-scrub.js +7 -5
  445. package/dist/tools/env-scrub.js.map +1 -1
  446. package/dist/tools/goal/index.d.ts +20 -0
  447. package/dist/tools/goal/index.d.ts.map +1 -0
  448. package/dist/tools/goal/index.js +104 -0
  449. package/dist/tools/goal/index.js.map +1 -0
  450. package/dist/tools/memory/index.d.ts +16 -0
  451. package/dist/tools/memory/index.d.ts.map +1 -1
  452. package/dist/tools/memory/index.js +6 -2
  453. package/dist/tools/memory/index.js.map +1 -1
  454. package/dist/tools/memory/search.d.ts +4 -1
  455. package/dist/tools/memory/search.d.ts.map +1 -1
  456. package/dist/tools/memory/search.js +10 -2
  457. package/dist/tools/memory/search.js.map +1 -1
  458. package/dist/types/agent/base.d.ts +11 -3
  459. package/dist/types/agent/base.d.ts.map +1 -1
  460. package/dist/types/agent/factory.d.ts +2 -0
  461. package/dist/types/agent/factory.d.ts.map +1 -1
  462. package/dist/types/agent/reactive.d.ts +2 -0
  463. package/dist/types/agent/reactive.d.ts.map +1 -1
  464. package/dist/types/agent/supervisor.d.ts +6 -0
  465. package/dist/types/agent/supervisor.d.ts.map +1 -1
  466. package/dist/types/agent/task.d.ts +6 -2
  467. package/dist/types/agent/task.d.ts.map +1 -1
  468. package/dist/types/connector/core.d.ts +13 -8
  469. package/dist/types/connector/core.d.ts.map +1 -1
  470. package/dist/types/connector/definition.d.ts +37 -18
  471. package/dist/types/connector/definition.d.ts.map +1 -1
  472. package/dist/types/connector/http.d.ts +6 -0
  473. package/dist/types/connector/http.d.ts.map +1 -1
  474. package/dist/types/connector/mcp.d.ts +24 -6
  475. package/dist/types/connector/mcp.d.ts.map +1 -1
  476. package/dist/types/connector/tenant.d.ts +17 -7
  477. package/dist/types/connector/tenant.d.ts.map +1 -1
  478. package/dist/types/connector/webhook.d.ts +6 -0
  479. package/dist/types/connector/webhook.d.ts.map +1 -1
  480. package/dist/types/goal/index.d.ts +46 -0
  481. package/dist/types/goal/index.d.ts.map +1 -0
  482. package/dist/types/goal/index.js +2 -0
  483. package/dist/types/goal/index.js.map +1 -0
  484. package/dist/types/ids/index.d.ts +2 -0
  485. package/dist/types/ids/index.d.ts.map +1 -1
  486. package/dist/types/ids/index.js.map +1 -1
  487. package/dist/types/message/index.d.ts +75 -10
  488. package/dist/types/message/index.d.ts.map +1 -1
  489. package/dist/types/message/index.js +49 -3
  490. package/dist/types/message/index.js.map +1 -1
  491. package/dist/types/plugin/index.d.ts +7 -5
  492. package/dist/types/plugin/index.d.ts.map +1 -1
  493. package/dist/types/plugin/index.js.map +1 -1
  494. package/dist/types/provider/chat.d.ts +27 -4
  495. package/dist/types/provider/chat.d.ts.map +1 -1
  496. package/dist/types/provider/interface.d.ts +18 -7
  497. package/dist/types/provider/interface.d.ts.map +1 -1
  498. package/dist/types/provider/stream.d.ts +6 -0
  499. package/dist/types/provider/stream.d.ts.map +1 -1
  500. package/dist/types/rag/embedding.d.ts +15 -2
  501. package/dist/types/rag/embedding.d.ts.map +1 -1
  502. package/dist/types/rag/ingestion.d.ts +2 -2
  503. package/dist/types/rag/ingestion.d.ts.map +1 -1
  504. package/dist/types/rag/knowledge-base.d.ts +3 -3
  505. package/dist/types/rag/knowledge-base.d.ts.map +1 -1
  506. package/dist/types/rag/retrieval.d.ts +2 -2
  507. package/dist/types/rag/retrieval.d.ts.map +1 -1
  508. package/dist/types/rag/scope.d.ts +4 -0
  509. package/dist/types/rag/scope.d.ts.map +1 -1
  510. package/dist/types/rag/vector.d.ts +5 -2
  511. package/dist/types/rag/vector.d.ts.map +1 -1
  512. package/dist/types/run/config.d.ts +20 -0
  513. package/dist/types/run/config.d.ts.map +1 -1
  514. package/dist/types/run/events.d.ts +25 -1
  515. package/dist/types/run/events.d.ts.map +1 -1
  516. package/dist/types/run/events.js.map +1 -1
  517. package/dist/types/run/metadata.d.ts +2 -0
  518. package/dist/types/run/metadata.d.ts.map +1 -1
  519. package/dist/types/run/step.d.ts +2 -6
  520. package/dist/types/run/step.d.ts.map +1 -1
  521. package/dist/types/run/step.js.map +1 -1
  522. package/dist/types/run/store.d.ts +55 -2
  523. package/dist/types/run/store.d.ts.map +1 -1
  524. package/dist/types/sandbox/index.d.ts +47 -6
  525. package/dist/types/sandbox/index.d.ts.map +1 -1
  526. package/dist/types/sandbox/index.js +23 -10
  527. package/dist/types/sandbox/index.js.map +1 -1
  528. package/dist/types/session/store.d.ts +13 -0
  529. package/dist/types/session/store.d.ts.map +1 -1
  530. package/dist/types/tool/index.d.ts +33 -11
  531. package/dist/types/tool/index.d.ts.map +1 -1
  532. package/dist/types/tool/index.js.map +1 -1
  533. package/dist/types/topic/entity.d.ts +3 -4
  534. package/dist/types/topic/entity.d.ts.map +1 -1
  535. package/dist/types/topic/store.d.ts +3 -3
  536. package/dist/utils/id.d.ts +3 -1
  537. package/dist/utils/id.d.ts.map +1 -1
  538. package/dist/utils/id.js +4 -0
  539. package/dist/utils/id.js.map +1 -1
  540. package/dist/utils/process-environment.d.ts +26 -0
  541. package/dist/utils/process-environment.d.ts.map +1 -0
  542. package/dist/utils/process-environment.js +78 -0
  543. package/dist/utils/process-environment.js.map +1 -0
  544. package/dist/vault/InMemoryCredentialVault.d.ts +2 -0
  545. package/dist/vault/InMemoryCredentialVault.d.ts.map +1 -1
  546. package/dist/vault/InMemoryCredentialVault.js +24 -4
  547. package/dist/vault/InMemoryCredentialVault.js.map +1 -1
  548. package/dist/vault/instrumentation.d.ts.map +1 -1
  549. package/dist/vault/instrumentation.js +15 -0
  550. package/dist/vault/instrumentation.js.map +1 -1
  551. package/package.json +1 -1
  552. package/src/advisory/executor.ts +4 -1
  553. package/src/agents/ReactiveAgent.ts +12 -0
  554. package/src/agents/RouterAgent.ts +25 -3
  555. package/src/agents/SupervisorAgent.ts +33 -11
  556. package/src/agents/runAgent.ts +38 -2
  557. package/src/authorization/command-line.ts +390 -0
  558. package/src/authorization/rules.ts +22 -1
  559. package/src/bridge/a2a/client.ts +384 -97
  560. package/src/bridge/a2a/mapper.ts +5 -0
  561. package/src/bridge/acp/server.ts +184 -42
  562. package/src/bridge/mcp/connector/adapter.ts +28 -20
  563. package/src/bridge/sse/mapper.ts +3 -0
  564. package/src/compaction/dangling.ts +203 -38
  565. package/src/compaction/index.ts +9 -2
  566. package/src/compaction/manual.ts +125 -18
  567. package/src/compaction/plan.ts +42 -11
  568. package/src/compaction/retention.ts +23 -8
  569. package/src/compaction/summary.ts +4 -4
  570. package/src/compaction/verifier.ts +66 -0
  571. package/src/connector/BaseConnector.ts +19 -4
  572. package/src/connector/builtins/http.ts +236 -46
  573. package/src/connector/builtins/webhook.ts +207 -47
  574. package/src/connector/execution-contract.ts +27 -0
  575. package/src/connector/http-operation.ts +238 -0
  576. package/src/connector/mcp/adapter.ts +8 -3
  577. package/src/connector/mcp/client.ts +207 -58
  578. package/src/connector/mcp/http-sse.ts +91 -23
  579. package/src/connector/mcp/prompt-adapter.ts +8 -2
  580. package/src/connector/mcp/stdio.ts +21 -46
  581. package/src/connector/mcp/streamable-http.ts +86 -22
  582. package/src/connector/tools/adapter.ts +23 -14
  583. package/src/connector/tools/definitions.ts +5 -2
  584. package/src/connector/tools/result.ts +21 -0
  585. package/src/connector/web/guarded-fetch.ts +188 -48
  586. package/src/constants/sandbox/index.ts +3 -2
  587. package/src/contracts/api.ts +2 -0
  588. package/src/contracts/schemas.ts +7 -0
  589. package/src/directory/derive-supervisor.ts +8 -0
  590. package/src/directory/derive.ts +8 -0
  591. package/src/directory/load.ts +2 -0
  592. package/src/directory/types.ts +4 -0
  593. package/src/eval/experiment.ts +160 -111
  594. package/src/eval/judge.ts +13 -2
  595. package/src/eval/types.ts +7 -1
  596. package/src/manager/agent/lifecycle.ts +28 -12
  597. package/src/manager/connector/environment.ts +3 -1
  598. package/src/manager/connector/lifecycle.ts +364 -24
  599. package/src/manager/connector/tenant.ts +56 -23
  600. package/src/manager/goal/activation.ts +52 -0
  601. package/src/manager/index.ts +10 -2
  602. package/src/manager/run/persistence.ts +6 -1
  603. package/src/manager/topic/dependency.ts +39 -0
  604. package/src/manager/topic/lifecycle.ts +19 -31
  605. package/src/plugin/index.ts +1 -0
  606. package/src/plugin/lifecycle.ts +180 -39
  607. package/src/plugin/loader.ts +70 -13
  608. package/src/pricing/rates.source.json +315 -204
  609. package/src/provider/collect-chat-completion.ts +36 -0
  610. package/src/provider/fallback.ts +79 -11
  611. package/src/provider/idle-timeout.ts +143 -17
  612. package/src/provider/index.ts +2 -0
  613. package/src/provider/instrumentation.ts +1 -0
  614. package/src/provider/retry.ts +16 -1
  615. package/src/public-runtime.ts +190 -32
  616. package/src/public-tools.ts +5 -0
  617. package/src/public-types.ts +85 -11
  618. package/src/rag/embedding.ts +147 -19
  619. package/src/rag/index.ts +1 -1
  620. package/src/rag/ingestion.ts +16 -2
  621. package/src/rag/knowledge-base.ts +13 -4
  622. package/src/rag/operation.ts +28 -0
  623. package/src/rag/rag-tool.ts +6 -3
  624. package/src/rag/retriever.ts +62 -11
  625. package/src/rag/vector-store.ts +8 -2
  626. package/src/registry/command/kernel-commands.ts +223 -4
  627. package/src/run/reporter.ts +10 -0
  628. package/src/run-query/index.ts +84 -15
  629. package/src/runtime/query/context.ts +13 -3
  630. package/src/runtime/query/executor.ts +90 -17
  631. package/src/runtime/query/guard.ts +12 -0
  632. package/src/runtime/query/index.ts +652 -103
  633. package/src/runtime/query/iteration/index.ts +89 -13
  634. package/src/runtime/query/iteration/phases/compaction.ts +120 -55
  635. package/src/runtime/query/iteration/phases/context.ts +4 -0
  636. package/src/runtime/query/iteration/phases/tool-review.ts +28 -0
  637. package/src/runtime/query/iteration/stream-turn.ts +3 -0
  638. package/src/runtime/query/project-instructions.ts +212 -0
  639. package/src/runtime/query/request-rich-content.ts +153 -0
  640. package/src/runtime/query/result.ts +12 -1
  641. package/src/runtime/query/resume-pending.ts +20 -18
  642. package/src/runtime/query/resume-run.ts +52 -8
  643. package/src/runtime/query/run-state.ts +11 -1
  644. package/src/runtime/query/sandbox-lifecycle.ts +210 -0
  645. package/src/sandbox/isolation.ts +9 -0
  646. package/src/sandbox/provider/local.ts +255 -90
  647. package/src/sandbox/terminal.ts +13 -5
  648. package/src/session/errors.ts +68 -42
  649. package/src/session/handoff/broadcast.ts +20 -14
  650. package/src/session/handoff/single.ts +14 -13
  651. package/src/store/attachment/index.ts +65 -3
  652. package/src/store/feedback/conformance.ts +39 -0
  653. package/src/store/feedback/disk.ts +128 -82
  654. package/src/store/goal/index.ts +594 -0
  655. package/src/store/index.ts +1 -1
  656. package/src/store/kv/revision-record-store.ts +346 -0
  657. package/src/store/memory/disk.ts +288 -113
  658. package/src/store/run/disk.ts +96 -5
  659. package/src/store/run/memory.ts +22 -2
  660. package/src/store/session/disk.ts +93 -6
  661. package/src/store/session/memory.ts +22 -0
  662. package/src/store/topic/memory.ts +4 -5
  663. package/src/store/topic/objective.ts +215 -100
  664. package/src/store/topic/state.ts +100 -63
  665. package/src/tools/builtins/bash.ts +21 -0
  666. package/src/tools/builtins/computer-use.ts +9 -1
  667. package/src/tools/builtins/content-fingerprint.ts +9 -12
  668. package/src/tools/builtins/edit.ts +190 -27
  669. package/src/tools/builtins/write-file.ts +55 -19
  670. package/src/tools/command-arguments.ts +54 -0
  671. package/src/tools/coordinator/index.ts +4 -1
  672. package/src/tools/defineTool.ts +11 -0
  673. package/src/tools/env-scrub.ts +7 -5
  674. package/src/tools/goal/index.ts +129 -0
  675. package/src/tools/memory/index.ts +26 -3
  676. package/src/tools/memory/search.ts +20 -3
  677. package/src/types/agent/base.ts +12 -3
  678. package/src/types/agent/factory.ts +2 -0
  679. package/src/types/agent/reactive.ts +2 -0
  680. package/src/types/agent/supervisor.ts +6 -0
  681. package/src/types/agent/task.ts +6 -2
  682. package/src/types/connector/core.ts +13 -8
  683. package/src/types/connector/definition.ts +54 -20
  684. package/src/types/connector/http.ts +7 -0
  685. package/src/types/connector/mcp.ts +27 -6
  686. package/src/types/connector/tenant.ts +21 -7
  687. package/src/types/connector/webhook.ts +7 -0
  688. package/src/types/goal/index.ts +50 -0
  689. package/src/types/ids/index.ts +2 -0
  690. package/src/types/message/index.ts +143 -9
  691. package/src/types/plugin/index.ts +7 -5
  692. package/src/types/provider/chat.ts +35 -4
  693. package/src/types/provider/interface.ts +22 -7
  694. package/src/types/provider/stream.ts +6 -0
  695. package/src/types/rag/embedding.ts +16 -2
  696. package/src/types/rag/ingestion.ts +2 -1
  697. package/src/types/rag/knowledge-base.ts +7 -3
  698. package/src/types/rag/retrieval.ts +2 -1
  699. package/src/types/rag/scope.ts +5 -0
  700. package/src/types/rag/vector.ts +5 -2
  701. package/src/types/run/config.ts +20 -0
  702. package/src/types/run/events.ts +28 -3
  703. package/src/types/run/metadata.ts +2 -0
  704. package/src/types/run/step.ts +2 -7
  705. package/src/types/run/store.ts +60 -2
  706. package/src/types/sandbox/index.ts +57 -14
  707. package/src/types/session/store.ts +18 -0
  708. package/src/types/tool/index.ts +33 -11
  709. package/src/types/topic/entity.ts +3 -4
  710. package/src/types/topic/store.ts +3 -3
  711. package/src/utils/id.ts +6 -0
  712. package/src/utils/process-environment.ts +101 -0
  713. package/src/vault/InMemoryCredentialVault.ts +29 -4
  714. package/src/vault/instrumentation.ts +24 -0
@@ -1,4 +1,5 @@
1
- import type { Message } from '../types/message/index.js'
1
+ import { NamzuError } from '../types/errors/index.js'
2
+ import type { AssistantMessage, Message, ToolCall, ToolMessage } from '../types/message/index.js'
2
3
 
3
4
  /**
4
5
  * Represents the result of scanning messages for dangling tool call/result pairs.
@@ -68,54 +69,73 @@ function isToolMessage(message: Message): boolean {
68
69
  * // result.assistantsWithUnmatchedCalls = [1] (index 1 has unmatched tool call)
69
70
  * ```
70
71
  */
71
- export function findDanglingMessages(messages: Message[]): DanglingResult {
72
+ export function findDanglingMessages(messages: readonly Message[]): DanglingResult {
72
73
  const assistantsWithUnmatchedCalls: number[] = []
73
74
  const orphanedToolMessages: number[] = []
75
+ const seenToolCallIds = new Set<string>()
76
+
77
+ // Provider tool results are not joins over a conversation-wide id map.
78
+ // They form one contiguous batch immediately after the assistant turn that
79
+ // declared them. A future call cannot own an earlier result, and a result
80
+ // displaced past a user/assistant/system message cannot answer backwards.
81
+ let index = 0
82
+ while (index < messages.length) {
83
+ const message = messages[index]
84
+ if (!message) {
85
+ index++
86
+ continue
87
+ }
74
88
 
75
- // Build a set of all tool call IDs that exist in assistant messages
76
- // along with their coverage map (which tool messages satisfy them)
77
- const toolCallIds = new Map<string, { assistantIndex: number; satisfied: boolean }>()
89
+ if (!hasToolCalls(message)) {
90
+ if (isToolMessage(message)) orphanedToolMessages.push(index)
91
+ index++
92
+ continue
93
+ }
78
94
 
79
- for (let i = 0; i < messages.length; i++) {
80
- const message = messages[i]
81
- if (!message) continue
95
+ const calls = (message as AssistantMessage).toolCalls ?? []
96
+ const callIds = new Set<string>()
97
+ let invalidOwner = false
98
+ for (const call of calls) {
99
+ if (!call.id || callIds.has(call.id) || seenToolCallIds.has(call.id)) invalidOwner = true
100
+ callIds.add(call.id)
101
+ seenToolCallIds.add(call.id)
102
+ }
82
103
 
83
- if (hasToolCalls(message)) {
84
- // Record all tool calls from this assistant message
85
- const assistantMsg = message as { toolCalls?: Array<{ id: string }> }
86
- if (assistantMsg.toolCalls) {
87
- for (const toolCall of assistantMsg.toolCalls) {
88
- toolCallIds.set(toolCall.id, { assistantIndex: i, satisfied: false })
89
- }
104
+ let resultEnd = index + 1
105
+ while (resultEnd < messages.length && messages[resultEnd]?.role === CONSTANTS.TOOL_ROLE) {
106
+ resultEnd++
107
+ }
108
+
109
+ // Keep the last immediate result for an id. A late real result can
110
+ // follow a synthetic cancellation result after a crash; keeping the
111
+ // first would preserve the stale guess and discard the observed fact.
112
+ const lastResultById = new Map<string, number>()
113
+ for (let resultIndex = index + 1; resultIndex < resultEnd; resultIndex++) {
114
+ const result = messages[resultIndex]
115
+ if (result?.role === CONSTANTS.TOOL_ROLE) {
116
+ lastResultById.set(result.toolCallId, resultIndex)
90
117
  }
91
118
  }
92
- }
93
119
 
94
- // Second pass: mark satisfied tool calls and find orphaned tool messages
95
- for (let i = 0; i < messages.length; i++) {
96
- const message = messages[i]
97
- if (!message) continue
98
-
99
- if (isToolMessage(message)) {
100
- const toolMsg = message as { toolCallId: string }
101
- if (toolCallIds.has(toolMsg.toolCallId)) {
102
- // Tool message satisfies a preceding tool call
103
- const entry = toolCallIds.get(toolMsg.toolCallId)
104
- if (entry) {
105
- entry.satisfied = true
106
- }
107
- } else {
108
- // Tool message has no matching tool call
109
- orphanedToolMessages.push(i)
120
+ const answered = new Set<string>()
121
+ for (let resultIndex = index + 1; resultIndex < resultEnd; resultIndex++) {
122
+ const result = messages[resultIndex]
123
+ if (result?.role !== CONSTANTS.TOOL_ROLE) continue
124
+ if (
125
+ invalidOwner ||
126
+ !callIds.has(result.toolCallId) ||
127
+ lastResultById.get(result.toolCallId) !== resultIndex
128
+ ) {
129
+ orphanedToolMessages.push(resultIndex)
130
+ continue
110
131
  }
132
+ answered.add(result.toolCallId)
111
133
  }
112
- }
113
134
 
114
- // Third pass: identify unsatisfied tool calls
115
- for (const entry of toolCallIds.values()) {
116
- if (!entry.satisfied) {
117
- assistantsWithUnmatchedCalls.push(entry.assistantIndex)
135
+ if (invalidOwner || calls.some((call) => !answered.has(call.id))) {
136
+ assistantsWithUnmatchedCalls.push(index)
118
137
  }
138
+ index = resultEnd
119
139
  }
120
140
 
121
141
  return {
@@ -152,7 +172,7 @@ export function findDanglingMessages(messages: Message[]): DanglingResult {
152
172
  * // Result: [{ role: 'user', content: 'test' }, { role: 'user', content: 'next' }]
153
173
  * ```
154
174
  */
155
- export function removeDanglingMessages(messages: Message[]): Message[] {
175
+ export function removeDanglingMessages(messages: readonly Message[]): Message[] {
156
176
  const result = findDanglingMessages(messages)
157
177
 
158
178
  if (result.isValid) {
@@ -203,6 +223,151 @@ export function removeDanglingMessages(messages: Message[]): Message[] {
203
223
  return messages.filter((_, idx) => !indicesToRemove.has(idx))
204
224
  }
205
225
 
226
+ /** Counts describing a provider-valid tool-history repair. */
227
+ export interface ToolHistoryRepairReport {
228
+ /** Earlier duplicates in an immediate result batch; the last result wins. */
229
+ readonly duplicateToolResultsRemoved: number
230
+ /** Results with no immediately preceding owner call. */
231
+ readonly orphanedToolResultsRemoved: number
232
+ /** Error results inserted for calls whose durable outcome is unavailable. */
233
+ readonly syntheticToolResultsInserted: number
234
+ }
235
+
236
+ /** A repaired copy and the exact changes made to it. */
237
+ export interface ToolHistoryRepairResult {
238
+ readonly messages: Message[]
239
+ readonly report: ToolHistoryRepairReport
240
+ }
241
+
242
+ /** Whether a repair changed the provider-bound history. */
243
+ export function toolHistoryRepairChanged(report: ToolHistoryRepairReport): boolean {
244
+ return (
245
+ report.duplicateToolResultsRemoved > 0 ||
246
+ report.orphanedToolResultsRemoved > 0 ||
247
+ report.syntheticToolResultsInserted > 0
248
+ )
249
+ }
250
+
251
+ function validateToolCallIds(messages: readonly Message[]): void {
252
+ const seen = new Set<string>()
253
+ for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
254
+ const message = messages[messageIndex]
255
+ if (message?.role !== CONSTANTS.ASSISTANT_ROLE) continue
256
+ for (let callIndex = 0; callIndex < (message.toolCalls?.length ?? 0); callIndex++) {
257
+ const call = message.toolCalls?.[callIndex]
258
+ if (!call?.id) {
259
+ throw new NamzuError({
260
+ code: 'invalid_config',
261
+ message: `Message history contains an empty tool-call id at messages[${messageIndex}].toolCalls[${callIndex}].`,
262
+ details: { messageIndex, callIndex, toolCallId: call?.id ?? '' },
263
+ })
264
+ }
265
+ if (seen.has(call.id)) {
266
+ throw new NamzuError({
267
+ code: 'invalid_config',
268
+ message: `Message history repeats tool-call id '${call.id}' at messages[${messageIndex}].toolCalls[${callIndex}]; a signed assistant turn cannot be rewritten safely.`,
269
+ details: { messageIndex, callIndex, toolCallId: call.id },
270
+ })
271
+ }
272
+ seen.add(call.id)
273
+ }
274
+ }
275
+ }
276
+
277
+ function unknownOutcomeResult(call: ToolCall, assistant: AssistantMessage): ToolMessage {
278
+ return {
279
+ role: CONSTANTS.TOOL_ROLE,
280
+ toolCallId: call.id,
281
+ isError: true,
282
+ content: `Tool execution was interrupted and no durable result is available for \`${call.function.name}\`. Its outcome is unknown. Retry only if the tool is read-only or idempotent; otherwise verify external state or ask the user before retrying.`,
283
+ ...(assistant.timestamp !== undefined ? { timestamp: assistant.timestamp } : {}),
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Repair provider tool-pairing violations without mutating the durable input.
289
+ *
290
+ * Results are valid only in the contiguous run immediately after their
291
+ * assistant call. Orphaned/displaced results are removed, earlier immediate
292
+ * duplicates are removed in favour of the last result, and every unanswered
293
+ * call receives a conservative error result in call order. Duplicate call ids
294
+ * fail closed: changing ids or tool calls would invalidate opaque native replay
295
+ * state carried by the assistant message.
296
+ */
297
+ export function repairToolMessageHistory(messages: readonly Message[]): ToolHistoryRepairResult {
298
+ validateToolCallIds(messages)
299
+
300
+ const repaired: Message[] = []
301
+ let duplicateToolResultsRemoved = 0
302
+ let orphanedToolResultsRemoved = 0
303
+ let syntheticToolResultsInserted = 0
304
+ let index = 0
305
+
306
+ while (index < messages.length) {
307
+ const message = messages[index]
308
+ if (!message) {
309
+ index++
310
+ continue
311
+ }
312
+
313
+ if (!hasToolCalls(message)) {
314
+ if (isToolMessage(message)) orphanedToolResultsRemoved++
315
+ else repaired.push(message)
316
+ index++
317
+ continue
318
+ }
319
+
320
+ const assistant = message as AssistantMessage
321
+ const calls = assistant.toolCalls ?? []
322
+ const callIds = new Set(calls.map((call) => call.id))
323
+ repaired.push(assistant)
324
+
325
+ let resultEnd = index + 1
326
+ while (resultEnd < messages.length && messages[resultEnd]?.role === CONSTANTS.TOOL_ROLE) {
327
+ resultEnd++
328
+ }
329
+ const lastResultById = new Map<string, number>()
330
+ for (let resultIndex = index + 1; resultIndex < resultEnd; resultIndex++) {
331
+ const result = messages[resultIndex]
332
+ if (result?.role === CONSTANTS.TOOL_ROLE) {
333
+ lastResultById.set(result.toolCallId, resultIndex)
334
+ }
335
+ }
336
+
337
+ const answered = new Set<string>()
338
+ for (let resultIndex = index + 1; resultIndex < resultEnd; resultIndex++) {
339
+ const result = messages[resultIndex]
340
+ if (result?.role !== CONSTANTS.TOOL_ROLE) continue
341
+ if (lastResultById.get(result.toolCallId) !== resultIndex) {
342
+ duplicateToolResultsRemoved++
343
+ continue
344
+ }
345
+ if (!callIds.has(result.toolCallId)) {
346
+ orphanedToolResultsRemoved++
347
+ continue
348
+ }
349
+ repaired.push(result)
350
+ answered.add(result.toolCallId)
351
+ }
352
+
353
+ for (const call of calls) {
354
+ if (answered.has(call.id)) continue
355
+ repaired.push(unknownOutcomeResult(call, assistant))
356
+ syntheticToolResultsInserted++
357
+ }
358
+ index = resultEnd
359
+ }
360
+
361
+ return {
362
+ messages: repaired,
363
+ report: {
364
+ duplicateToolResultsRemoved,
365
+ orphanedToolResultsRemoved,
366
+ syntheticToolResultsInserted,
367
+ },
368
+ }
369
+ }
370
+
206
371
  /**
207
372
  * Finds a safe index for trimming messages while preserving tool call/result atomicity.
208
373
  *
@@ -7,7 +7,11 @@ export type {
7
7
  CompactionStrategy,
8
8
  } from './types.js'
9
9
 
10
- export type { DanglingResult } from './dangling.js'
10
+ export type {
11
+ DanglingResult,
12
+ ToolHistoryRepairReport,
13
+ ToolHistoryRepairResult,
14
+ } from './dangling.js'
11
15
 
12
16
  export type { ConversationManager } from './interface.js'
13
17
 
@@ -31,11 +35,14 @@ export {
31
35
  } from './extractor.js'
32
36
 
33
37
  export { buildVerifiedSummary } from './verifier.js'
38
+ export type { CompactionVerificationOptions } from './verifier.js'
34
39
 
35
40
  export {
36
41
  findDanglingMessages,
37
- removeDanglingMessages,
38
42
  findSafeTrimIndex,
43
+ removeDanglingMessages,
44
+ repairToolMessageHistory,
45
+ toolHistoryRepairChanged,
39
46
  } from './dangling.js'
40
47
 
41
48
  export { findRetainedIndices } from './retention.js'
@@ -1,13 +1,24 @@
1
1
  import type { CompactionConfig } from '../config/runtime.js'
2
+ import { EMPTY_TOKEN_USAGE } from '../constants/limits.js'
3
+ import { resolveStreamIdleTimeoutMs } from '../provider/idle-timeout.js'
4
+ import { type TokenUsage, accumulateTokenUsage } from '../types/common/index.js'
2
5
  import { NamzuError } from '../types/errors/index.js'
6
+ import { toolResultToText } from '../types/message/content.js'
3
7
  import type { Message } from '../types/message/index.js'
4
8
  import type { LLMProvider } from '../types/provider/index.js'
5
9
  import { resolveContextWindow } from './context-window.js'
6
10
  import { findDanglingMessages, findSafeTrimIndex } from './dangling.js'
11
+ import {
12
+ extractFromAssistantMessage,
13
+ extractFromToolCall,
14
+ extractFromToolResult,
15
+ extractFromUserMessage,
16
+ } from './extractor.js'
7
17
  import { WorkingStateManager } from './manager.js'
8
18
  import { planCompaction } from './plan.js'
19
+ import { findRetainedIndices } from './retention.js'
9
20
  import { buildCompactionMessage, isCompactionMessage } from './summary.js'
10
- import { buildVerifiedSummary } from './verifier.js'
21
+ import { type CompactionVerificationOptions, buildVerifiedSummary } from './verifier.js'
11
22
 
12
23
  /**
13
24
  * Compaction a host can ask for, rather than one that only happens to it.
@@ -30,9 +41,17 @@ export interface CompactionResult {
30
41
  readonly shed: number
31
42
  /** The summary that replaced them, as it appears in `messages`. */
32
43
  readonly summary: Message
44
+ /**
45
+ * Tokens spent by this host-triggered pass's optional verifier call.
46
+ *
47
+ * A zero record means no verifier model call ran. The manual path has no
48
+ * `RunManager` to account for side-channel usage, so returning it is the
49
+ * only way a host can include that real provider work in its own ledger.
50
+ */
51
+ readonly usage: TokenUsage
33
52
  }
34
53
 
35
- export interface CompactNowInput {
54
+ export interface CompactNowInput extends CompactionVerificationOptions {
36
55
  readonly messages: readonly Message[]
37
56
  readonly config: CompactionConfig
38
57
  readonly provider: LLMProvider
@@ -40,20 +59,72 @@ export interface CompactNowInput {
40
59
  readonly contextWindowTokens?: number
41
60
  }
42
61
 
62
+ function admitManualCompaction(input: CompactionVerificationOptions): void {
63
+ input.signal?.throwIfAborted()
64
+ // Validate at the public boundary even when the history is too short to
65
+ // call the verifier. A malformed liveness policy is not a successful no-op.
66
+ resolveStreamIdleTimeoutMs(input.streamIdleTimeoutMs)
67
+ }
68
+
43
69
  /** Assembles the new history from a plan's partition plus its summary. */
70
+ function preservedSystemFloor(systemMessages: readonly Message[]): Message[] {
71
+ // A replaceable in-run summary may be superseded. A pinned summary may not:
72
+ // `retain` is the public promise that compaction leaves the message alone,
73
+ // and a prior manual summary is opaque state rather than redundant prose.
74
+ return systemMessages.filter(
75
+ (m) =>
76
+ !isCompactionMessage(typeof m.content === 'string' ? m.content : null) || m.retain === true,
77
+ )
78
+ }
79
+
44
80
  function splice(
45
- systemMessages: readonly Message[],
81
+ preservedSystem: readonly Message[],
46
82
  summaryBody: string,
83
+ retainedOlder: readonly Message[],
47
84
  recentMessages: readonly Message[],
48
85
  ): { messages: Message[]; summary: Message } {
49
- const summary = buildCompactionMessage(summaryBody)
50
- // Any PRIOR summary is dropped: the state a summary is built from is
51
- // cumulative, so a new one supersedes it. Without this the never-trimmed
52
- // floor accumulates one redundant block per pass, unbounded.
53
- const preserved = systemMessages.filter(
54
- (m) => !isCompactionMessage(typeof m.content === 'string' ? m.content : null),
55
- )
56
- return { messages: [...preserved, summary, ...recentMessages], summary }
86
+ // A host-triggered pass has no run-scoped WorkingStateManager to carry the
87
+ // summary into a later query. Pin this summary itself: until a future run
88
+ // has rebuilt equivalent state, this message is the only surviving record
89
+ // of what the pass removed.
90
+ const summary = { ...buildCompactionMessage(summaryBody), retain: true }
91
+ return { messages: [...preservedSystem, summary, ...retainedOlder, ...recentMessages], summary }
92
+ }
93
+
94
+ /** Build the state a host-triggered pass is about to replace. */
95
+ function populateWorkingState(
96
+ manager: WorkingStateManager,
97
+ messages: readonly Message[],
98
+ config: CompactionConfig,
99
+ ): void {
100
+ let firstUser = true
101
+ const toolNames = new Map<string, string>()
102
+
103
+ for (const message of messages) {
104
+ switch (message.role) {
105
+ case 'user':
106
+ extractFromUserMessage(manager, message.content, firstUser)
107
+ firstUser = false
108
+ break
109
+ case 'assistant':
110
+ if (message.content) extractFromAssistantMessage(manager, message.content, config)
111
+ for (const call of message.toolCalls ?? []) {
112
+ toolNames.set(call.id, call.function.name)
113
+ extractFromToolCall(manager, call.function.name, call.function.arguments)
114
+ }
115
+ break
116
+ case 'tool':
117
+ extractFromToolResult(
118
+ manager,
119
+ toolNames.get(message.toolCallId) ?? 'tool',
120
+ toolResultToText(message.content),
121
+ message.isError === true,
122
+ )
123
+ break
124
+ case 'system':
125
+ break
126
+ }
127
+ }
57
128
  }
58
129
 
59
130
  /**
@@ -65,6 +136,7 @@ function splice(
65
136
  * logged as a successful pass and shown to a user as work done.
66
137
  */
67
138
  export async function compactNow(input: CompactNowInput): Promise<CompactionResult | null> {
139
+ admitManualCompaction(input)
68
140
  const window = resolveContextWindow(
69
141
  input.contextWindowTokens ?? input.config.contextWindowTokens,
70
142
  input.model,
@@ -79,22 +151,40 @@ export async function compactNow(input: CompactNowInput): Promise<CompactionResu
79
151
  contextWindowTokens: window.tokens,
80
152
  estimatedTokens: window.tokens,
81
153
  force: true,
154
+ allowNoSystemFloor: true,
82
155
  skipToolResultClear: true,
83
156
  })
84
157
  if (plan.kind !== 'plan') return null
85
158
 
159
+ const retained = findRetainedIndices(input.messages)
160
+ const retainedOlder = plan.olderMessages.filter((_, offset) =>
161
+ retained.has(plan.systemMessages.length + offset),
162
+ )
163
+ const preservedSystem = preservedSystemFloor(plan.systemMessages)
164
+ const projectedLength =
165
+ preservedSystem.length + 1 + retainedOlder.length + plan.recentMessages.length
166
+ // Decide before building a summary (and before a possible verifier call).
167
+ // A pinned older message remains verbatim, so replacing one removable
168
+ // message with one summary has shed nothing and must be reported as a no-op.
169
+ if (input.messages.length - projectedLength < 1) return null
170
+
86
171
  const manager = new WorkingStateManager(input.config)
172
+ populateWorkingState(manager, plan.olderMessages, input.config)
173
+ let usage: TokenUsage = { ...EMPTY_TOKEN_USAGE }
87
174
  const body = await buildVerifiedSummary(
88
175
  manager,
89
176
  [...plan.olderMessages],
90
177
  input.provider,
91
178
  input.config,
92
- undefined,
179
+ (reported) => {
180
+ usage = accumulateTokenUsage(usage, reported)
181
+ },
93
182
  input.model ?? '',
183
+ { signal: input.signal, streamIdleTimeoutMs: input.streamIdleTimeoutMs },
94
184
  )
95
185
 
96
- const { messages, summary } = splice(plan.systemMessages, body, plan.recentMessages)
97
- return { messages, shed: input.messages.length - messages.length, summary }
186
+ const { messages, summary } = splice(preservedSystem, body, retainedOlder, plan.recentMessages)
187
+ return { messages, shed: input.messages.length - messages.length, summary, usage }
98
188
  }
99
189
 
100
190
  export interface CompactRegionInput extends CompactNowInput {
@@ -115,6 +205,7 @@ export interface CompactRegionInput extends CompactNowInput {
115
205
  * the caller can move it themselves.
116
206
  */
117
207
  export async function compactRegion(input: CompactRegionInput): Promise<CompactionResult | null> {
208
+ admitManualCompaction(input)
118
209
  const { messages, start, end } = input
119
210
 
120
211
  if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end > messages.length) {
@@ -146,18 +237,34 @@ export async function compactRegion(input: CompactRegionInput): Promise<Compacti
146
237
  }
147
238
  }
148
239
 
240
+ const retained = findRetainedIndices(messages)
241
+ const retainedSelected = messages
242
+ .slice(start, end)
243
+ .filter((_, offset) => retained.has(start + offset))
244
+ // The replacement summary itself costs one slot. If the selected region
245
+ // contains no additional removable message, the honest result is a no-op
246
+ // and no verifier/provider work is authorized.
247
+ if (end - start - retainedSelected.length - 1 < 1) return null
248
+
149
249
  const manager = new WorkingStateManager(input.config)
250
+ populateWorkingState(manager, messages.slice(start, end), input.config)
251
+ let usage: TokenUsage = { ...EMPTY_TOKEN_USAGE }
150
252
  const body = await buildVerifiedSummary(
151
253
  manager,
152
254
  messages.slice(start, end),
153
255
  input.provider,
154
256
  input.config,
155
- undefined,
257
+ (reported) => {
258
+ usage = accumulateTokenUsage(usage, reported)
259
+ },
156
260
  input.model ?? '',
261
+ { signal: input.signal, streamIdleTimeoutMs: input.streamIdleTimeoutMs },
157
262
  )
158
263
 
159
- const summary = buildCompactionMessage(body)
160
- const out = [...messages.slice(0, start), summary, ...messages.slice(end)]
264
+ // Same cross-run ownership as compactNow: a selected region has been
265
+ // replaced outside a run, so its only surviving account is pinned.
266
+ const summary = { ...buildCompactionMessage(body), retain: true }
267
+ const out = [...messages.slice(0, start), summary, ...retainedSelected, ...messages.slice(end)]
161
268
 
162
269
  // Checked after the splice, not only before it. The edges being
163
270
  // individually safe does not make the RESULT valid — a span whose
@@ -173,5 +280,5 @@ export async function compactRegion(input: CompactRegionInput): Promise<Compacti
173
280
  })
174
281
  }
175
282
 
176
- return { messages: out, shed: messages.length - out.length, summary }
283
+ return { messages: out, shed: messages.length - out.length, summary, usage }
177
284
  }
@@ -30,7 +30,7 @@ import { clearStaleToolResults } from './tool-result-editing.js'
30
30
  export type CompactionSkipReason =
31
31
  /** Fewer messages than the recent window plus a floor — nothing to move. */
32
32
  | 'too_few_messages'
33
- /** No leading `system` message, so there is no permanent floor to keep. */
33
+ /** An in-run pass has no leading `system` message to use as its permanent floor. */
34
34
  | 'no_system_floor'
35
35
  /** Every candidate boundary would split a tool-call pair. */
36
36
  | 'no_safe_cut'
@@ -61,14 +61,27 @@ export interface CompactionPlanInput {
61
61
  readonly contextWindowTokens: number
62
62
  readonly estimatedTokens: number
63
63
  readonly force?: boolean
64
+ /**
65
+ * Let a host-created pass establish its own retained summary floor.
66
+ *
67
+ * This is deliberately separate from `force`: a provider overflow can
68
+ * force the threshold decision without changing the live run's prompt
69
+ * invariant, while a host may own a durable user/assistant-only history
70
+ * that has no system floor yet.
71
+ */
72
+ readonly allowNoSystemFloor?: boolean
64
73
  /**
65
74
  * Skip the tool-result pre-pass and go straight to the boundary search.
66
75
  *
67
- * The two steps are sequential in a real pass: clear first, install the
68
- * result, and if that was not enough relief, plan a cut over the history
69
- * you just edited. Returning both from one call would mean nesting a
70
- * union inside a union and computing a boundary the caller may never
71
- * use; asking twice says what is happening.
76
+ * The two steps are sequential in a real pass: ask once for a cleared
77
+ * candidate and, if that was not enough relief, ask again for a cut over
78
+ * that candidate. The caller need not install the first answer before the
79
+ * second question. In fact the live runtime deliberately stages an
80
+ * insufficient clear until summary verification succeeds, then publishes
81
+ * the combined edit atomically. Returning both from one call would mean
82
+ * nesting a union inside a union and computing a boundary the caller may
83
+ * never use; asking twice says what is happening without prescribing when
84
+ * an effect becomes visible.
72
85
  */
73
86
  readonly skipToolResultClear?: boolean
74
87
  }
@@ -158,16 +171,34 @@ export function planCompaction(input: CompactionPlanInput): CompactionPlan {
158
171
  }
159
172
 
160
173
  const messages = input.messages
161
- if (messages.length < config.keepRecentMessages + 2) {
162
- return { kind: 'skip', reason: 'too_few_messages' }
163
- }
164
-
165
174
  const systemMessages: Message[] = []
166
175
  for (const msg of messages) {
167
176
  if (msg.role !== 'system') break
168
177
  systemMessages.push(msg)
169
178
  }
170
- if (systemMessages.length === 0) return { kind: 'skip', reason: 'no_system_floor' }
179
+
180
+ // Count and token retention are different boundary policies. In count
181
+ // mode the configured tail plus one older message must fit after the
182
+ // floor. Token mode does not consult keepRecentMessages at all; it only
183
+ // needs one older and one recent message before the token walk and safe
184
+ // boundary search can make the real decision.
185
+ //
186
+ // Preserve the old short-history ordering for a run with no floor: until
187
+ // a host explicitly allows a floorless pass, one notional system message
188
+ // remains part of the admission minimum and `no_system_floor` follows it.
189
+ const floorForAdmission =
190
+ systemMessages.length > 0 ? systemMessages.length : input.allowNoSystemFloor ? 0 : 1
191
+ const minimumMessages =
192
+ config.keepRecentTokens === undefined
193
+ ? floorForAdmission + config.keepRecentMessages + 1
194
+ : floorForAdmission + 2
195
+ if (messages.length < minimumMessages) {
196
+ return { kind: 'skip', reason: 'too_few_messages' }
197
+ }
198
+
199
+ if (systemMessages.length === 0 && !input.allowNoSystemFloor) {
200
+ return { kind: 'skip', reason: 'no_system_floor' }
201
+ }
171
202
 
172
203
  // Tool-pair atomicity. A naive cut at `length - keepRecentMessages` can
173
204
  // land BETWEEN an assistant-with-toolCalls (dropped into `older`) and its
@@ -24,15 +24,18 @@ const isAssistantWithCalls = (m: Message): m is AssistantMessage =>
24
24
 
25
25
  /**
26
26
  * Indices of every message protected by a `retain` marker, expanded across
27
- * tool pairs.
27
+ * provider-valid turns.
28
28
  *
29
29
  * Transitive by necessity, not politeness. A `tool_result` whose
30
- * `tool_use` was dropped is rejected by the provider outright, so pinning
31
- * half a pair would turn a retention request into a broken next turn. So:
32
- * a pinned tool result pulls in the assistant turn that issued the call,
33
- * and a pinned assistant turn pulls in EVERY result answering it the
34
- * second hop matters, because an assistant turn with three calls and one
35
- * surviving result is the same dangling error in the other direction.
30
+ * `tool_use` was dropped is rejected by the provider outright, and a
31
+ * compacted conversation whose first non-system message is an assistant
32
+ * turn is rejected before the pair is even considered. Pinning half a pair
33
+ * or the pair without its user turn boundary would therefore turn a
34
+ * retention request into a broken next turn. A pinned tool result pulls in
35
+ * the assistant turn that issued the call; a pinned assistant turn pulls in
36
+ * the user message that opened its turn and EVERY result answering it. The
37
+ * second result hop matters, because an assistant turn with three calls and
38
+ * one surviving result is the same dangling error in the other direction.
36
39
  */
37
40
  export function findRetainedIndices(messages: readonly Message[]): Set<number> {
38
41
  const retained = new Set<number>()
@@ -43,10 +46,19 @@ export function findRetainedIndices(messages: readonly Message[]): Set<number> {
43
46
 
44
47
  const assistantOfCall = new Map<string, number>()
45
48
  const resultsOfAssistant = new Map<number, number[]>()
49
+ const userOfAssistant = new Map<number, number>()
46
50
 
51
+ let currentUser: number | undefined
47
52
  for (let i = 0; i < messages.length; i++) {
48
53
  const message = messages[i]
49
- if (!message || !isAssistantWithCalls(message)) continue
54
+ if (!message) continue
55
+ if (message.role === 'user') {
56
+ currentUser = i
57
+ continue
58
+ }
59
+ if (message.role !== 'assistant') continue
60
+ if (currentUser !== undefined) userOfAssistant.set(i, currentUser)
61
+ if (!isAssistantWithCalls(message)) continue
50
62
  for (const call of message.toolCalls ?? []) {
51
63
  assistantOfCall.set(call.id, i)
52
64
  }
@@ -80,6 +92,9 @@ export function findRetainedIndices(messages: readonly Message[]): Set<number> {
80
92
  if (message.role === 'tool') {
81
93
  add(assistantOfCall.get((message as ToolMessage).toolCallId))
82
94
  }
95
+ if (message.role === 'assistant') {
96
+ add(userOfAssistant.get(index))
97
+ }
83
98
  for (const sibling of resultsOfAssistant.get(index) ?? []) {
84
99
  add(sibling)
85
100
  }