@namzu/sdk 1.0.0 → 1.2.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 (330) hide show
  1. package/CHANGELOG.md +253 -0
  2. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  3. package/dist/agents/ReactiveAgent.js +5 -0
  4. package/dist/agents/ReactiveAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts +22 -1
  6. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  7. package/dist/agents/SupervisorAgent.js +47 -14
  8. package/dist/agents/SupervisorAgent.js.map +1 -1
  9. package/dist/agents/__tests__/supervisor-ledger.test.d.ts +2 -0
  10. package/dist/agents/__tests__/supervisor-ledger.test.d.ts.map +1 -0
  11. package/dist/agents/__tests__/supervisor-ledger.test.js +131 -0
  12. package/dist/agents/__tests__/supervisor-ledger.test.js.map +1 -0
  13. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  14. package/dist/bridge/a2a/mapper.js +2 -0
  15. package/dist/bridge/a2a/mapper.js.map +1 -1
  16. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  17. package/dist/bridge/sse/mapper.js +4 -0
  18. package/dist/bridge/sse/mapper.js.map +1 -1
  19. package/dist/config/runtime.d.ts +24 -0
  20. package/dist/config/runtime.d.ts.map +1 -1
  21. package/dist/config/runtime.js +9 -0
  22. package/dist/config/runtime.js.map +1 -1
  23. package/dist/connector/builtins/webhook.test.js +17 -0
  24. package/dist/connector/builtins/webhook.test.js.map +1 -1
  25. package/dist/connector/index.d.ts +1 -0
  26. package/dist/connector/index.d.ts.map +1 -1
  27. package/dist/connector/index.js +1 -0
  28. package/dist/connector/index.js.map +1 -1
  29. package/dist/connector/mcp/client.d.ts.map +1 -1
  30. package/dist/connector/mcp/client.js +4 -0
  31. package/dist/connector/mcp/client.js.map +1 -1
  32. package/dist/connector/mcp/index.d.ts +1 -0
  33. package/dist/connector/mcp/index.d.ts.map +1 -1
  34. package/dist/connector/mcp/index.js +1 -0
  35. package/dist/connector/mcp/index.js.map +1 -1
  36. package/dist/connector/mcp/streamable-http.d.ts +22 -0
  37. package/dist/connector/mcp/streamable-http.d.ts.map +1 -0
  38. package/dist/connector/mcp/streamable-http.js +128 -0
  39. package/dist/connector/mcp/streamable-http.js.map +1 -0
  40. package/dist/connector/mcp/streamable-http.test.d.ts +2 -0
  41. package/dist/connector/mcp/streamable-http.test.d.ts.map +1 -0
  42. package/dist/connector/mcp/streamable-http.test.js +132 -0
  43. package/dist/connector/mcp/streamable-http.test.js.map +1 -0
  44. package/dist/{runtime/query → constants}/continuation.d.ts +5 -0
  45. package/dist/constants/continuation.d.ts.map +1 -0
  46. package/dist/{runtime/query → constants}/continuation.js +5 -0
  47. package/dist/constants/continuation.js.map +1 -0
  48. package/dist/constants/index.d.ts +1 -0
  49. package/dist/constants/index.d.ts.map +1 -1
  50. package/dist/constants/index.js +1 -0
  51. package/dist/constants/index.js.map +1 -1
  52. package/dist/manager/run/persistence.d.ts +11 -0
  53. package/dist/manager/run/persistence.d.ts.map +1 -1
  54. package/dist/manager/run/persistence.js +30 -1
  55. package/dist/manager/run/persistence.js.map +1 -1
  56. package/dist/persona/assembler.d.ts.map +1 -1
  57. package/dist/persona/assembler.js +18 -9
  58. package/dist/persona/assembler.js.map +1 -1
  59. package/dist/provider/capabilities.d.ts +27 -0
  60. package/dist/provider/capabilities.d.ts.map +1 -0
  61. package/dist/provider/capabilities.js +29 -0
  62. package/dist/provider/capabilities.js.map +1 -0
  63. package/dist/provider/collect.d.ts.map +1 -1
  64. package/dist/provider/collect.js +4 -1
  65. package/dist/provider/collect.js.map +1 -1
  66. package/dist/provider/index.d.ts +2 -0
  67. package/dist/provider/index.d.ts.map +1 -1
  68. package/dist/provider/index.js +1 -0
  69. package/dist/provider/index.js.map +1 -1
  70. package/dist/public-runtime.d.ts +4 -3
  71. package/dist/public-runtime.d.ts.map +1 -1
  72. package/dist/public-runtime.js +3 -3
  73. package/dist/public-runtime.js.map +1 -1
  74. package/dist/registry/tool/execute.d.ts +27 -0
  75. package/dist/registry/tool/execute.d.ts.map +1 -1
  76. package/dist/registry/tool/execute.js +124 -21
  77. package/dist/registry/tool/execute.js.map +1 -1
  78. package/dist/registry/tool/execute.test.d.ts +9 -6
  79. package/dist/registry/tool/execute.test.d.ts.map +1 -1
  80. package/dist/registry/tool/execute.test.js +72 -10
  81. package/dist/registry/tool/execute.test.js.map +1 -1
  82. package/dist/registry/toolset/catalog.test.js +62 -0
  83. package/dist/registry/toolset/catalog.test.js.map +1 -1
  84. package/dist/run/reporter.d.ts.map +1 -1
  85. package/dist/run/reporter.js +8 -0
  86. package/dist/run/reporter.js.map +1 -1
  87. package/dist/runtime/query/__tests__/capability-negotiation.test.d.ts +2 -0
  88. package/dist/runtime/query/__tests__/capability-negotiation.test.d.ts.map +1 -0
  89. package/dist/runtime/query/__tests__/capability-negotiation.test.js +184 -0
  90. package/dist/runtime/query/__tests__/capability-negotiation.test.js.map +1 -0
  91. package/dist/runtime/query/__tests__/checkpoint-store.test.d.ts +2 -0
  92. package/dist/runtime/query/__tests__/checkpoint-store.test.d.ts.map +1 -0
  93. package/dist/runtime/query/__tests__/checkpoint-store.test.js +259 -0
  94. package/dist/runtime/query/__tests__/checkpoint-store.test.js.map +1 -0
  95. package/dist/runtime/query/__tests__/checkpoint.test.js +9 -3
  96. package/dist/runtime/query/__tests__/checkpoint.test.js.map +1 -1
  97. package/dist/runtime/query/__tests__/deferred-tools.test.js +43 -5
  98. package/dist/runtime/query/__tests__/deferred-tools.test.js.map +1 -1
  99. package/dist/runtime/query/__tests__/long-document-flow.test.d.ts +2 -0
  100. package/dist/runtime/query/__tests__/long-document-flow.test.d.ts.map +1 -0
  101. package/dist/runtime/query/__tests__/long-document-flow.test.js +154 -0
  102. package/dist/runtime/query/__tests__/long-document-flow.test.js.map +1 -0
  103. package/dist/runtime/query/__tests__/orphan-task-turn-end.test.d.ts +2 -0
  104. package/dist/runtime/query/__tests__/orphan-task-turn-end.test.d.ts.map +1 -0
  105. package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js +110 -0
  106. package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js.map +1 -0
  107. package/dist/runtime/query/__tests__/prompt.test.js +15 -11
  108. package/dist/runtime/query/__tests__/prompt.test.js.map +1 -1
  109. package/dist/runtime/query/checkpoint.d.ts +16 -2
  110. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  111. package/dist/runtime/query/checkpoint.js +21 -7
  112. package/dist/runtime/query/checkpoint.js.map +1 -1
  113. package/dist/runtime/query/context.d.ts +7 -0
  114. package/dist/runtime/query/context.d.ts.map +1 -1
  115. package/dist/runtime/query/context.js +1 -0
  116. package/dist/runtime/query/context.js.map +1 -1
  117. package/dist/runtime/query/index.d.ts +29 -0
  118. package/dist/runtime/query/index.d.ts.map +1 -1
  119. package/dist/runtime/query/index.js +98 -8
  120. package/dist/runtime/query/index.js.map +1 -1
  121. package/dist/runtime/query/iteration/index.d.ts +3 -55
  122. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  123. package/dist/runtime/query/iteration/index.js +260 -656
  124. package/dist/runtime/query/iteration/index.js.map +1 -1
  125. package/dist/runtime/query/iteration/phases/checkpoint.d.ts.map +1 -1
  126. package/dist/runtime/query/iteration/phases/checkpoint.js +24 -2
  127. package/dist/runtime/query/iteration/phases/checkpoint.js.map +1 -1
  128. package/dist/runtime/query/iteration/phases/compaction.d.ts +8 -0
  129. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  130. package/dist/runtime/query/iteration/phases/compaction.js +84 -3
  131. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  132. package/dist/runtime/query/iteration/phases/context.d.ts +13 -2
  133. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  134. package/dist/runtime/query/iteration/phases/context.js +53 -0
  135. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  136. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts +2 -0
  137. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts.map +1 -0
  138. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js +73 -0
  139. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js.map +1 -0
  140. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts +2 -0
  141. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts.map +1 -0
  142. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js +120 -0
  143. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js.map +1 -0
  144. package/dist/runtime/query/iteration/phases/tool-review.d.ts +1 -1
  145. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  146. package/dist/runtime/query/iteration/phases/tool-review.js +7 -2
  147. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  148. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts +19 -0
  149. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts.map +1 -0
  150. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js +177 -0
  151. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js.map +1 -0
  152. package/dist/runtime/query/iteration/phases/working-memory.d.ts +31 -0
  153. package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -0
  154. package/dist/runtime/query/iteration/phases/working-memory.js +81 -0
  155. package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -0
  156. package/dist/runtime/query/iteration/stream-turn.d.ts +35 -0
  157. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -0
  158. package/dist/runtime/query/iteration/stream-turn.js +341 -0
  159. package/dist/runtime/query/iteration/stream-turn.js.map +1 -0
  160. package/dist/runtime/query/replay/list.d.ts +17 -1
  161. package/dist/runtime/query/replay/list.d.ts.map +1 -1
  162. package/dist/runtime/query/replay/list.js +21 -4
  163. package/dist/runtime/query/replay/list.js.map +1 -1
  164. package/dist/runtime/query/replay/prepare.d.ts +10 -0
  165. package/dist/runtime/query/replay/prepare.d.ts.map +1 -1
  166. package/dist/runtime/query/replay/prepare.js +20 -4
  167. package/dist/runtime/query/replay/prepare.js.map +1 -1
  168. package/dist/session/workspace/__tests__/shared-run.test.js +1 -1
  169. package/dist/session/workspace/__tests__/shared-run.test.js.map +1 -1
  170. package/dist/session/workspace/shared-run.js +1 -0
  171. package/dist/session/workspace/shared-run.js.map +1 -1
  172. package/dist/skills/loader.test.js +8 -8
  173. package/dist/skills/loader.test.js.map +1 -1
  174. package/dist/store/index.d.ts +1 -0
  175. package/dist/store/index.d.ts.map +1 -1
  176. package/dist/store/index.js +1 -0
  177. package/dist/store/index.js.map +1 -1
  178. package/dist/store/run/checkpoint-disk.d.ts +28 -0
  179. package/dist/store/run/checkpoint-disk.d.ts.map +1 -0
  180. package/dist/store/run/checkpoint-disk.js +55 -0
  181. package/dist/store/run/checkpoint-disk.js.map +1 -0
  182. package/dist/streaming/coalesce.test.js.map +1 -1
  183. package/dist/tools/builtins/__tests__/payload-budget.test.js +48 -0
  184. package/dist/tools/builtins/__tests__/payload-budget.test.js.map +1 -1
  185. package/dist/tools/builtins/__tests__/read-file.test.js +9 -0
  186. package/dist/tools/builtins/__tests__/read-file.test.js.map +1 -1
  187. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  188. package/dist/tools/builtins/read-file.js +50 -2
  189. package/dist/tools/builtins/read-file.js.map +1 -1
  190. package/dist/tools/builtins/search-tools.d.ts.map +1 -1
  191. package/dist/tools/builtins/search-tools.js +32 -7
  192. package/dist/tools/builtins/search-tools.js.map +1 -1
  193. package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts +2 -0
  194. package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts.map +1 -0
  195. package/dist/tools/coordinator/__tests__/approve-plan.test.js +114 -0
  196. package/dist/tools/coordinator/__tests__/approve-plan.test.js.map +1 -0
  197. package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts +2 -0
  198. package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts.map +1 -0
  199. package/dist/tools/coordinator/__tests__/ask-user-question.test.js +309 -0
  200. package/dist/tools/coordinator/__tests__/ask-user-question.test.js.map +1 -0
  201. package/dist/tools/coordinator/index.d.ts +8 -0
  202. package/dist/tools/coordinator/index.d.ts.map +1 -1
  203. package/dist/tools/coordinator/index.js +195 -15
  204. package/dist/tools/coordinator/index.js.map +1 -1
  205. package/dist/types/agent/index.d.ts +1 -0
  206. package/dist/types/agent/index.d.ts.map +1 -1
  207. package/dist/types/agent/index.js +1 -0
  208. package/dist/types/agent/index.js.map +1 -1
  209. package/dist/types/agent/reactive.d.ts +15 -0
  210. package/dist/types/agent/reactive.d.ts.map +1 -1
  211. package/dist/types/agent/supervisor.d.ts +18 -0
  212. package/dist/types/agent/supervisor.d.ts.map +1 -1
  213. package/dist/types/agent/working-memory.d.ts +29 -0
  214. package/dist/types/agent/working-memory.d.ts.map +1 -0
  215. package/dist/types/agent/working-memory.js +2 -0
  216. package/dist/types/agent/working-memory.js.map +1 -0
  217. package/dist/types/common/index.d.ts +10 -0
  218. package/dist/types/common/index.d.ts.map +1 -1
  219. package/dist/types/common/index.js +18 -0
  220. package/dist/types/common/index.js.map +1 -1
  221. package/dist/types/common/usage-merge.test.d.ts +2 -0
  222. package/dist/types/common/usage-merge.test.d.ts.map +1 -0
  223. package/dist/types/common/usage-merge.test.js +48 -0
  224. package/dist/types/common/usage-merge.test.js.map +1 -0
  225. package/dist/types/connector/mcp.d.ts +9 -2
  226. package/dist/types/connector/mcp.d.ts.map +1 -1
  227. package/dist/types/hitl/index.d.ts +42 -0
  228. package/dist/types/hitl/index.d.ts.map +1 -1
  229. package/dist/types/hitl/index.js +11 -0
  230. package/dist/types/hitl/index.js.map +1 -1
  231. package/dist/types/provider/chat.d.ts +9 -0
  232. package/dist/types/provider/chat.d.ts.map +1 -1
  233. package/dist/types/provider/config.d.ts +18 -0
  234. package/dist/types/provider/config.d.ts.map +1 -1
  235. package/dist/types/provider/interface.d.ts +10 -0
  236. package/dist/types/provider/interface.d.ts.map +1 -1
  237. package/dist/types/run/checkpoint-store.d.ts +64 -0
  238. package/dist/types/run/checkpoint-store.d.ts.map +1 -0
  239. package/dist/types/run/checkpoint-store.js +12 -0
  240. package/dist/types/run/checkpoint-store.js.map +1 -0
  241. package/dist/types/run/config.d.ts +27 -0
  242. package/dist/types/run/config.d.ts.map +1 -1
  243. package/dist/types/run/events.d.ts +6 -0
  244. package/dist/types/run/events.d.ts.map +1 -1
  245. package/dist/types/run/events.js.map +1 -1
  246. package/dist/types/run/index.d.ts +1 -0
  247. package/dist/types/run/index.d.ts.map +1 -1
  248. package/dist/types/run/index.js +1 -0
  249. package/dist/types/run/index.js.map +1 -1
  250. package/dist/version.d.ts.map +1 -1
  251. package/dist/version.js +25 -4
  252. package/dist/version.js.map +1 -1
  253. package/package.json +1 -1
  254. package/src/agents/ReactiveAgent.ts +5 -0
  255. package/src/agents/SupervisorAgent.ts +56 -16
  256. package/src/agents/__tests__/supervisor-ledger.test.ts +185 -0
  257. package/src/bridge/a2a/mapper.ts +3 -0
  258. package/src/bridge/sse/mapper.ts +5 -0
  259. package/src/config/runtime.ts +9 -0
  260. package/src/connector/builtins/webhook.test.ts +21 -0
  261. package/src/connector/index.ts +1 -0
  262. package/src/connector/mcp/client.ts +4 -0
  263. package/src/connector/mcp/index.ts +1 -0
  264. package/src/connector/mcp/streamable-http.test.ts +169 -0
  265. package/src/connector/mcp/streamable-http.ts +152 -0
  266. package/src/{runtime/query → constants}/continuation.ts +5 -0
  267. package/src/constants/index.ts +1 -0
  268. package/src/manager/run/persistence.ts +34 -1
  269. package/src/persona/assembler.ts +18 -6
  270. package/src/provider/capabilities.ts +47 -0
  271. package/src/provider/collect.ts +4 -1
  272. package/src/provider/index.ts +5 -0
  273. package/src/public-runtime.ts +5 -0
  274. package/src/registry/tool/execute.test.ts +85 -10
  275. package/src/registry/tool/execute.ts +124 -21
  276. package/src/registry/toolset/catalog.test.ts +68 -0
  277. package/src/run/reporter.ts +9 -0
  278. package/src/runtime/query/__tests__/capability-negotiation.test.ts +248 -0
  279. package/src/runtime/query/__tests__/checkpoint-store.test.ts +322 -0
  280. package/src/runtime/query/__tests__/checkpoint.test.ts +15 -6
  281. package/src/runtime/query/__tests__/deferred-tools.test.ts +52 -5
  282. package/src/runtime/query/__tests__/long-document-flow.test.ts +180 -0
  283. package/src/runtime/query/__tests__/orphan-task-turn-end.test.ts +127 -0
  284. package/src/runtime/query/__tests__/prompt.test.ts +15 -11
  285. package/src/runtime/query/checkpoint.ts +23 -9
  286. package/src/runtime/query/context.ts +9 -0
  287. package/src/runtime/query/index.ts +148 -29
  288. package/src/runtime/query/iteration/index.ts +316 -831
  289. package/src/runtime/query/iteration/phases/checkpoint.ts +32 -2
  290. package/src/runtime/query/iteration/phases/compaction.ts +89 -3
  291. package/src/runtime/query/iteration/phases/context.ts +59 -3
  292. package/src/runtime/query/iteration/phases/hitl-abort-race.test.ts +86 -0
  293. package/src/runtime/query/iteration/phases/hitl-answer-question.test.ts +139 -0
  294. package/src/runtime/query/iteration/phases/tool-review.ts +7 -3
  295. package/src/runtime/query/iteration/phases/working-memory-compaction.test.ts +215 -0
  296. package/src/runtime/query/iteration/phases/working-memory.ts +86 -0
  297. package/src/runtime/query/iteration/stream-turn.ts +396 -0
  298. package/src/runtime/query/replay/list.ts +38 -4
  299. package/src/runtime/query/replay/prepare.ts +37 -4
  300. package/src/session/workspace/__tests__/shared-run.test.ts +1 -1
  301. package/src/session/workspace/shared-run.ts +1 -0
  302. package/src/skills/loader.test.ts +8 -8
  303. package/src/store/index.ts +1 -0
  304. package/src/store/run/checkpoint-disk.ts +67 -0
  305. package/src/streaming/coalesce.test.ts +3 -3
  306. package/src/tools/builtins/__tests__/payload-budget.test.ts +63 -0
  307. package/src/tools/builtins/__tests__/read-file.test.ts +12 -0
  308. package/src/tools/builtins/read-file.ts +50 -2
  309. package/src/tools/builtins/search-tools.ts +38 -7
  310. package/src/tools/coordinator/__tests__/approve-plan.test.ts +139 -0
  311. package/src/tools/coordinator/__tests__/ask-user-question.test.ts +370 -0
  312. package/src/tools/coordinator/index.ts +227 -17
  313. package/src/types/agent/index.ts +1 -0
  314. package/src/types/agent/reactive.ts +17 -0
  315. package/src/types/agent/supervisor.ts +20 -0
  316. package/src/types/agent/working-memory.ts +29 -0
  317. package/src/types/common/index.ts +19 -0
  318. package/src/types/common/usage-merge.test.ts +53 -0
  319. package/src/types/connector/mcp.ts +14 -2
  320. package/src/types/hitl/index.ts +52 -1
  321. package/src/types/provider/chat.ts +10 -0
  322. package/src/types/provider/config.ts +18 -0
  323. package/src/types/provider/interface.ts +11 -0
  324. package/src/types/run/checkpoint-store.ts +72 -0
  325. package/src/types/run/config.ts +30 -0
  326. package/src/types/run/events.ts +13 -0
  327. package/src/types/run/index.ts +1 -0
  328. package/src/version.ts +26 -4
  329. package/dist/runtime/query/continuation.d.ts.map +0 -1
  330. package/dist/runtime/query/continuation.js.map +0 -1
@@ -0,0 +1,127 @@
1
+ import { rm } from 'node:fs/promises'
2
+ import { mkdtemp } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import { afterEach, describe, expect, it } from 'vitest'
6
+
7
+ import { ToolRegistry } from '../../../registry/tool/execute.js'
8
+ import type { TaskGateway, TaskHandle } from '../../../types/agent/gateway.js'
9
+ import type { SessionId, TaskId, TenantId } from '../../../types/ids/index.js'
10
+ import { createUserMessage } from '../../../types/message/index.js'
11
+ import type { LLMProvider, StreamChunk } from '../../../types/provider/index.js'
12
+ import type { RunEvent } from '../../../types/run/index.js'
13
+ import type { ProjectId, ThreadId } from '../../../types/session/ids.js'
14
+ import { drainQuery } from '../index.js'
15
+
16
+ const ZERO_USAGE = {
17
+ promptTokens: 0,
18
+ completionTokens: 0,
19
+ totalTokens: 0,
20
+ cachedTokens: 0,
21
+ cacheWriteTokens: 0,
22
+ }
23
+
24
+ /** Ends its turn with plain text on the first (and only) call. */
25
+ class SingleTurnProvider implements LLMProvider {
26
+ readonly id = 'single-turn'
27
+ readonly name = 'Single Turn Provider'
28
+ calls = 0
29
+
30
+ async *chatStream(): AsyncIterable<StreamChunk> {
31
+ this.calls += 1
32
+ yield {
33
+ id: `msg_${this.calls}`,
34
+ delta: { content: 'Final answer.' },
35
+ }
36
+ yield {
37
+ id: `msg_${this.calls}`,
38
+ delta: {},
39
+ finishReason: 'stop',
40
+ usage: ZERO_USAGE,
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Gateway that permanently reports one running task. Every dispatch
47
+ * tool is blocking, so a running task at end-of-turn is an orphan —
48
+ * there is no notification producer that could ever deliver its
49
+ * result (the listener was removed in dc16d58). The run must NOT
50
+ * busy-wait on it.
51
+ */
52
+ function orphanTaskGateway(): TaskGateway {
53
+ const handle: TaskHandle = {
54
+ taskId: 'task_orphan' as TaskId,
55
+ agentId: 'agent_worker',
56
+ state: 'running',
57
+ createdAt: Date.now(),
58
+ }
59
+ return {
60
+ createTask: async () => handle,
61
+ waitForTask: () => new Promise<TaskHandle>(() => {}),
62
+ continueTask: async () => {},
63
+ cancelTask: () => {},
64
+ getTask: () => handle,
65
+ listTasks: () => [handle],
66
+ onTaskCompleted: () => () => {},
67
+ }
68
+ }
69
+
70
+ describe('end of turn with running agent tasks', () => {
71
+ let workdirs: string[] = []
72
+
73
+ afterEach(async () => {
74
+ await Promise.all(workdirs.map((dir) => rm(dir, { recursive: true, force: true })))
75
+ workdirs = []
76
+ })
77
+
78
+ // Regression: the loop used to poll `pendingNotifications` every
79
+ // 250ms for up to `runConfig.timeoutMs` (120s default) whenever the
80
+ // turn ended while the gateway still listed a running task — but
81
+ // nothing has pushed onto that queue since dc16d58 removed the
82
+ // onTaskCompleted producer, so the wait always injected nothing and
83
+ // the run hung for minutes. The vitest per-test timeout is the
84
+ // hang detector here: with the busy-wait present this test times
85
+ // out instead of completing.
86
+ it('ends the run promptly instead of busy-waiting on orphan tasks', async () => {
87
+ const provider = new SingleTurnProvider()
88
+ const workingDirectory = await mkdtemp(join(tmpdir(), 'namzu-orphan-task-'))
89
+ workdirs.push(workingDirectory)
90
+ const events: RunEvent[] = []
91
+
92
+ const run = await drainQuery(
93
+ {
94
+ provider,
95
+ tools: new ToolRegistry(),
96
+ runConfig: {
97
+ model: 'mock-model',
98
+ // Deliberately longer than the vitest timeout: the old
99
+ // code waited min(timeoutMs, …) polling the dead queue.
100
+ timeoutMs: 120_000,
101
+ tokenBudget: 100_000,
102
+ maxIterations: 3,
103
+ maxResponseTokens: 256,
104
+ },
105
+ agentId: 'agent_test',
106
+ agentName: 'Test Agent',
107
+ messages: [createUserMessage('do the thing')],
108
+ workingDirectory,
109
+ taskGateway: orphanTaskGateway(),
110
+ sessionId: 'ses_orphan_task' as SessionId,
111
+ threadId: 'thd_orphan_task' as ThreadId,
112
+ projectId: 'prj_orphan_task' as ProjectId,
113
+ tenantId: 'tnt_orphan_task' as TenantId,
114
+ },
115
+ (event) => {
116
+ events.push(event)
117
+ },
118
+ )
119
+
120
+ expect(run.status).toBe('completed')
121
+ expect(run.stopReason).toBe('end_turn')
122
+ expect(run.result).toBe('Final answer.')
123
+ // One turn only — no futile re-invocation loop on the orphan.
124
+ expect(provider.calls).toBe(1)
125
+ expect(events.some((event) => event.type === 'run_failed')).toBe(false)
126
+ }, 10_000)
127
+ })
@@ -43,18 +43,21 @@ describe('PromptBuilder runtime context', () => {
43
43
  skills: [
44
44
  {
45
45
  metadata: {
46
- name: 'project-documents',
47
- description: 'Draft and edit project documents from grounded inputs.',
46
+ name: 'delivery-briefing',
47
+ description: 'Draft and edit delivery briefings from grounded inputs.',
48
48
  },
49
- dirPath: '/repo/.agents/skills/project-documents',
49
+ dirPath: '/repo/.agents/skills/delivery-briefing',
50
50
  },
51
51
  ],
52
52
  }).build('full', '/tmp/work')
53
53
 
54
54
  expect(prompt).toContain('You are a project assistant.')
55
55
  expect(prompt).toContain('## Available Skills')
56
- expect(prompt).toContain('project-documents')
57
- expect(prompt).toContain('Draft and edit project documents')
56
+ expect(prompt).toContain('<available_skills>')
57
+ expect(prompt).toContain('delivery-briefing')
58
+ expect(prompt).toContain('Draft and edit delivery briefings')
59
+ expect(prompt).toContain('<location>/repo/.agents/skills/delivery-briefing/SKILL.md</location>')
60
+ expect(prompt).toContain('The following block is a manifest')
58
61
  expect(prompt).not.toContain('## Loaded Skills')
59
62
  })
60
63
 
@@ -65,22 +68,23 @@ describe('PromptBuilder runtime context', () => {
65
68
  skills: [
66
69
  {
67
70
  metadata: {
68
- name: 'long-form-files',
69
- description: 'Create long files with bounded edit chunks.',
71
+ name: 'structured-file-authoring',
72
+ description: 'Create structured files with bounded edit chunks.',
70
73
  license: 'MIT',
71
74
  compatibility: 'Requires file tools',
72
75
  allowedTools: 'read write edit',
73
76
  },
74
77
  body: 'Use skeleton-first writes and bounded edit chunks.',
75
- dirPath: '/repo/.agents/skills/long-form-files',
78
+ dirPath: '/repo/.agents/skills/structured-file-authoring',
76
79
  },
77
80
  ],
78
81
  }).build('full', '/tmp/work')
79
82
 
80
83
  expect(prompt).toContain('## Available Skills')
81
- expect(prompt).toContain('license: MIT')
82
- expect(prompt).toContain('compatibility: Requires file tools')
83
- expect(prompt).toContain('allowed-tools: read write edit')
84
+ expect(prompt).toContain('<license>MIT</license>')
85
+ expect(prompt).toContain('<compatibility>Requires file tools</compatibility>')
86
+ expect(prompt).toContain('<allowed_tools>read write edit</allowed_tools>')
87
+ expect(prompt).toContain('read the SKILL.md at its <location> before writing code')
84
88
  expect(prompt).toContain('## Loaded Skills')
85
89
  expect(prompt).toContain('Use skeleton-first writes')
86
90
  })
@@ -1,5 +1,4 @@
1
1
  import type { RunPersistence } from '../../manager/run/persistence.js'
2
- import type { RunDiskStore } from '../../store/run/disk.js'
3
2
  import type {
4
3
  ActiveNodeInfo,
5
4
  BranchStackEntry,
@@ -8,13 +7,19 @@ import type {
8
7
  IterationCheckpoint,
9
8
  } from '../../types/hitl/index.js'
10
9
  import type { AssistantMessage } from '../../types/message/index.js'
10
+ import type { CheckpointRunScope, CheckpointStore } from '../../types/run/checkpoint-store.js'
11
11
  import type { EmergencySaveData } from '../../types/run/emergency.js'
12
12
  import type { CheckpointListEntry } from '../../types/run/replay.js'
13
13
  import { ZERO_COST } from '../../utils/cost.js'
14
14
  import { buildToolResultHashes } from '../../utils/hash.js'
15
15
  import { generateCheckpointId } from '../../utils/id.js'
16
16
 
17
- function toCheckpointListEntry(cp: IterationCheckpoint): CheckpointListEntry {
17
+ /**
18
+ * Projection from a full checkpoint payload to the public listing entry.
19
+ * Exported for the replay `listCheckpoints` entry point, which projects
20
+ * store results without constructing a `CheckpointManager`.
21
+ */
22
+ export function toCheckpointListEntry(cp: IterationCheckpoint): CheckpointListEntry {
18
23
  return {
19
24
  id: cp.id,
20
25
  runId: cp.runId,
@@ -55,10 +60,19 @@ export function projectEmergencyToCheckpoint(dump: EmergencySaveData): Iteration
55
60
  }
56
61
 
57
62
  export class CheckpointManager {
58
- private store: RunDiskStore
63
+ private store: CheckpointStore
64
+ private scope: CheckpointRunScope
59
65
 
60
- constructor(store: RunDiskStore) {
66
+ /**
67
+ * @param store scope-keyed checkpoint persistence. The default query
68
+ * pipeline passes the run's disk-backed store
69
+ * ({@link import('../../store/run/checkpoint-disk.js').DiskCheckpointStore});
70
+ * hosts inject their own via `QueryParams.checkpointStore`.
71
+ * @param scope the run every operation of this manager is keyed to.
72
+ */
73
+ constructor(store: CheckpointStore, scope: CheckpointRunScope) {
61
74
  this.store = store
75
+ this.scope = scope
62
76
  }
63
77
 
64
78
  async create(
@@ -87,12 +101,12 @@ export class CheckpointManager {
87
101
  activeNode: extra?.activeNode,
88
102
  }
89
103
 
90
- await this.store.writeCheckpoint(checkpoint)
104
+ await this.store.writeCheckpoint(this.scope, checkpoint)
91
105
  return checkpoint
92
106
  }
93
107
 
94
108
  async restore(checkpointId: CheckpointId): Promise<IterationCheckpoint> {
95
- const checkpoint = await this.store.readCheckpoint(checkpointId)
109
+ const checkpoint = await this.store.readCheckpoint(this.scope, checkpointId)
96
110
  if (!checkpoint) {
97
111
  throw new Error(`Checkpoint not found: ${checkpointId}`)
98
112
  }
@@ -100,7 +114,7 @@ export class CheckpointManager {
100
114
  }
101
115
 
102
116
  async list(): Promise<IterationCheckpoint[]> {
103
- return this.store.listCheckpoints()
117
+ return this.store.listCheckpoints(this.scope)
104
118
  }
105
119
 
106
120
  /**
@@ -110,7 +124,7 @@ export class CheckpointManager {
110
124
  * full checkpoint payload. See ses_005-deterministic-replay design §3.1.
111
125
  */
112
126
  async listEntries(): Promise<CheckpointListEntry[]> {
113
- const checkpoints = await this.store.listCheckpoints()
127
+ const checkpoints = await this.store.listCheckpoints(this.scope)
114
128
  return checkpoints.map(toCheckpointListEntry)
115
129
  }
116
130
 
@@ -121,7 +135,7 @@ export class CheckpointManager {
121
135
  const toDelete = all.sort((a, b) => a.createdAt - b.createdAt).slice(0, all.length - keepLast)
122
136
 
123
137
  for (const cp of toDelete) {
124
- await this.store.deleteCheckpoint(cp.id)
138
+ await this.store.deleteCheckpoint(this.scope, cp.id)
125
139
  }
126
140
  }
127
141
 
@@ -15,6 +15,7 @@ import type { RunId, SessionId, TenantId } from '../../types/ids/index.js'
15
15
  import type { Message } from '../../types/message/index.js'
16
16
  import type { PermissionMode } from '../../types/permission/index.js'
17
17
  import type { LLMProvider } from '../../types/provider/index.js'
18
+ import type { CheckpointStore } from '../../types/run/checkpoint-store.js'
18
19
  import type { AgentRunConfig } from '../../types/run/index.js'
19
20
  import type { ProjectId, ThreadId } from '../../types/session/ids.js'
20
21
  import type { ModelPricing } from '../../utils/cost.js'
@@ -55,6 +56,13 @@ export interface RunContextConfig {
55
56
 
56
57
  pathBuilder?: PathBuilder
57
58
 
59
+ /**
60
+ * Optional checkpoint persistence override, threaded through to
61
+ * {@link RunPersistence}. Absent ⇒ disk default under the run's
62
+ * output directory.
63
+ */
64
+ checkpointStore?: CheckpointStore
65
+
58
66
  /**
59
67
  * Optional injected migrator — tests pass a stub; production code relies
60
68
  * on the {@link DefaultFilesystemMigrator}. See session-hierarchy.md
@@ -162,6 +170,7 @@ export class RunContextFactory {
162
170
  projectId: config.projectId,
163
171
  parentRunId: config.parentRunId,
164
172
  depth: config.depth,
173
+ checkpointStore: config.checkpointStore,
165
174
  })
166
175
 
167
176
  const trackingConfig = resolveActivityTracking(permissionMode, config.enableActivityTracking)
@@ -7,6 +7,7 @@ import {
7
7
  import { extractFromUserMessage } from '../../compaction/extractor.js'
8
8
  import { WorkingStateManager } from '../../compaction/manager.js'
9
9
  import type { CompactionConfig } from '../../config/runtime.js'
10
+ import { resolveProviderCapabilities } from '../../provider/capabilities.js'
10
11
  import type { PathBuilder } from '../../session/workspace/path-builder.js'
11
12
  import { GENAI, NAMZU, agentRunSpanName } from '../../telemetry/attributes.js'
12
13
  import { getTracer } from '../../telemetry/runtime-accessors.js'
@@ -16,6 +17,7 @@ import { buildTaskTools } from '../../tools/task/index.js'
16
17
  import type { AdvisoryConfig } from '../../types/advisory/index.js'
17
18
  import type { AgentRuntimeContext, RuntimeToolOverrides } from '../../types/agent/base.js'
18
19
  import type { AgentContextLevel } from '../../types/agent/factory.js'
20
+ import type { WorkingMemoryProvider } from '../../types/agent/working-memory.js'
19
21
  import {
20
22
  type CheckpointId,
21
23
  type ResumeHandler,
@@ -27,6 +29,7 @@ import { type Message, createSystemMessage } from '../../types/message/index.js'
27
29
  import type { AgentPersona } from '../../types/persona/index.js'
28
30
  import type { LLMProvider } from '../../types/provider/index.js'
29
31
  import type { TaskRouterConfig } from '../../types/router/index.js'
32
+ import type { CheckpointStore } from '../../types/run/checkpoint-store.js'
30
33
  import type { AgentRunConfig, Run, RunEvent, RunEventListener } from '../../types/run/index.js'
31
34
  import type { Sandbox, SandboxProvider } from '../../types/sandbox/index.js'
32
35
  import type { ProjectId, ThreadId } from '../../types/session/ids.js'
@@ -42,6 +45,8 @@ import { RunContextFactory } from './context.js'
42
45
  import { EventTranslator } from './events.js'
43
46
  import { GuardCoordinator } from './guard.js'
44
47
  import { IterationOrchestrator } from './iteration/index.js'
48
+ import { isCompactionMessage } from './iteration/phases/compaction.js'
49
+ import { isWorkingMemoryMessage } from './iteration/phases/working-memory.js'
45
50
  import { applyLifecycleHookResults } from './plugin-hooks.js'
46
51
  import { PromptBuilder } from './prompt.js'
47
52
  import type { PromptSegments } from './prompt.js'
@@ -92,6 +97,15 @@ export interface QueryParams {
92
97
  */
93
98
  pathBuilder?: PathBuilder
94
99
 
100
+ /**
101
+ * Optional checkpoint persistence override. Absent ⇒ iteration
102
+ * checkpoints go to the disk layout under the run's output directory
103
+ * (today's behavior). A host injects a scope-keyed
104
+ * {@link CheckpointStore} (e.g. Postgres-backed) so mid-turn resume
105
+ * survives machines that lose their local disk.
106
+ */
107
+ checkpointStore?: CheckpointStore
108
+
95
109
  runId?: RunId
96
110
 
97
111
  parentRunId?: RunId
@@ -127,6 +141,15 @@ export interface QueryParams {
127
141
 
128
142
  compactionConfig?: CompactionConfig
129
143
 
144
+ /**
145
+ * Optional neutral working-memory seam. When set, the iteration loop
146
+ * re-renders the provider's string into a single pinned leading system
147
+ * message every turn (the primacy-edge, compaction-preserved slot).
148
+ * Absent ⇒ `refreshWorkingMemory` early-returns and the run path is
149
+ * byte-identical.
150
+ */
151
+ workingMemoryProvider?: WorkingMemoryProvider
152
+
130
153
  agentBus?: import('../../bus/index.js').AgentBus
131
154
 
132
155
  verificationGate?: VerificationGateConfig
@@ -136,6 +159,18 @@ export interface QueryParams {
136
159
  sandboxProvider?: SandboxProvider
137
160
 
138
161
  invocationState?: InvocationState
162
+
163
+ /**
164
+ * Capability-mismatch handling. Default `false`: when the request asks
165
+ * for something the provider driver declared it cannot do (tools
166
+ * registered against a `supportsTools: false` driver, image
167
+ * attachments against a `supportsVision: false` driver), the runtime
168
+ * warns loudly, emits a `capability_warning` run event, and degrades
169
+ * explicitly (tool surfaces stripped from prompt + request;
170
+ * attachments left unmapped by the driver). `true`: throw instead of
171
+ * degrading.
172
+ */
173
+ strictCapabilities?: boolean
139
174
  }
140
175
 
141
176
  export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run> {
@@ -162,6 +197,7 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
162
197
  projectId: params.projectId,
163
198
  tenantId: params.tenantId,
164
199
  pathBuilder: params.pathBuilder,
200
+ checkpointStore: params.checkpointStore,
165
201
  runId: params.runId,
166
202
  parentRunId: params.parentRunId,
167
203
  depth: params.depth,
@@ -187,7 +223,15 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
187
223
  })
188
224
 
189
225
  if (decision.action === 'approve_plan') {
190
- return { approved: true }
226
+ // Optional approve-with-edits channel: the host may attach
227
+ // feedback to an approval. `PlanApprovalResponse.feedback`
228
+ // already exists on the type; threading it through lets the
229
+ // coordinator's approve_plan tool surface the user's edits in
230
+ // the same tool_result that unblocks the park. Bare approvals
231
+ // stay byte-identical (`{ approved: true }`).
232
+ return decision.feedback
233
+ ? { approved: true, feedback: decision.feedback }
234
+ : { approved: true }
191
235
  }
192
236
  if (decision.action === 'reject_plan') {
193
237
  return { approved: false, feedback: decision.feedback }
@@ -224,7 +268,45 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
224
268
  }
225
269
  }
226
270
 
227
- const effectiveAllowedTools = withDeferredDiscoveryTool(params.tools, params.allowedTools)
271
+ // ─── Provider capability negotiation (before tooling bootstrap) ────────
272
+ // Compare what the request asks for with what the DRIVER declared it
273
+ // does. Undeclared capabilities resolve permissively (today's behavior
274
+ // for third-party providers); declared gaps degrade loudly instead of
275
+ // silently.
276
+ const capabilities = resolveProviderCapabilities(params.provider)
277
+ const registeredToolCount = params.tools.listNames().length
278
+ const stripToolSurfaces = !capabilities.supportsTools && registeredToolCount > 0
279
+ const attachmentMessageCount = capabilities.supportsVision
280
+ ? 0
281
+ : params.messages.filter(
282
+ (m) => m.role === 'user' && m.attachments !== undefined && m.attachments.length > 0,
283
+ ).length
284
+
285
+ if (stripToolSurfaces) {
286
+ const message = `Provider '${params.provider.id}' declares supportsTools: false but ${registeredToolCount} tool(s) are registered — stripping all tool surfaces from the prompt and request so the model is never told about tools it cannot call. Pass strictCapabilities: true to fail instead, or use a tools-capable provider.`
287
+ if (params.strictCapabilities) {
288
+ throw new Error(message)
289
+ }
290
+ ctx.log.warn(`CAPABILITY MISMATCH: ${message}`, {
291
+ providerId: params.provider.id,
292
+ registeredToolCount,
293
+ })
294
+ }
295
+
296
+ if (attachmentMessageCount > 0) {
297
+ const message = `Provider '${params.provider.id}' declares supportsVision: false but ${attachmentMessageCount} user message(s) carry image attachments — the driver will not map them, so the model never sees the images. Pass strictCapabilities: true to fail instead, or use a vision-capable provider.`
298
+ if (params.strictCapabilities) {
299
+ throw new Error(message)
300
+ }
301
+ ctx.log.warn(`CAPABILITY MISMATCH: ${message}`, {
302
+ providerId: params.provider.id,
303
+ attachmentMessageCount,
304
+ })
305
+ }
306
+
307
+ const effectiveAllowedTools = stripToolSurfaces
308
+ ? []
309
+ : withDeferredDiscoveryTool(params.tools, params.allowedTools)
228
310
 
229
311
  const toolExecutor = ToolingBootstrap.init(
230
312
  {
@@ -266,7 +348,10 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
266
348
  maxIterations: params.runConfig.maxIterations,
267
349
  })
268
350
 
269
- const checkpointMgr = new CheckpointManager(ctx.runMgr.getRunStore())
351
+ const checkpointMgr = new CheckpointManager(
352
+ ctx.runMgr.getCheckpointStore(),
353
+ ctx.runMgr.getRunScope(),
354
+ )
270
355
 
271
356
  const resultAssembler = new ResultAssembler({
272
357
  runMgr: ctx.runMgr,
@@ -310,34 +395,33 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
310
395
  ? new VerificationGate(params.verificationGate, ctx.log)
311
396
  : undefined
312
397
 
313
- const iterationOrchestrator = new IterationOrchestrator(
314
- {
315
- provider: params.provider,
316
- runConfig: params.runConfig,
317
- tools: params.tools,
318
- allowedTools: effectiveAllowedTools,
319
- taskGateway: params.taskGateway,
320
- taskStore: params.taskStore,
321
- launchedTasks: params.launchedTasks,
322
- advisoryCtx,
323
- compactionConfig: params.compactionConfig,
324
- workingStateManager,
325
- agentBus: params.agentBus,
326
- verificationGate: verificationGate,
327
- pluginManager: params.pluginManager,
328
- },
329
- ctx.runMgr,
398
+ const iterationOrchestrator = new IterationOrchestrator({
399
+ provider: params.provider,
400
+ runConfig: params.runConfig,
401
+ tools: params.tools,
402
+ allowedTools: effectiveAllowedTools,
403
+ runMgr: ctx.runMgr,
330
404
  toolExecutor,
331
405
  guard,
332
- ctx.activityStore,
333
- eventTranslator.emitEvent,
334
- () => eventTranslator.drainPending(),
335
- ctx.abortController,
336
- ctx.log,
337
- params.resumeHandler,
406
+ activityStore: ctx.activityStore,
407
+ emitEvent: eventTranslator.emitEvent,
408
+ drainPending: () => eventTranslator.drainPending(),
409
+ abortController: ctx.abortController,
410
+ log: ctx.log,
411
+ resumeHandler: params.resumeHandler,
338
412
  checkpointMgr,
339
- ctx.planManager,
340
- )
413
+ planManager: ctx.planManager,
414
+ taskGateway: params.taskGateway,
415
+ taskStore: params.taskStore,
416
+ launchedTasks: params.launchedTasks ?? new Map(),
417
+ compactionConfig: params.compactionConfig,
418
+ workingStateManager,
419
+ workingMemoryProvider: params.workingMemoryProvider,
420
+ advisoryCtx,
421
+ agentBus: params.agentBus,
422
+ verificationGate,
423
+ pluginManager: params.pluginManager,
424
+ })
341
425
 
342
426
  const tracer = getTracer()
343
427
 
@@ -408,7 +492,18 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
408
492
 
409
493
  pushSystemMessages()
410
494
  for (const msg of checkpoint.messages) {
411
- if (msg.role === 'system') continue
495
+ if (msg.role === 'system') {
496
+ // Re-push the FRESH static/dynamic floor (done above) but PRESERVE
497
+ // the two system messages that carry irreplaceable run state: the
498
+ // `[COMPACTED CONTEXT]` summary is the only surviving record of the
499
+ // older history a compaction pass deleted, and the working-memory
500
+ // slot pins the produced-artifact ledger. Dropping every system
501
+ // message on restore silently lost both on resume.
502
+ if (isCompactionMessage(msg.content) || isWorkingMemoryMessage(msg.content)) {
503
+ ctx.runMgr.pushMessage(msg)
504
+ }
505
+ continue
506
+ }
412
507
  ctx.runMgr.pushMessage(msg)
413
508
  }
414
509
  } else if (params.continuationMode) {
@@ -442,6 +537,30 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
442
537
  })
443
538
  yield* eventTranslator.drainPending()
444
539
 
540
+ // Surface capability degradation to the host as run events —
541
+ // explicit, not silent (the log.warn above fires at setup time;
542
+ // this is the machine-readable channel).
543
+ if (stripToolSurfaces) {
544
+ await eventTranslator.emitEvent({
545
+ type: 'capability_warning',
546
+ runId: ctx.runMgr.id,
547
+ capability: 'tools',
548
+ providerId: params.provider.id,
549
+ message: `Provider '${params.provider.id}' does not support tools — ${registeredToolCount} registered tool(s) were stripped from the prompt and request.`,
550
+ })
551
+ yield* eventTranslator.drainPending()
552
+ }
553
+ if (attachmentMessageCount > 0) {
554
+ await eventTranslator.emitEvent({
555
+ type: 'capability_warning',
556
+ runId: ctx.runMgr.id,
557
+ capability: 'vision',
558
+ providerId: params.provider.id,
559
+ message: `Provider '${params.provider.id}' does not support vision — image attachments on ${attachmentMessageCount} user message(s) will not reach the model.`,
560
+ })
561
+ yield* eventTranslator.drainPending()
562
+ }
563
+
445
564
  if (params.pluginManager) {
446
565
  const hookResults = await params.pluginManager.executeHooks(
447
566
  'run_start',