@animalabs/agent-framework 0.22.2 → 0.22.3

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 (935) hide show
  1. package/README.md +3 -1
  2. package/dist/src/agent.d.ts +77 -12
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +136 -40
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/mcp-server-core.js +1 -1
  7. package/dist/src/api/mcp-server-core.js.map +1 -1
  8. package/dist/src/api/server.d.ts.map +1 -1
  9. package/dist/src/api/server.js +5 -1
  10. package/dist/src/api/server.js.map +1 -1
  11. package/dist/src/api/types.d.ts +1 -1
  12. package/dist/src/api/types.d.ts.map +1 -1
  13. package/dist/src/branch-names.d.ts +14 -0
  14. package/dist/src/branch-names.d.ts.map +1 -0
  15. package/dist/src/branch-names.js +22 -0
  16. package/dist/src/branch-names.js.map +1 -0
  17. package/dist/src/closest-names.d.ts +28 -0
  18. package/dist/src/closest-names.d.ts.map +1 -0
  19. package/dist/src/closest-names.js +98 -0
  20. package/dist/src/closest-names.js.map +1 -0
  21. package/dist/src/code-execution/py-runner.d.ts +16 -5
  22. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  23. package/dist/src/code-execution/py-runner.js +186 -74
  24. package/dist/src/code-execution/py-runner.js.map +1 -1
  25. package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
  26. package/dist/src/code-execution/runtime-py.js +49 -3
  27. package/dist/src/code-execution/runtime-py.js.map +1 -1
  28. package/dist/src/code-execution/tool-definition.d.ts +12 -1
  29. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  30. package/dist/src/code-execution/tool-definition.js +21 -3
  31. package/dist/src/code-execution/tool-definition.js.map +1 -1
  32. package/dist/src/config-checks.d.ts +28 -0
  33. package/dist/src/config-checks.d.ts.map +1 -0
  34. package/dist/src/config-checks.js +52 -0
  35. package/dist/src/config-checks.js.map +1 -0
  36. package/dist/src/deadline-timer.d.ts +21 -0
  37. package/dist/src/deadline-timer.d.ts.map +1 -0
  38. package/dist/src/deadline-timer.js +43 -0
  39. package/dist/src/deadline-timer.js.map +1 -0
  40. package/dist/src/error-message.d.ts +30 -0
  41. package/dist/src/error-message.d.ts.map +1 -0
  42. package/dist/src/error-message.js +50 -0
  43. package/dist/src/error-message.js.map +1 -0
  44. package/dist/src/framework.d.ts +828 -88
  45. package/dist/src/framework.d.ts.map +1 -1
  46. package/dist/src/framework.js +5011 -1326
  47. package/dist/src/framework.js.map +1 -1
  48. package/dist/src/gate/event-gate.d.ts +8 -0
  49. package/dist/src/gate/event-gate.d.ts.map +1 -1
  50. package/dist/src/gate/event-gate.js +27 -3
  51. package/dist/src/gate/event-gate.js.map +1 -1
  52. package/dist/src/index.d.ts +4 -2
  53. package/dist/src/index.d.ts.map +1 -1
  54. package/dist/src/index.js +4 -2
  55. package/dist/src/index.js.map +1 -1
  56. package/dist/src/mcpl/channel-registry.d.ts +144 -6
  57. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  58. package/dist/src/mcpl/channel-registry.js +424 -263
  59. package/dist/src/mcpl/channel-registry.js.map +1 -1
  60. package/dist/src/mcpl/checkpoint-manager.d.ts +21 -1
  61. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  62. package/dist/src/mcpl/checkpoint-manager.js +91 -30
  63. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  64. package/dist/src/mcpl/conversation-router.d.ts +19 -3
  65. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  66. package/dist/src/mcpl/conversation-router.js +23 -8
  67. package/dist/src/mcpl/conversation-router.js.map +1 -1
  68. package/dist/src/mcpl/convert-block.d.ts +20 -0
  69. package/dist/src/mcpl/convert-block.d.ts.map +1 -0
  70. package/dist/src/mcpl/convert-block.js +89 -0
  71. package/dist/src/mcpl/convert-block.js.map +1 -0
  72. package/dist/src/mcpl/errors.d.ts +13 -0
  73. package/dist/src/mcpl/errors.d.ts.map +1 -1
  74. package/dist/src/mcpl/errors.js +28 -0
  75. package/dist/src/mcpl/errors.js.map +1 -1
  76. package/dist/src/mcpl/hook-orchestrator.d.ts +23 -0
  77. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  78. package/dist/src/mcpl/hook-orchestrator.js +83 -72
  79. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  80. package/dist/src/mcpl/inbound-source.d.ts +114 -0
  81. package/dist/src/mcpl/inbound-source.d.ts.map +1 -0
  82. package/dist/src/mcpl/inbound-source.js +83 -0
  83. package/dist/src/mcpl/inbound-source.js.map +1 -0
  84. package/dist/src/mcpl/index.d.ts +3 -1
  85. package/dist/src/mcpl/index.d.ts.map +1 -1
  86. package/dist/src/mcpl/index.js +3 -1
  87. package/dist/src/mcpl/index.js.map +1 -1
  88. package/dist/src/mcpl/inference-router.d.ts +15 -11
  89. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  90. package/dist/src/mcpl/inference-router.js +153 -84
  91. package/dist/src/mcpl/inference-router.js.map +1 -1
  92. package/dist/src/mcpl/push-coalescer.d.ts +33 -11
  93. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  94. package/dist/src/mcpl/push-coalescer.js +52 -20
  95. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  96. package/dist/src/mcpl/push-handler.d.ts +16 -9
  97. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  98. package/dist/src/mcpl/push-handler.js +18 -83
  99. package/dist/src/mcpl/push-handler.js.map +1 -1
  100. package/dist/src/mcpl/reference-fetcher.d.ts +14 -2
  101. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  102. package/dist/src/mcpl/reference-fetcher.js +72 -30
  103. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  104. package/dist/src/mcpl/references.d.ts +47 -12
  105. package/dist/src/mcpl/references.d.ts.map +1 -1
  106. package/dist/src/mcpl/references.js +74 -21
  107. package/dist/src/mcpl/references.js.map +1 -1
  108. package/dist/src/mcpl/server-connection.d.ts +89 -10
  109. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  110. package/dist/src/mcpl/server-connection.js +405 -129
  111. package/dist/src/mcpl/server-connection.js.map +1 -1
  112. package/dist/src/mcpl/server-registry.d.ts +10 -1
  113. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  114. package/dist/src/mcpl/server-registry.js +14 -3
  115. package/dist/src/mcpl/server-registry.js.map +1 -1
  116. package/dist/src/mcpl/skip-reply-wake.d.ts +36 -0
  117. package/dist/src/mcpl/skip-reply-wake.d.ts.map +1 -0
  118. package/dist/src/mcpl/skip-reply-wake.js +55 -0
  119. package/dist/src/mcpl/skip-reply-wake.js.map +1 -0
  120. package/dist/src/mcpl/transport.d.ts +51 -2
  121. package/dist/src/mcpl/transport.d.ts.map +1 -1
  122. package/dist/src/mcpl/transport.js +114 -20
  123. package/dist/src/mcpl/transport.js.map +1 -1
  124. package/dist/src/mcpl/types.d.ts +14 -3
  125. package/dist/src/mcpl/types.d.ts.map +1 -1
  126. package/dist/src/mcpl/types.js.map +1 -1
  127. package/dist/src/mcpl/visible-content.d.ts +15 -0
  128. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  129. package/dist/src/mcpl/visible-content.js +25 -0
  130. package/dist/src/mcpl/visible-content.js.map +1 -1
  131. package/dist/src/module-registry.d.ts +19 -1
  132. package/dist/src/module-registry.d.ts.map +1 -1
  133. package/dist/src/module-registry.js +54 -25
  134. package/dist/src/module-registry.js.map +1 -1
  135. package/dist/src/modules/api/index.js +1 -1
  136. package/dist/src/modules/api/index.js.map +1 -1
  137. package/dist/src/modules/discord/discord-js-client.d.ts +1 -0
  138. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
  139. package/dist/src/modules/discord/discord-js-client.js +25 -9
  140. package/dist/src/modules/discord/discord-js-client.js.map +1 -1
  141. package/dist/src/modules/discord/index.d.ts +29 -3
  142. package/dist/src/modules/discord/index.d.ts.map +1 -1
  143. package/dist/src/modules/discord/index.js +379 -143
  144. package/dist/src/modules/discord/index.js.map +1 -1
  145. package/dist/src/modules/discord/types.d.ts +14 -1
  146. package/dist/src/modules/discord/types.d.ts.map +1 -1
  147. package/dist/src/modules/health/index.d.ts.map +1 -1
  148. package/dist/src/modules/health/index.js +2 -1
  149. package/dist/src/modules/health/index.js.map +1 -1
  150. package/dist/src/modules/history/index.d.ts +3 -1
  151. package/dist/src/modules/history/index.d.ts.map +1 -1
  152. package/dist/src/modules/history/index.js +359 -127
  153. package/dist/src/modules/history/index.js.map +1 -1
  154. package/dist/src/modules/history/semantic.d.ts +28 -1
  155. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  156. package/dist/src/modules/history/semantic.js +177 -32
  157. package/dist/src/modules/history/semantic.js.map +1 -1
  158. package/dist/src/modules/unknown-tool.d.ts +10 -0
  159. package/dist/src/modules/unknown-tool.d.ts.map +1 -0
  160. package/dist/src/modules/unknown-tool.js +17 -0
  161. package/dist/src/modules/unknown-tool.js.map +1 -0
  162. package/dist/src/modules/workspace/disk-agreement.d.ts +32 -2
  163. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
  164. package/dist/src/modules/workspace/disk-agreement.js +30 -2
  165. package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
  166. package/dist/src/modules/workspace/effects.d.ts +64 -6
  167. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  168. package/dist/src/modules/workspace/effects.js +291 -44
  169. package/dist/src/modules/workspace/effects.js.map +1 -1
  170. package/dist/src/modules/workspace/index.d.ts +119 -23
  171. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  172. package/dist/src/modules/workspace/index.js +793 -247
  173. package/dist/src/modules/workspace/index.js.map +1 -1
  174. package/dist/src/modules/workspace/observe.d.ts +75 -8
  175. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  176. package/dist/src/modules/workspace/observe.js +174 -14
  177. package/dist/src/modules/workspace/observe.js.map +1 -1
  178. package/dist/src/modules/workspace/reconcile.d.ts +58 -7
  179. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  180. package/dist/src/modules/workspace/reconcile.js +521 -56
  181. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  182. package/dist/src/modules/workspace/types.d.ts +18 -4
  183. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  184. package/dist/src/modules/workspace/types.js.map +1 -1
  185. package/dist/src/modules/workspace/watcher.d.ts +7 -2
  186. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  187. package/dist/src/modules/workspace/watcher.js +28 -4
  188. package/dist/src/modules/workspace/watcher.js.map +1 -1
  189. package/dist/src/prose-drafts.d.ts +240 -0
  190. package/dist/src/prose-drafts.d.ts.map +1 -0
  191. package/dist/src/prose-drafts.js +362 -0
  192. package/dist/src/prose-drafts.js.map +1 -0
  193. package/dist/src/prose-segments.d.ts +21 -2
  194. package/dist/src/prose-segments.d.ts.map +1 -1
  195. package/dist/src/prose-segments.js +46 -9
  196. package/dist/src/prose-segments.js.map +1 -1
  197. package/dist/src/recovery/offline-branch.d.ts +1 -0
  198. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  199. package/dist/src/recovery/offline-branch.js +3 -1
  200. package/dist/src/recovery/offline-branch.js.map +1 -1
  201. package/dist/src/safe-slice.d.ts +5 -1
  202. package/dist/src/safe-slice.d.ts.map +1 -1
  203. package/dist/src/safe-slice.js +9 -1
  204. package/dist/src/safe-slice.js.map +1 -1
  205. package/dist/src/tool-image-ledger.d.ts +87 -19
  206. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  207. package/dist/src/tool-image-ledger.js +135 -32
  208. package/dist/src/tool-image-ledger.js.map +1 -1
  209. package/dist/src/tool-presentation.d.ts +9 -0
  210. package/dist/src/tool-presentation.d.ts.map +1 -1
  211. package/dist/src/tool-presentation.js +257 -60
  212. package/dist/src/tool-presentation.js.map +1 -1
  213. package/dist/src/tool-result-guard.d.ts.map +1 -1
  214. package/dist/src/tool-result-guard.js +7 -10
  215. package/dist/src/tool-result-guard.js.map +1 -1
  216. package/dist/src/tool-result-history.d.ts +32 -3
  217. package/dist/src/tool-result-history.d.ts.map +1 -1
  218. package/dist/src/tool-result-history.js +106 -19
  219. package/dist/src/tool-result-history.js.map +1 -1
  220. package/dist/src/tune-out/coordinator.d.ts +25 -1
  221. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  222. package/dist/src/tune-out/coordinator.js +260 -65
  223. package/dist/src/tune-out/coordinator.js.map +1 -1
  224. package/dist/src/types/agent.d.ts +17 -5
  225. package/dist/src/types/agent.d.ts.map +1 -1
  226. package/dist/src/types/events.d.ts +17 -0
  227. package/dist/src/types/events.d.ts.map +1 -1
  228. package/dist/src/types/framework.d.ts +21 -5
  229. package/dist/src/types/framework.d.ts.map +1 -1
  230. package/dist/src/types/inference-log.d.ts +1 -1
  231. package/dist/src/types/inference-log.d.ts.map +1 -1
  232. package/dist/src/types/module.d.ts +23 -4
  233. package/dist/src/types/module.d.ts.map +1 -1
  234. package/dist/src/types/trace.d.ts +95 -6
  235. package/dist/src/types/trace.d.ts.map +1 -1
  236. package/dist/src/usage/usage-tracker.d.ts +5 -0
  237. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  238. package/dist/src/usage/usage-tracker.js +35 -8
  239. package/dist/src/usage/usage-tracker.js.map +1 -1
  240. package/package.json +3 -4
  241. package/src/agent.ts +162 -40
  242. package/src/api/mcp-server-core.ts +1 -1
  243. package/src/api/server.ts +4 -1
  244. package/src/api/types.ts +1 -1
  245. package/src/branch-names.ts +19 -0
  246. package/src/closest-names.ts +94 -0
  247. package/src/code-execution/py-runner.ts +200 -76
  248. package/src/code-execution/runtime-py.ts +49 -3
  249. package/src/code-execution/tool-definition.ts +28 -4
  250. package/src/config-checks.ts +48 -0
  251. package/src/deadline-timer.ts +44 -0
  252. package/src/error-message.ts +48 -0
  253. package/src/framework.ts +5352 -1388
  254. package/src/gate/event-gate.ts +28 -3
  255. package/src/index.ts +11 -2
  256. package/src/mcpl/channel-registry.ts +517 -286
  257. package/src/mcpl/checkpoint-manager.ts +104 -29
  258. package/src/mcpl/conversation-router.ts +28 -8
  259. package/src/mcpl/convert-block.ts +97 -0
  260. package/src/mcpl/errors.ts +28 -0
  261. package/src/mcpl/hook-orchestrator.ts +84 -77
  262. package/src/mcpl/inbound-source.ts +156 -0
  263. package/src/mcpl/index.ts +10 -1
  264. package/src/mcpl/inference-router.ts +174 -99
  265. package/src/mcpl/push-coalescer.ts +60 -24
  266. package/src/mcpl/push-handler.ts +20 -90
  267. package/src/mcpl/reference-fetcher.ts +71 -26
  268. package/src/mcpl/references.ts +81 -22
  269. package/src/mcpl/server-connection.ts +427 -132
  270. package/src/mcpl/server-registry.ts +15 -4
  271. package/src/mcpl/skip-reply-wake.ts +73 -0
  272. package/src/mcpl/transport.ts +120 -18
  273. package/src/mcpl/types.ts +14 -3
  274. package/src/mcpl/visible-content.ts +27 -0
  275. package/src/module-registry.ts +58 -23
  276. package/src/modules/api/index.ts +1 -1
  277. package/src/modules/discord/discord-js-client.ts +26 -10
  278. package/src/modules/discord/index.ts +391 -158
  279. package/src/modules/discord/types.ts +15 -2
  280. package/src/modules/health/index.ts +2 -1
  281. package/src/modules/history/index.ts +410 -120
  282. package/src/modules/history/semantic.ts +189 -32
  283. package/src/modules/unknown-tool.ts +16 -0
  284. package/src/modules/workspace/disk-agreement.ts +51 -3
  285. package/src/modules/workspace/effects.ts +279 -35
  286. package/src/modules/workspace/index.ts +810 -260
  287. package/src/modules/workspace/observe.ts +194 -20
  288. package/src/modules/workspace/reconcile.ts +619 -64
  289. package/src/modules/workspace/types.ts +18 -4
  290. package/src/modules/workspace/watcher.ts +27 -4
  291. package/src/prose-drafts.ts +481 -0
  292. package/src/prose-segments.ts +51 -8
  293. package/src/recovery/offline-branch.ts +4 -1
  294. package/src/safe-slice.ts +9 -1
  295. package/src/tool-image-ledger.ts +153 -34
  296. package/src/tool-presentation.ts +224 -40
  297. package/src/tool-result-guard.ts +7 -8
  298. package/src/tool-result-history.ts +124 -20
  299. package/src/tune-out/coordinator.ts +259 -65
  300. package/src/types/agent.ts +17 -7
  301. package/src/types/events.ts +17 -0
  302. package/src/types/framework.ts +21 -5
  303. package/src/types/inference-log.ts +1 -1
  304. package/src/types/module.ts +24 -4
  305. package/src/types/trace.ts +101 -6
  306. package/src/usage/usage-tracker.ts +32 -8
  307. package/dist/test/activation-request-sanitize.test.d.ts +0 -2
  308. package/dist/test/activation-request-sanitize.test.d.ts.map +0 -1
  309. package/dist/test/activation-request-sanitize.test.js +0 -89
  310. package/dist/test/activation-request-sanitize.test.js.map +0 -1
  311. package/dist/test/agent-cache-ttl.test.d.ts +0 -2
  312. package/dist/test/agent-cache-ttl.test.d.ts.map +0 -1
  313. package/dist/test/agent-cache-ttl.test.js +0 -41
  314. package/dist/test/agent-cache-ttl.test.js.map +0 -1
  315. package/dist/test/agent-runtime-settings.test.d.ts +0 -2
  316. package/dist/test/agent-runtime-settings.test.d.ts.map +0 -1
  317. package/dist/test/agent-runtime-settings.test.js +0 -1598
  318. package/dist/test/agent-runtime-settings.test.js.map +0 -1
  319. package/dist/test/api-admin-gate.test.d.ts +0 -2
  320. package/dist/test/api-admin-gate.test.d.ts.map +0 -1
  321. package/dist/test/api-admin-gate.test.js +0 -116
  322. package/dist/test/api-admin-gate.test.js.map +0 -1
  323. package/dist/test/assistant-persist-refusal.test.d.ts +0 -2
  324. package/dist/test/assistant-persist-refusal.test.d.ts.map +0 -1
  325. package/dist/test/assistant-persist-refusal.test.js +0 -63
  326. package/dist/test/assistant-persist-refusal.test.js.map +0 -1
  327. package/dist/test/batched-wake-locus.test.d.ts +0 -2
  328. package/dist/test/batched-wake-locus.test.d.ts.map +0 -1
  329. package/dist/test/batched-wake-locus.test.js +0 -166
  330. package/dist/test/batched-wake-locus.test.js.map +0 -1
  331. package/dist/test/busy-agent-scheduler.test.d.ts +0 -2
  332. package/dist/test/busy-agent-scheduler.test.d.ts.map +0 -1
  333. package/dist/test/busy-agent-scheduler.test.js +0 -92
  334. package/dist/test/busy-agent-scheduler.test.js.map +0 -1
  335. package/dist/test/channel-incoming-targeting.test.d.ts +0 -2
  336. package/dist/test/channel-incoming-targeting.test.d.ts.map +0 -1
  337. package/dist/test/channel-incoming-targeting.test.js +0 -81
  338. package/dist/test/channel-incoming-targeting.test.js.map +0 -1
  339. package/dist/test/channel-label-history.test.d.ts +0 -2
  340. package/dist/test/channel-label-history.test.d.ts.map +0 -1
  341. package/dist/test/channel-label-history.test.js +0 -424
  342. package/dist/test/channel-label-history.test.js.map +0 -1
  343. package/dist/test/channel-lifecycle-provenance.test.d.ts +0 -2
  344. package/dist/test/channel-lifecycle-provenance.test.d.ts.map +0 -1
  345. package/dist/test/channel-lifecycle-provenance.test.js +0 -47
  346. package/dist/test/channel-lifecycle-provenance.test.js.map +0 -1
  347. package/dist/test/channel-registry-routing.test.d.ts +0 -2
  348. package/dist/test/channel-registry-routing.test.d.ts.map +0 -1
  349. package/dist/test/channel-registry-routing.test.js +0 -446
  350. package/dist/test/channel-registry-routing.test.js.map +0 -1
  351. package/dist/test/channel-registry-typing.test.d.ts +0 -2
  352. package/dist/test/channel-registry-typing.test.d.ts.map +0 -1
  353. package/dist/test/channel-registry-typing.test.js +0 -91
  354. package/dist/test/channel-registry-typing.test.js.map +0 -1
  355. package/dist/test/channels-changed-respond-first.test.d.ts +0 -2
  356. package/dist/test/channels-changed-respond-first.test.d.ts.map +0 -1
  357. package/dist/test/channels-changed-respond-first.test.js +0 -74
  358. package/dist/test/channels-changed-respond-first.test.js.map +0 -1
  359. package/dist/test/checkpoint-attribution.test.d.ts +0 -2
  360. package/dist/test/checkpoint-attribution.test.d.ts.map +0 -1
  361. package/dist/test/checkpoint-attribution.test.js +0 -200
  362. package/dist/test/checkpoint-attribution.test.js.map +0 -1
  363. package/dist/test/code-execution.test.d.ts +0 -11
  364. package/dist/test/code-execution.test.d.ts.map +0 -1
  365. package/dist/test/code-execution.test.js +0 -1272
  366. package/dist/test/code-execution.test.js.map +0 -1
  367. package/dist/test/component-defaults.test.d.ts +0 -2
  368. package/dist/test/component-defaults.test.d.ts.map +0 -1
  369. package/dist/test/component-defaults.test.js +0 -54
  370. package/dist/test/component-defaults.test.js.map +0 -1
  371. package/dist/test/conversation-fork-injection-scoping.test.d.ts +0 -2
  372. package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +0 -1
  373. package/dist/test/conversation-fork-injection-scoping.test.js +0 -200
  374. package/dist/test/conversation-fork-injection-scoping.test.js.map +0 -1
  375. package/dist/test/conversation-router.test.d.ts +0 -2
  376. package/dist/test/conversation-router.test.d.ts.map +0 -1
  377. package/dist/test/conversation-router.test.js +0 -174
  378. package/dist/test/conversation-router.test.js.map +0 -1
  379. package/dist/test/conversation-routing.test.d.ts +0 -7
  380. package/dist/test/conversation-routing.test.d.ts.map +0 -1
  381. package/dist/test/conversation-routing.test.js +0 -621
  382. package/dist/test/conversation-routing.test.js.map +0 -1
  383. package/dist/test/deferred-wake-integrity.test.d.ts +0 -29
  384. package/dist/test/deferred-wake-integrity.test.d.ts.map +0 -1
  385. package/dist/test/deferred-wake-integrity.test.js +0 -320
  386. package/dist/test/deferred-wake-integrity.test.js.map +0 -1
  387. package/dist/test/discord-awareness-outbox.test.d.ts +0 -2
  388. package/dist/test/discord-awareness-outbox.test.d.ts.map +0 -1
  389. package/dist/test/discord-awareness-outbox.test.js +0 -371
  390. package/dist/test/discord-awareness-outbox.test.js.map +0 -1
  391. package/dist/test/discord-history-delete-window.test.d.ts +0 -2
  392. package/dist/test/discord-history-delete-window.test.d.ts.map +0 -1
  393. package/dist/test/discord-history-delete-window.test.js +0 -81
  394. package/dist/test/discord-history-delete-window.test.js.map +0 -1
  395. package/dist/test/discord-history-sync.test.d.ts +0 -2
  396. package/dist/test/discord-history-sync.test.d.ts.map +0 -1
  397. package/dist/test/discord-history-sync.test.js +0 -56
  398. package/dist/test/discord-history-sync.test.js.map +0 -1
  399. package/dist/test/discord-typing-keepalive.test.d.ts +0 -2
  400. package/dist/test/discord-typing-keepalive.test.d.ts.map +0 -1
  401. package/dist/test/discord-typing-keepalive.test.js +0 -77
  402. package/dist/test/discord-typing-keepalive.test.js.map +0 -1
  403. package/dist/test/ephemeral-settle.test.d.ts +0 -2
  404. package/dist/test/ephemeral-settle.test.d.ts.map +0 -1
  405. package/dist/test/ephemeral-settle.test.js +0 -354
  406. package/dist/test/ephemeral-settle.test.js.map +0 -1
  407. package/dist/test/event-gate-passthrough.test.d.ts +0 -15
  408. package/dist/test/event-gate-passthrough.test.d.ts.map +0 -1
  409. package/dist/test/event-gate-passthrough.test.js +0 -289
  410. package/dist/test/event-gate-passthrough.test.js.map +0 -1
  411. package/dist/test/event-gate-policy-mutation.test.d.ts +0 -2
  412. package/dist/test/event-gate-policy-mutation.test.d.ts.map +0 -1
  413. package/dist/test/event-gate-policy-mutation.test.js +0 -137
  414. package/dist/test/event-gate-policy-mutation.test.js.map +0 -1
  415. package/dist/test/event-gate-rate-limit.test.d.ts +0 -2
  416. package/dist/test/event-gate-rate-limit.test.d.ts.map +0 -1
  417. package/dist/test/event-gate-rate-limit.test.js +0 -627
  418. package/dist/test/event-gate-rate-limit.test.js.map +0 -1
  419. package/dist/test/event-gate-script.test.d.ts +0 -2
  420. package/dist/test/event-gate-script.test.d.ts.map +0 -1
  421. package/dist/test/event-gate-script.test.js +0 -95
  422. package/dist/test/event-gate-script.test.js.map +0 -1
  423. package/dist/test/event-gate-selfwake.test.d.ts +0 -15
  424. package/dist/test/event-gate-selfwake.test.d.ts.map +0 -1
  425. package/dist/test/event-gate-selfwake.test.js +0 -125
  426. package/dist/test/event-gate-selfwake.test.js.map +0 -1
  427. package/dist/test/event-gate-tags.test.d.ts +0 -2
  428. package/dist/test/event-gate-tags.test.d.ts.map +0 -1
  429. package/dist/test/event-gate-tags.test.js +0 -76
  430. package/dist/test/event-gate-tags.test.js.map +0 -1
  431. package/dist/test/event-gate.test.d.ts +0 -2
  432. package/dist/test/event-gate.test.d.ts.map +0 -1
  433. package/dist/test/event-gate.test.js +0 -919
  434. package/dist/test/event-gate.test.js.map +0 -1
  435. package/dist/test/explicit-prose-routing.test.d.ts +0 -11
  436. package/dist/test/explicit-prose-routing.test.d.ts.map +0 -1
  437. package/dist/test/explicit-prose-routing.test.js +0 -1715
  438. package/dist/test/explicit-prose-routing.test.js.map +0 -1
  439. package/dist/test/framework.test.d.ts +0 -5
  440. package/dist/test/framework.test.d.ts.map +0 -1
  441. package/dist/test/framework.test.js +0 -1048
  442. package/dist/test/framework.test.js.map +0 -1
  443. package/dist/test/gather-context-timeout.test.d.ts +0 -2
  444. package/dist/test/gather-context-timeout.test.d.ts.map +0 -1
  445. package/dist/test/gather-context-timeout.test.js +0 -120
  446. package/dist/test/gather-context-timeout.test.js.map +0 -1
  447. package/dist/test/health-snapshot.test.d.ts +0 -2
  448. package/dist/test/health-snapshot.test.d.ts.map +0 -1
  449. package/dist/test/health-snapshot.test.js +0 -74
  450. package/dist/test/health-snapshot.test.js.map +0 -1
  451. package/dist/test/helpers/coalescing-fixture.d.ts +0 -74
  452. package/dist/test/helpers/coalescing-fixture.d.ts.map +0 -1
  453. package/dist/test/helpers/coalescing-fixture.js +0 -152
  454. package/dist/test/helpers/coalescing-fixture.js.map +0 -1
  455. package/dist/test/helpers/coalescing-kill-child.d.ts +0 -2
  456. package/dist/test/helpers/coalescing-kill-child.d.ts.map +0 -1
  457. package/dist/test/helpers/coalescing-kill-child.js +0 -24
  458. package/dist/test/helpers/coalescing-kill-child.js.map +0 -1
  459. package/dist/test/helpers/mock-membrane.d.ts +0 -53
  460. package/dist/test/helpers/mock-membrane.d.ts.map +0 -1
  461. package/dist/test/helpers/mock-membrane.js +0 -168
  462. package/dist/test/helpers/mock-membrane.js.map +0 -1
  463. package/dist/test/helpers/pytc-stop-rows.d.ts +0 -11
  464. package/dist/test/helpers/pytc-stop-rows.d.ts.map +0 -1
  465. package/dist/test/helpers/pytc-stop-rows.js +0 -143
  466. package/dist/test/helpers/pytc-stop-rows.js.map +0 -1
  467. package/dist/test/helpers/quiesce-crash-child.d.ts +0 -2
  468. package/dist/test/helpers/quiesce-crash-child.d.ts.map +0 -1
  469. package/dist/test/helpers/quiesce-crash-child.js +0 -156
  470. package/dist/test/helpers/quiesce-crash-child.js.map +0 -1
  471. package/dist/test/hide-then-turn.test.d.ts +0 -2
  472. package/dist/test/hide-then-turn.test.d.ts.map +0 -1
  473. package/dist/test/hide-then-turn.test.js +0 -101
  474. package/dist/test/hide-then-turn.test.js.map +0 -1
  475. package/dist/test/history-module-overview.test.d.ts +0 -2
  476. package/dist/test/history-module-overview.test.d.ts.map +0 -1
  477. package/dist/test/history-module-overview.test.js +0 -711
  478. package/dist/test/history-module-overview.test.js.map +0 -1
  479. package/dist/test/history-module-realstore.test.d.ts +0 -2
  480. package/dist/test/history-module-realstore.test.d.ts.map +0 -1
  481. package/dist/test/history-module-realstore.test.js +0 -363
  482. package/dist/test/history-module-realstore.test.js.map +0 -1
  483. package/dist/test/history-module-semantic.test.d.ts +0 -2
  484. package/dist/test/history-module-semantic.test.d.ts.map +0 -1
  485. package/dist/test/history-module-semantic.test.js +0 -213
  486. package/dist/test/history-module-semantic.test.js.map +0 -1
  487. package/dist/test/history-module-ux.test.d.ts +0 -2
  488. package/dist/test/history-module-ux.test.d.ts.map +0 -1
  489. package/dist/test/history-module-ux.test.js +0 -304
  490. package/dist/test/history-module-ux.test.js.map +0 -1
  491. package/dist/test/history-module.test.d.ts +0 -2
  492. package/dist/test/history-module.test.d.ts.map +0 -1
  493. package/dist/test/history-module.test.js +0 -619
  494. package/dist/test/history-module.test.js.map +0 -1
  495. package/dist/test/history-semantic-review-findings.test.d.ts +0 -2
  496. package/dist/test/history-semantic-review-findings.test.d.ts.map +0 -1
  497. package/dist/test/history-semantic-review-findings.test.js +0 -338
  498. package/dist/test/history-semantic-review-findings.test.js.map +0 -1
  499. package/dist/test/history-semantic-sync-converges.test.d.ts +0 -2
  500. package/dist/test/history-semantic-sync-converges.test.d.ts.map +0 -1
  501. package/dist/test/history-semantic-sync-converges.test.js +0 -109
  502. package/dist/test/history-semantic-sync-converges.test.js.map +0 -1
  503. package/dist/test/host-quiesce.test.d.ts +0 -2
  504. package/dist/test/host-quiesce.test.d.ts.map +0 -1
  505. package/dist/test/host-quiesce.test.js +0 -988
  506. package/dist/test/host-quiesce.test.js.map +0 -1
  507. package/dist/test/inference-exhaustion-rewind.test.d.ts +0 -2
  508. package/dist/test/inference-exhaustion-rewind.test.d.ts.map +0 -1
  509. package/dist/test/inference-exhaustion-rewind.test.js +0 -387
  510. package/dist/test/inference-exhaustion-rewind.test.js.map +0 -1
  511. package/dist/test/inference-failure-bounds.test.d.ts +0 -2
  512. package/dist/test/inference-failure-bounds.test.d.ts.map +0 -1
  513. package/dist/test/inference-failure-bounds.test.js +0 -1202
  514. package/dist/test/inference-failure-bounds.test.js.map +0 -1
  515. package/dist/test/inference-failure-observability.test.d.ts +0 -2
  516. package/dist/test/inference-failure-observability.test.d.ts.map +0 -1
  517. package/dist/test/inference-failure-observability.test.js +0 -84
  518. package/dist/test/inference-failure-observability.test.js.map +0 -1
  519. package/dist/test/journal-tool.test.d.ts +0 -2
  520. package/dist/test/journal-tool.test.d.ts.map +0 -1
  521. package/dist/test/journal-tool.test.js +0 -33
  522. package/dist/test/journal-tool.test.js.map +0 -1
  523. package/dist/test/kv-unified-open-flight-retry.test.d.ts +0 -2
  524. package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +0 -1
  525. package/dist/test/kv-unified-open-flight-retry.test.js +0 -189
  526. package/dist/test/kv-unified-open-flight-retry.test.js.map +0 -1
  527. package/dist/test/kv-unified-wiring.test.d.ts +0 -2
  528. package/dist/test/kv-unified-wiring.test.d.ts.map +0 -1
  529. package/dist/test/kv-unified-wiring.test.js +0 -95
  530. package/dist/test/kv-unified-wiring.test.js.map +0 -1
  531. package/dist/test/late-events-after-stop.test.d.ts +0 -2
  532. package/dist/test/late-events-after-stop.test.d.ts.map +0 -1
  533. package/dist/test/late-events-after-stop.test.js +0 -122
  534. package/dist/test/late-events-after-stop.test.js.map +0 -1
  535. package/dist/test/liveness-watchdog.test.d.ts +0 -2
  536. package/dist/test/liveness-watchdog.test.d.ts.map +0 -1
  537. package/dist/test/liveness-watchdog.test.js +0 -94
  538. package/dist/test/liveness-watchdog.test.js.map +0 -1
  539. package/dist/test/machine-close-provenance.test.d.ts +0 -2
  540. package/dist/test/machine-close-provenance.test.d.ts.map +0 -1
  541. package/dist/test/machine-close-provenance.test.js +0 -116
  542. package/dist/test/machine-close-provenance.test.js.map +0 -1
  543. package/dist/test/maintenance-timer.test.d.ts +0 -2
  544. package/dist/test/maintenance-timer.test.d.ts.map +0 -1
  545. package/dist/test/maintenance-timer.test.js +0 -1827
  546. package/dist/test/maintenance-timer.test.js.map +0 -1
  547. package/dist/test/mcp-server-bin.test.d.ts +0 -2
  548. package/dist/test/mcp-server-bin.test.d.ts.map +0 -1
  549. package/dist/test/mcp-server-bin.test.js +0 -142
  550. package/dist/test/mcp-server-bin.test.js.map +0 -1
  551. package/dist/test/mcpl-access-provider.test.d.ts +0 -2
  552. package/dist/test/mcpl-access-provider.test.d.ts.map +0 -1
  553. package/dist/test/mcpl-access-provider.test.js +0 -20
  554. package/dist/test/mcpl-access-provider.test.js.map +0 -1
  555. package/dist/test/mcpl-awareness-barrier.test.d.ts +0 -2
  556. package/dist/test/mcpl-awareness-barrier.test.d.ts.map +0 -1
  557. package/dist/test/mcpl-awareness-barrier.test.js +0 -769
  558. package/dist/test/mcpl-awareness-barrier.test.js.map +0 -1
  559. package/dist/test/mcpl-capability-grant.test.d.ts +0 -10
  560. package/dist/test/mcpl-capability-grant.test.d.ts.map +0 -1
  561. package/dist/test/mcpl-capability-grant.test.js +0 -111
  562. package/dist/test/mcpl-capability-grant.test.js.map +0 -1
  563. package/dist/test/mcpl-capability-scoping.test.d.ts +0 -2
  564. package/dist/test/mcpl-capability-scoping.test.d.ts.map +0 -1
  565. package/dist/test/mcpl-capability-scoping.test.js +0 -266
  566. package/dist/test/mcpl-capability-scoping.test.js.map +0 -1
  567. package/dist/test/mcpl-child-env.test.d.ts +0 -2
  568. package/dist/test/mcpl-child-env.test.d.ts.map +0 -1
  569. package/dist/test/mcpl-child-env.test.js +0 -158
  570. package/dist/test/mcpl-child-env.test.js.map +0 -1
  571. package/dist/test/mcpl-coalesced-content-parity.test.d.ts +0 -2
  572. package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +0 -1
  573. package/dist/test/mcpl-coalesced-content-parity.test.js +0 -532
  574. package/dist/test/mcpl-coalesced-content-parity.test.js.map +0 -1
  575. package/dist/test/mcpl-coalescing.test.d.ts +0 -2
  576. package/dist/test/mcpl-coalescing.test.d.ts.map +0 -1
  577. package/dist/test/mcpl-coalescing.test.js +0 -1125
  578. package/dist/test/mcpl-coalescing.test.js.map +0 -1
  579. package/dist/test/mcpl-config-diagnostics.test.d.ts +0 -2
  580. package/dist/test/mcpl-config-diagnostics.test.d.ts.map +0 -1
  581. package/dist/test/mcpl-config-diagnostics.test.js +0 -452
  582. package/dist/test/mcpl-config-diagnostics.test.js.map +0 -1
  583. package/dist/test/mcpl-empty-content.test.d.ts +0 -2
  584. package/dist/test/mcpl-empty-content.test.d.ts.map +0 -1
  585. package/dist/test/mcpl-empty-content.test.js +0 -368
  586. package/dist/test/mcpl-empty-content.test.js.map +0 -1
  587. package/dist/test/mcpl-gate-roundtrip.test.d.ts +0 -2
  588. package/dist/test/mcpl-gate-roundtrip.test.d.ts.map +0 -1
  589. package/dist/test/mcpl-gate-roundtrip.test.js +0 -285
  590. package/dist/test/mcpl-gate-roundtrip.test.js.map +0 -1
  591. package/dist/test/mcpl-list-status.test.d.ts +0 -2
  592. package/dist/test/mcpl-list-status.test.d.ts.map +0 -1
  593. package/dist/test/mcpl-list-status.test.js +0 -82
  594. package/dist/test/mcpl-list-status.test.js.map +0 -1
  595. package/dist/test/mcpl-manifest-changed.test.d.ts +0 -2
  596. package/dist/test/mcpl-manifest-changed.test.d.ts.map +0 -1
  597. package/dist/test/mcpl-manifest-changed.test.js +0 -178
  598. package/dist/test/mcpl-manifest-changed.test.js.map +0 -1
  599. package/dist/test/mcpl-push-convert.test.d.ts +0 -2
  600. package/dist/test/mcpl-push-convert.test.d.ts.map +0 -1
  601. package/dist/test/mcpl-push-convert.test.js +0 -42
  602. package/dist/test/mcpl-push-convert.test.js.map +0 -1
  603. package/dist/test/mcpl-reconnect-events.test.d.ts +0 -2
  604. package/dist/test/mcpl-reconnect-events.test.d.ts.map +0 -1
  605. package/dist/test/mcpl-reconnect-events.test.js +0 -144
  606. package/dist/test/mcpl-reconnect-events.test.js.map +0 -1
  607. package/dist/test/mcpl-reconnect-reregistration.test.d.ts +0 -2
  608. package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +0 -1
  609. package/dist/test/mcpl-reconnect-reregistration.test.js +0 -238
  610. package/dist/test/mcpl-reconnect-reregistration.test.js.map +0 -1
  611. package/dist/test/mcpl-reference-fetcher.test.d.ts +0 -2
  612. package/dist/test/mcpl-reference-fetcher.test.d.ts.map +0 -1
  613. package/dist/test/mcpl-reference-fetcher.test.js +0 -427
  614. package/dist/test/mcpl-reference-fetcher.test.js.map +0 -1
  615. package/dist/test/mcpl-reference-fields.test.d.ts +0 -2
  616. package/dist/test/mcpl-reference-fields.test.d.ts.map +0 -1
  617. package/dist/test/mcpl-reference-fields.test.js +0 -149
  618. package/dist/test/mcpl-reference-fields.test.js.map +0 -1
  619. package/dist/test/mcpl-rejection-log-lines.test.d.ts +0 -2
  620. package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +0 -1
  621. package/dist/test/mcpl-rejection-log-lines.test.js +0 -349
  622. package/dist/test/mcpl-rejection-log-lines.test.js.map +0 -1
  623. package/dist/test/mcpl-request-timeout.test.d.ts +0 -2
  624. package/dist/test/mcpl-request-timeout.test.d.ts.map +0 -1
  625. package/dist/test/mcpl-request-timeout.test.js +0 -138
  626. package/dist/test/mcpl-request-timeout.test.js.map +0 -1
  627. package/dist/test/mcpl-runtime-lifecycle.test.d.ts +0 -7
  628. package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +0 -1
  629. package/dist/test/mcpl-runtime-lifecycle.test.js +0 -142
  630. package/dist/test/mcpl-runtime-lifecycle.test.js.map +0 -1
  631. package/dist/test/mcpl-stderr-tail.test.d.ts +0 -2
  632. package/dist/test/mcpl-stderr-tail.test.d.ts.map +0 -1
  633. package/dist/test/mcpl-stderr-tail.test.js +0 -210
  634. package/dist/test/mcpl-stderr-tail.test.js.map +0 -1
  635. package/dist/test/mcpl-tool-class-listing.test.d.ts +0 -2
  636. package/dist/test/mcpl-tool-class-listing.test.d.ts.map +0 -1
  637. package/dist/test/mcpl-tool-class-listing.test.js +0 -147
  638. package/dist/test/mcpl-tool-class-listing.test.js.map +0 -1
  639. package/dist/test/mcpl-tool-classes.test.d.ts +0 -7
  640. package/dist/test/mcpl-tool-classes.test.d.ts.map +0 -1
  641. package/dist/test/mcpl-tool-classes.test.js +0 -80
  642. package/dist/test/mcpl-tool-classes.test.js.map +0 -1
  643. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts +0 -2
  644. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts.map +0 -1
  645. package/dist/test/mcpl-tool-lifecycle-e2e.test.js +0 -237
  646. package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +0 -1
  647. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts +0 -11
  648. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts.map +0 -1
  649. package/dist/test/mcpl-tool-lifecycle-emitter.test.js +0 -412
  650. package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +0 -1
  651. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +0 -2
  652. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +0 -1
  653. package/dist/test/mcpl-tool-lifecycle-script.test.js +0 -288
  654. package/dist/test/mcpl-tool-lifecycle-script.test.js.map +0 -1
  655. package/dist/test/mcpl-tool-lifecycle.test.d.ts +0 -11
  656. package/dist/test/mcpl-tool-lifecycle.test.d.ts.map +0 -1
  657. package/dist/test/mcpl-tool-lifecycle.test.js +0 -270
  658. package/dist/test/mcpl-tool-lifecycle.test.js.map +0 -1
  659. package/dist/test/mcpl-transport-close.test.d.ts +0 -2
  660. package/dist/test/mcpl-transport-close.test.d.ts.map +0 -1
  661. package/dist/test/mcpl-transport-close.test.js +0 -373
  662. package/dist/test/mcpl-transport-close.test.js.map +0 -1
  663. package/dist/test/mcpl-transport-spawn.test.d.ts +0 -2
  664. package/dist/test/mcpl-transport-spawn.test.d.ts.map +0 -1
  665. package/dist/test/mcpl-transport-spawn.test.js +0 -58
  666. package/dist/test/mcpl-transport-spawn.test.js.map +0 -1
  667. package/dist/test/mcpl-ws-transport.test.d.ts +0 -2
  668. package/dist/test/mcpl-ws-transport.test.d.ts.map +0 -1
  669. package/dist/test/mcpl-ws-transport.test.js +0 -259
  670. package/dist/test/mcpl-ws-transport.test.js.map +0 -1
  671. package/dist/test/module-context-integration.test.d.ts +0 -18
  672. package/dist/test/module-context-integration.test.d.ts.map +0 -1
  673. package/dist/test/module-context-integration.test.js +0 -181
  674. package/dist/test/module-context-integration.test.js.map +0 -1
  675. package/dist/test/offline-recovery-branch.test.d.ts +0 -2
  676. package/dist/test/offline-recovery-branch.test.d.ts.map +0 -1
  677. package/dist/test/offline-recovery-branch.test.js +0 -570
  678. package/dist/test/offline-recovery-branch.test.js.map +0 -1
  679. package/dist/test/operator-surgery.test.d.ts +0 -2
  680. package/dist/test/operator-surgery.test.d.ts.map +0 -1
  681. package/dist/test/operator-surgery.test.js +0 -595
  682. package/dist/test/operator-surgery.test.js.map +0 -1
  683. package/dist/test/ops-alert.test.d.ts +0 -2
  684. package/dist/test/ops-alert.test.d.ts.map +0 -1
  685. package/dist/test/ops-alert.test.js +0 -153
  686. package/dist/test/ops-alert.test.js.map +0 -1
  687. package/dist/test/overbudget-drain-breaker.test.d.ts +0 -2
  688. package/dist/test/overbudget-drain-breaker.test.d.ts.map +0 -1
  689. package/dist/test/overbudget-drain-breaker.test.js +0 -198
  690. package/dist/test/overbudget-drain-breaker.test.js.map +0 -1
  691. package/dist/test/pack-installs-alone.test.d.ts +0 -2
  692. package/dist/test/pack-installs-alone.test.d.ts.map +0 -1
  693. package/dist/test/pack-installs-alone.test.js +0 -284
  694. package/dist/test/pack-installs-alone.test.js.map +0 -1
  695. package/dist/test/per-agent-delivery.test.d.ts +0 -2
  696. package/dist/test/per-agent-delivery.test.d.ts.map +0 -1
  697. package/dist/test/per-agent-delivery.test.js +0 -108
  698. package/dist/test/per-agent-delivery.test.js.map +0 -1
  699. package/dist/test/physical-window-restart.test.d.ts +0 -16
  700. package/dist/test/physical-window-restart.test.d.ts.map +0 -1
  701. package/dist/test/physical-window-restart.test.js +0 -487
  702. package/dist/test/physical-window-restart.test.js.map +0 -1
  703. package/dist/test/placed-reaction-baseline.test.d.ts +0 -2
  704. package/dist/test/placed-reaction-baseline.test.d.ts.map +0 -1
  705. package/dist/test/placed-reaction-baseline.test.js +0 -312
  706. package/dist/test/placed-reaction-baseline.test.js.map +0 -1
  707. package/dist/test/present-while-acting.test.d.ts +0 -19
  708. package/dist/test/present-while-acting.test.d.ts.map +0 -1
  709. package/dist/test/present-while-acting.test.js +0 -1649
  710. package/dist/test/present-while-acting.test.js.map +0 -1
  711. package/dist/test/prose-segments.test.d.ts +0 -2
  712. package/dist/test/prose-segments.test.d.ts.map +0 -1
  713. package/dist/test/prose-segments.test.js +0 -52
  714. package/dist/test/prose-segments.test.js.map +0 -1
  715. package/dist/test/prose-stream-router.test.d.ts +0 -2
  716. package/dist/test/prose-stream-router.test.d.ts.map +0 -1
  717. package/dist/test/prose-stream-router.test.js +0 -171
  718. package/dist/test/prose-stream-router.test.js.map +0 -1
  719. package/dist/test/provider-acceleration-cooldown.test.d.ts +0 -2
  720. package/dist/test/provider-acceleration-cooldown.test.d.ts.map +0 -1
  721. package/dist/test/provider-acceleration-cooldown.test.js +0 -435
  722. package/dist/test/provider-acceleration-cooldown.test.js.map +0 -1
  723. package/dist/test/provider-waits.test.d.ts +0 -2
  724. package/dist/test/provider-waits.test.d.ts.map +0 -1
  725. package/dist/test/provider-waits.test.js +0 -2259
  726. package/dist/test/provider-waits.test.js.map +0 -1
  727. package/dist/test/puppet-tool-call.test.d.ts +0 -2
  728. package/dist/test/puppet-tool-call.test.d.ts.map +0 -1
  729. package/dist/test/puppet-tool-call.test.js +0 -226
  730. package/dist/test/puppet-tool-call.test.js.map +0 -1
  731. package/dist/test/puppet-turn-reservation.test.d.ts +0 -2
  732. package/dist/test/puppet-turn-reservation.test.d.ts.map +0 -1
  733. package/dist/test/puppet-turn-reservation.test.js +0 -139
  734. package/dist/test/puppet-turn-reservation.test.js.map +0 -1
  735. package/dist/test/reaction-baseline-platform.test.d.ts +0 -2
  736. package/dist/test/reaction-baseline-platform.test.d.ts.map +0 -1
  737. package/dist/test/reaction-baseline-platform.test.js +0 -124
  738. package/dist/test/reaction-baseline-platform.test.js.map +0 -1
  739. package/dist/test/record-journal.test.d.ts +0 -6
  740. package/dist/test/record-journal.test.d.ts.map +0 -1
  741. package/dist/test/record-journal.test.js +0 -269
  742. package/dist/test/record-journal.test.js.map +0 -1
  743. package/dist/test/refusal-reaction-baseline.test.d.ts +0 -2
  744. package/dist/test/refusal-reaction-baseline.test.d.ts.map +0 -1
  745. package/dist/test/refusal-reaction-baseline.test.js +0 -81
  746. package/dist/test/refusal-reaction-baseline.test.js.map +0 -1
  747. package/dist/test/refusal-rewind.test.d.ts +0 -2
  748. package/dist/test/refusal-rewind.test.d.ts.map +0 -1
  749. package/dist/test/refusal-rewind.test.js +0 -117
  750. package/dist/test/refusal-rewind.test.js.map +0 -1
  751. package/dist/test/refusal-single-call.test.d.ts +0 -2
  752. package/dist/test/refusal-single-call.test.d.ts.map +0 -1
  753. package/dist/test/refusal-single-call.test.js +0 -177
  754. package/dist/test/refusal-single-call.test.js.map +0 -1
  755. package/dist/test/release-bump.test.d.ts +0 -2
  756. package/dist/test/release-bump.test.d.ts.map +0 -1
  757. package/dist/test/release-bump.test.js +0 -248
  758. package/dist/test/release-bump.test.js.map +0 -1
  759. package/dist/test/release-changelog.test.d.ts +0 -2
  760. package/dist/test/release-changelog.test.d.ts.map +0 -1
  761. package/dist/test/release-changelog.test.js +0 -159
  762. package/dist/test/release-changelog.test.js.map +0 -1
  763. package/dist/test/release-holds.test.d.ts +0 -2
  764. package/dist/test/release-holds.test.d.ts.map +0 -1
  765. package/dist/test/release-holds.test.js +0 -456
  766. package/dist/test/release-holds.test.js.map +0 -1
  767. package/dist/test/same-round-think-policy.test.d.ts +0 -2
  768. package/dist/test/same-round-think-policy.test.d.ts.map +0 -1
  769. package/dist/test/same-round-think-policy.test.js +0 -853
  770. package/dist/test/same-round-think-policy.test.js.map +0 -1
  771. package/dist/test/save-recent-image.test.d.ts +0 -26
  772. package/dist/test/save-recent-image.test.d.ts.map +0 -1
  773. package/dist/test/save-recent-image.test.js +0 -974
  774. package/dist/test/save-recent-image.test.js.map +0 -1
  775. package/dist/test/shutdown-errors.test.d.ts +0 -2
  776. package/dist/test/shutdown-errors.test.d.ts.map +0 -1
  777. package/dist/test/shutdown-errors.test.js +0 -662
  778. package/dist/test/shutdown-errors.test.js.map +0 -1
  779. package/dist/test/silent-heartbeat-rows.test.d.ts +0 -2
  780. package/dist/test/silent-heartbeat-rows.test.d.ts.map +0 -1
  781. package/dist/test/silent-heartbeat-rows.test.js +0 -451
  782. package/dist/test/silent-heartbeat-rows.test.js.map +0 -1
  783. package/dist/test/silent-heartbeat.test.d.ts +0 -2
  784. package/dist/test/silent-heartbeat.test.d.ts.map +0 -1
  785. package/dist/test/silent-heartbeat.test.js +0 -165
  786. package/dist/test/silent-heartbeat.test.js.map +0 -1
  787. package/dist/test/state-scaling-e2e.test.d.ts +0 -22
  788. package/dist/test/state-scaling-e2e.test.d.ts.map +0 -1
  789. package/dist/test/state-scaling-e2e.test.js +0 -206
  790. package/dist/test/state-scaling-e2e.test.js.map +0 -1
  791. package/dist/test/state-scaling.test.d.ts +0 -12
  792. package/dist/test/state-scaling.test.d.ts.map +0 -1
  793. package/dist/test/state-scaling.test.js +0 -251
  794. package/dist/test/state-scaling.test.js.map +0 -1
  795. package/dist/test/stop-with-host-operations.test.d.ts +0 -20
  796. package/dist/test/stop-with-host-operations.test.d.ts.map +0 -1
  797. package/dist/test/stop-with-host-operations.test.js +0 -1174
  798. package/dist/test/stop-with-host-operations.test.js.map +0 -1
  799. package/dist/test/stop-with-work-in-flight.test.d.ts +0 -23
  800. package/dist/test/stop-with-work-in-flight.test.d.ts.map +0 -1
  801. package/dist/test/stop-with-work-in-flight.test.js +0 -784
  802. package/dist/test/stop-with-work-in-flight.test.js.map +0 -1
  803. package/dist/test/surgery-marks-receipt.test.d.ts +0 -2
  804. package/dist/test/surgery-marks-receipt.test.d.ts.map +0 -1
  805. package/dist/test/surgery-marks-receipt.test.js +0 -540
  806. package/dist/test/surgery-marks-receipt.test.js.map +0 -1
  807. package/dist/test/thinking-token-stamp.test.d.ts +0 -12
  808. package/dist/test/thinking-token-stamp.test.d.ts.map +0 -1
  809. package/dist/test/thinking-token-stamp.test.js +0 -129
  810. package/dist/test/thinking-token-stamp.test.js.map +0 -1
  811. package/dist/test/timezone.test.d.ts +0 -2
  812. package/dist/test/timezone.test.d.ts.map +0 -1
  813. package/dist/test/timezone.test.js +0 -18
  814. package/dist/test/timezone.test.js.map +0 -1
  815. package/dist/test/tool-batch-hook.test.d.ts +0 -8
  816. package/dist/test/tool-batch-hook.test.d.ts.map +0 -1
  817. package/dist/test/tool-batch-hook.test.js +0 -275
  818. package/dist/test/tool-batch-hook.test.js.map +0 -1
  819. package/dist/test/tool-policy.test.d.ts +0 -2
  820. package/dist/test/tool-policy.test.d.ts.map +0 -1
  821. package/dist/test/tool-policy.test.js +0 -51
  822. package/dist/test/tool-policy.test.js.map +0 -1
  823. package/dist/test/tool-presentation.test.d.ts +0 -2
  824. package/dist/test/tool-presentation.test.d.ts.map +0 -1
  825. package/dist/test/tool-presentation.test.js +0 -268
  826. package/dist/test/tool-presentation.test.js.map +0 -1
  827. package/dist/test/tool-result-content.test.d.ts +0 -2
  828. package/dist/test/tool-result-content.test.d.ts.map +0 -1
  829. package/dist/test/tool-result-content.test.js +0 -37
  830. package/dist/test/tool-result-content.test.js.map +0 -1
  831. package/dist/test/tool-result-guard.test.d.ts +0 -2
  832. package/dist/test/tool-result-guard.test.d.ts.map +0 -1
  833. package/dist/test/tool-result-guard.test.js +0 -3729
  834. package/dist/test/tool-result-guard.test.js.map +0 -1
  835. package/dist/test/tool-result-history.test.d.ts +0 -7
  836. package/dist/test/tool-result-history.test.d.ts.map +0 -1
  837. package/dist/test/tool-result-history.test.js +0 -156
  838. package/dist/test/tool-result-history.test.js.map +0 -1
  839. package/dist/test/tool-result-spill.test.d.ts +0 -13
  840. package/dist/test/tool-result-spill.test.d.ts.map +0 -1
  841. package/dist/test/tool-result-spill.test.js +0 -586
  842. package/dist/test/tool-result-spill.test.js.map +0 -1
  843. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts +0 -2
  844. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts.map +0 -1
  845. package/dist/test/tool-wrapper-prose-guard-integration.test.js +0 -485
  846. package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +0 -1
  847. package/dist/test/tool-wrapper-prose-guard.test.d.ts +0 -2
  848. package/dist/test/tool-wrapper-prose-guard.test.d.ts.map +0 -1
  849. package/dist/test/tool-wrapper-prose-guard.test.js +0 -34
  850. package/dist/test/tool-wrapper-prose-guard.test.js.map +0 -1
  851. package/dist/test/tools-observe-parser-conformance.test.d.ts +0 -2
  852. package/dist/test/tools-observe-parser-conformance.test.d.ts.map +0 -1
  853. package/dist/test/tools-observe-parser-conformance.test.js +0 -129
  854. package/dist/test/tools-observe-parser-conformance.test.js.map +0 -1
  855. package/dist/test/trunk-channel-routing.test.d.ts +0 -19
  856. package/dist/test/trunk-channel-routing.test.d.ts.map +0 -1
  857. package/dist/test/trunk-channel-routing.test.js +0 -652
  858. package/dist/test/trunk-channel-routing.test.js.map +0 -1
  859. package/dist/test/tune-out-e2e.test.d.ts +0 -2
  860. package/dist/test/tune-out-e2e.test.d.ts.map +0 -1
  861. package/dist/test/tune-out-e2e.test.js +0 -142
  862. package/dist/test/tune-out-e2e.test.js.map +0 -1
  863. package/dist/test/tune-out-gate-composition.test.d.ts +0 -2
  864. package/dist/test/tune-out-gate-composition.test.d.ts.map +0 -1
  865. package/dist/test/tune-out-gate-composition.test.js +0 -221
  866. package/dist/test/tune-out-gate-composition.test.js.map +0 -1
  867. package/dist/test/tune-out-lifecycle.test.d.ts +0 -2
  868. package/dist/test/tune-out-lifecycle.test.d.ts.map +0 -1
  869. package/dist/test/tune-out-lifecycle.test.js +0 -121
  870. package/dist/test/tune-out-lifecycle.test.js.map +0 -1
  871. package/dist/test/turn-trigger-provenance.test.d.ts +0 -2
  872. package/dist/test/turn-trigger-provenance.test.d.ts.map +0 -1
  873. package/dist/test/turn-trigger-provenance.test.js +0 -137
  874. package/dist/test/turn-trigger-provenance.test.js.map +0 -1
  875. package/dist/test/unconvertible-tool-result.test.d.ts +0 -2
  876. package/dist/test/unconvertible-tool-result.test.d.ts.map +0 -1
  877. package/dist/test/unconvertible-tool-result.test.js +0 -358
  878. package/dist/test/unconvertible-tool-result.test.js.map +0 -1
  879. package/dist/test/unsent-compiles.test.d.ts +0 -2
  880. package/dist/test/unsent-compiles.test.d.ts.map +0 -1
  881. package/dist/test/unsent-compiles.test.js +0 -139
  882. package/dist/test/unsent-compiles.test.js.map +0 -1
  883. package/dist/test/usage-accounting.test.d.ts +0 -2
  884. package/dist/test/usage-accounting.test.d.ts.map +0 -1
  885. package/dist/test/usage-accounting.test.js +0 -13
  886. package/dist/test/usage-accounting.test.js.map +0 -1
  887. package/dist/test/usage-tracker.test.d.ts +0 -2
  888. package/dist/test/usage-tracker.test.d.ts.map +0 -1
  889. package/dist/test/usage-tracker.test.js +0 -129
  890. package/dist/test/usage-tracker.test.js.map +0 -1
  891. package/dist/test/utils-surface.test.d.ts +0 -2
  892. package/dist/test/utils-surface.test.d.ts.map +0 -1
  893. package/dist/test/utils-surface.test.js +0 -145
  894. package/dist/test/utils-surface.test.js.map +0 -1
  895. package/dist/test/workspace-branch-continuation.test.d.ts +0 -15
  896. package/dist/test/workspace-branch-continuation.test.d.ts.map +0 -1
  897. package/dist/test/workspace-branch-continuation.test.js +0 -183
  898. package/dist/test/workspace-branch-continuation.test.js.map +0 -1
  899. package/dist/test/workspace-materialize-freshness.test.d.ts +0 -16
  900. package/dist/test/workspace-materialize-freshness.test.d.ts.map +0 -1
  901. package/dist/test/workspace-materialize-freshness.test.js +0 -252
  902. package/dist/test/workspace-materialize-freshness.test.js.map +0 -1
  903. package/dist/test/workspace-path-containment.test.d.ts +0 -19
  904. package/dist/test/workspace-path-containment.test.d.ts.map +0 -1
  905. package/dist/test/workspace-path-containment.test.js +0 -91
  906. package/dist/test/workspace-path-containment.test.js.map +0 -1
  907. package/dist/test/workspace-read-file-from-disk.test.d.ts +0 -17
  908. package/dist/test/workspace-read-file-from-disk.test.d.ts.map +0 -1
  909. package/dist/test/workspace-read-file-from-disk.test.js +0 -174
  910. package/dist/test/workspace-read-file-from-disk.test.js.map +0 -1
  911. package/dist/test/workspace-read-image.test.d.ts +0 -2
  912. package/dist/test/workspace-read-image.test.d.ts.map +0 -1
  913. package/dist/test/workspace-read-image.test.js +0 -597
  914. package/dist/test/workspace-read-image.test.js.map +0 -1
  915. package/dist/test/workspace-read-text.test.d.ts +0 -2
  916. package/dist/test/workspace-read-text.test.d.ts.map +0 -1
  917. package/dist/test/workspace-read-text.test.js +0 -101
  918. package/dist/test/workspace-read-text.test.js.map +0 -1
  919. package/dist/test/workspace-restore-order.test.d.ts +0 -16
  920. package/dist/test/workspace-restore-order.test.d.ts.map +0 -1
  921. package/dist/test/workspace-restore-order.test.js +0 -140
  922. package/dist/test/workspace-restore-order.test.js.map +0 -1
  923. package/dist/test/workspace-three-way.test.d.ts +0 -12
  924. package/dist/test/workspace-three-way.test.d.ts.map +0 -1
  925. package/dist/test/workspace-three-way.test.js +0 -4094
  926. package/dist/test/workspace-three-way.test.js.map +0 -1
  927. package/dist/test/workspace-watcher.test.d.ts +0 -2
  928. package/dist/test/workspace-watcher.test.d.ts.map +0 -1
  929. package/dist/test/workspace-watcher.test.js +0 -281
  930. package/dist/test/workspace-watcher.test.js.map +0 -1
  931. package/dist/test/xml-round-persist.test.d.ts +0 -19
  932. package/dist/test/xml-round-persist.test.d.ts.map +0 -1
  933. package/dist/test/xml-round-persist.test.js +0 -235
  934. package/dist/test/xml-round-persist.test.js.map +0 -1
  935. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,974 +0,0 @@
1
- /**
2
- * `save_recent_image` provenance — issue #104.
3
- *
4
- * Tool-result images never reached the persisted window (history keeps a
5
- * text placeholder), so the recency scan walked past a snapshot the resident
6
- * had just seen and quietly saved an OLDER attachment under the snapshot's
7
- * filename. These tests pin the repaired contract:
8
- *
9
- * 1. index 0 after a tool image IS that tool image, byte-exact, even with
10
- * an older attachment in context; index 1 is the attachment.
11
- * 2. when the bytes behind the newest image are gone, the save fails at
12
- * that index and writes nothing — never the older attachment.
13
- * 3. placeholders written before retention existed are unsaveable slots,
14
- * not skipped ones.
15
- * 4. `ref` saves by provenance; an unknown ref is a defined miss.
16
- * 5. receipts carry source, tool call, MIME, size and SHA-256.
17
- *
18
- * Second review round (#140) added three more representations of the same
19
- * failure, each pinned below:
20
- * 6. truncation/spill never drops an image slot from the stored text.
21
- * 7. a save dispatched in the same batch as the snapshot waits for it.
22
- * 8. refs are namespaced per ledger — a stale ref resolves to nothing,
23
- * and a direct `ref` is saved through its occurrence in context.
24
- */
25
- import { describe, it } from 'node:test';
26
- import assert from 'node:assert';
27
- import { createHash } from 'node:crypto';
28
- import { mkdtempSync, mkdirSync, rmSync } from 'node:fs';
29
- import { tmpdir } from 'node:os';
30
- import { join } from 'node:path';
31
- import { AgentFramework } from '../src/index.js';
32
- import { WorkspaceModule } from '../src/modules/workspace/index.js';
33
- import { FALLBACK_MEDIA_TYPE, TOOL_IMAGE_REF_RE, ToolImageLedger, formatPreservedImageSlots, formatToolImagePlaceholder, normalizeMediaType, parseImagePlaceholders, splitPreservingImageSlots, } from '../src/tool-image-ledger.js';
34
- import { toolResultDataToHistoryString, truncateForHistory } from '../src/tool-result-history.js';
35
- import { classifyBlock, referenceRegistry } from '../src/mcpl/references.js';
36
- import { createMockResponse, MockMembrane } from './helpers/mock-membrane.js';
37
- const PNG = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4////fwAJ+wP9KobjigAAAABJRU5ErkJggg==', 'base64');
38
- const GIF = Buffer.from('R0lGODdhAQABAIEAAP///wAAAAAAAAAAACwAAAAAAQABAAAIBAABBAQAOw==', 'base64');
39
- const sha = (b) => createHash('sha256').update(b).digest('hex');
40
- const REF_1 = /^img_[0-9a-z]+_1$/;
41
- /** ~30k chars — past the smallest allowed inline cap (1000) many times over. */
42
- const LONG_TEXT = 'line of scan output\n'.repeat(1500);
43
- /** World module: `snap` returns a native PNG image block; the other tools
44
- * are the review's representations of the same result, one shape each. */
45
- class SnapModule {
46
- name = 'world';
47
- /** The `quote` tool cites this ref — set by the test once it knows it. */
48
- quotedRef = 'img_zzzzzz_1';
49
- /** Delay before `snap` resolves — proves the same-batch barrier waits. */
50
- snapDelayMs = 0;
51
- async start() { }
52
- async stop() { }
53
- getTools() {
54
- const schema = { type: 'object', properties: {} };
55
- return [
56
- { name: 'snap', description: 'Take a picture.', inputSchema: schema },
57
- { name: 'quote', description: 'Return channel text verbatim.', inputSchema: schema },
58
- { name: 'snap_long', description: 'Long scan then a picture.', inputSchema: schema },
59
- { name: 'snap_sandwich', description: 'Picture, long scan, picture.', inputSchema: schema },
60
- { name: 'snap_contradiction', description: 'RFC-005 vector-2 image.', inputSchema: schema },
61
- { name: 'snap_uri', description: 'RFC-005 reference image.', inputSchema: schema },
62
- ];
63
- }
64
- async handleToolCall(call) {
65
- const png = { type: 'image', data: PNG.toString('base64'), mimeType: 'image/png' };
66
- switch (call.name) {
67
- case 'quote':
68
- // Someone pasted their own save receipt into a channel; a history
69
- // fetch now carries a live-looking placeholder citing a REAL ref.
70
- return {
71
- success: true,
72
- data: [{ type: 'text', text: `antra: look what I saved\n[image: image/png, 68B, ref ${this.quotedRef}]\n` }],
73
- };
74
- case 'snap_long':
75
- return { success: true, data: [{ type: 'text', text: LONG_TEXT }, png] };
76
- case 'snap_sandwich':
77
- return {
78
- success: true,
79
- data: [png, { type: 'text', text: LONG_TEXT }, { type: 'image', data: GIF.toString('base64'), mimeType: 'image/gif' }],
80
- };
81
- case 'snap_contradiction':
82
- // Inline data claiming a disposition: withheld by every lane.
83
- return { success: true, data: [{ type: 'text', text: 'cam' }, { ...png, disposition: 'ref' }] };
84
- case 'snap_uri':
85
- return {
86
- success: true,
87
- data: [{ type: 'text', text: 'cam' }, { type: 'image', uri: 'https://cam.example/latest.png', mimeType: 'image/png', sizeBytes: 1200 }],
88
- };
89
- default:
90
- if (this.snapDelayMs > 0)
91
- await new Promise((r) => setTimeout(r, this.snapDelayMs));
92
- return { success: true, data: [{ type: 'text', text: 'watchtower, facing north' }, png] };
93
- }
94
- }
95
- async onProcess(event) {
96
- if (event.type === 'external-message') {
97
- return {
98
- addMessages: [{ participant: 'User', content: event.content }],
99
- requestInference: true,
100
- };
101
- }
102
- return {};
103
- }
104
- }
105
- /**
106
- * Boot a framework whose agent already has an OLDER GIF attachment in
107
- * context, then run one turn scripted by `responses` (tool_use rounds, then
108
- * an end_turn). The GIF is the "unrelated image from another surface" of the
109
- * incident; the PNG the `world--snap` tool returns is the snapshot.
110
- */
111
- async function startTurn(opts) {
112
- const tempDir = mkdtempSync(join(tmpdir(), opts.prefix));
113
- const mountDir = join(tempDir, 'mount');
114
- mkdirSync(mountDir, { recursive: true });
115
- const membrane = new MockMembrane();
116
- for (const content of opts.responses) {
117
- const hasToolUse = content.some((b) => b.type === 'tool_use');
118
- membrane.pushResponse(createMockResponse(content, hasToolUse ? 'tool_use' : 'end_turn'));
119
- }
120
- const workspace = new WorkspaceModule({
121
- mounts: [{ name: 'files', path: mountDir, mode: 'read-write', watch: 'never' }],
122
- });
123
- const world = new SnapModule();
124
- opts.world?.(world);
125
- const framework = await AgentFramework.create({
126
- storePath: join(tempDir, 'store'),
127
- membrane: membrane.asMembrane(),
128
- agents: [{ name: 'prime', model: 'test-model', systemPrompt: 'You are prime.', allowedTools: 'all' }],
129
- modules: [world, workspace],
130
- syncIntervalMs: 0,
131
- ...(opts.toolResultInlineMaxChars !== undefined ? { toolResultInlineMaxChars: opts.toolResultInlineMaxChars } : {}),
132
- });
133
- workspace.initStore(framework.getStore());
134
- // Install the ledger up front so tests know the nonce its refs will carry.
135
- const ledger = opts.ledger ?? new ToolImageLedger();
136
- framework
137
- .toolImageLedgers.set('prime', ledger);
138
- // The older attachment: a GIF the resident saw in an ordinary message.
139
- framework.getAgent('prime').getContextManager().addMessage('user', [
140
- { type: 'text', text: 'here is my avatar' },
141
- { type: 'image', source: { type: 'base64', data: GIF.toString('base64'), mediaType: 'image/gif' } },
142
- ]);
143
- opts.seedHistory?.(framework);
144
- opts.workspace?.(workspace);
145
- framework.start();
146
- framework.pushEvent({
147
- type: 'external-message',
148
- source: 'test',
149
- content: [{ type: 'text', text: 'take a picture and save it' }],
150
- metadata: {},
151
- triggerInference: true,
152
- });
153
- return { framework, membrane, workspace, world, ledger, mountDir, tempDir };
154
- }
155
- const refOf = (h, seq) => `img_${h.ledger.nonce}_${seq}`;
156
- /** Stored tool_result for `callId`, once the framework has committed it. */
157
- async function waitForToolResult(framework, callId, timeoutMs = 20_000) {
158
- const deadline = Date.now() + timeoutMs;
159
- while (Date.now() < deadline) {
160
- await new Promise((r) => setTimeout(r, 50));
161
- const cm = framework.getAgent('prime')?.getContextManager();
162
- const msgs = (cm?.queryMessages({}).messages ?? []);
163
- for (const m of msgs) {
164
- for (const b of m.content ?? []) {
165
- if (b.type === 'tool_result' && b.toolUseId === callId) {
166
- return { content: b.content, isError: b.isError === true };
167
- }
168
- }
169
- }
170
- }
171
- throw new Error(`no stored tool_result for ${callId} within ${timeoutMs}ms`);
172
- }
173
- /** Bytes at a mount path via the workspace's own read path (tree-first), or null. */
174
- async function fileBytes(h, path) {
175
- const read = await h.workspace.readBinary(path);
176
- return 'data' in read ? read.data : null;
177
- }
178
- function saved(result) {
179
- assert.strictEqual(result.isError, false, `expected success, got: ${result.content}`);
180
- const parsed = JSON.parse(result.content);
181
- return parsed.saved;
182
- }
183
- const snapCall = (id) => ({ type: 'tool_use', id, name: 'world--snap', input: {} });
184
- const saveCall = (id, input) => ({ type: 'tool_use', id, name: 'save_recent_image', input });
185
- const done = [{ type: 'text', text: 'Done.' }];
186
- describe('save_recent_image provenance (issue #104)', () => {
187
- it('index 0 after a tool image saves THAT image byte-exactly; index 1 is the older attachment', async () => {
188
- const h = await startTurn({
189
- prefix: 'sri-order-',
190
- responses: [
191
- [snapCall('call_snap')],
192
- [saveCall('call_save0', { path: 'files/watchtower.png', index: 0 })],
193
- [saveCall('call_save1', { path: 'files/avatar.gif', index: 1 })],
194
- done,
195
- ],
196
- });
197
- try {
198
- const snap = await waitForToolResult(h.framework, 'call_snap');
199
- assert.match(snap.content, /\[image: image\/png, \d+B, ref img_[0-9a-z]+_1\]/, 'placeholder carries the ref');
200
- assert.ok(!snap.content.includes(PNG.toString('base64')), 'history never holds the base64');
201
- const first = saved(await waitForToolResult(h.framework, 'call_save0'));
202
- assert.strictEqual(first.length, 1);
203
- assert.strictEqual(first[0].source, 'tool-result');
204
- assert.strictEqual(first[0].ref, refOf(h, 1));
205
- assert.strictEqual(first[0].toolName, 'world--snap');
206
- assert.strictEqual(first[0].toolCallId, 'call_snap');
207
- assert.strictEqual(first[0].mediaType, 'image/png');
208
- assert.strictEqual(first[0].byteSize, PNG.byteLength);
209
- assert.strictEqual(first[0].sha256, sha(PNG));
210
- assert.strictEqual(first[0].imageIndex, 0);
211
- assert.ok((await fileBytes(h, 'files/watchtower.png'))?.equals(PNG), 'saved bytes are the snapshot');
212
- const second = saved(await waitForToolResult(h.framework, 'call_save1'));
213
- assert.strictEqual(second[0].source, 'attachment');
214
- assert.strictEqual(second[0].sha256, sha(GIF));
215
- assert.strictEqual(second[0].imageIndex, 1);
216
- assert.ok((await fileBytes(h, 'files/avatar.gif'))?.equals(GIF), 'index 1 is the attachment');
217
- }
218
- finally {
219
- await h.framework.stop();
220
- rmSync(h.tempDir, { recursive: true, force: true });
221
- }
222
- });
223
- it('fails at the exact index when the newest image was evicted — never saves the older attachment', async () => {
224
- // A ledger that holds nothing: every retained image is evicted at once,
225
- // which is what a restart or a blown budget looks like from the scan.
226
- const h = await startTurn({
227
- prefix: 'sri-evicted-',
228
- responses: [
229
- [snapCall('call_snap')],
230
- [saveCall('call_save', { path: 'files/watchtower.png', index: 0 })],
231
- done,
232
- ],
233
- ledger: new ToolImageLedger({ maxEntries: 0 }),
234
- });
235
- try {
236
- const snap = await waitForToolResult(h.framework, 'call_snap');
237
- assert.match(snap.content, /ref img_[0-9a-z]+_1\]/, 'ref is still issued so the placeholder is honest');
238
- const result = await waitForToolResult(h.framework, 'call_save');
239
- assert.strictEqual(result.isError, true);
240
- assert.match(result.content, new RegExp(`image at index 0 \\(${refOf(h, 1)}, from world--snap \\(call call_snap\\)\\) is no longer retained`));
241
- assert.match(result.content, /evicted/);
242
- assert.match(result.content, /Nothing written/);
243
- assert.ok((await fileBytes(h, 'files/watchtower.png')) === null, 'no file under the snapshot name');
244
- assert.ok((await fileBytes(h, 'files/avatar.gif')) === null);
245
- }
246
- finally {
247
- await h.framework.stop();
248
- rmSync(h.tempDir, { recursive: true, force: true });
249
- }
250
- });
251
- it('a placeholder from before retention existed is an unsaveable slot, not a skipped one', async () => {
252
- const h = await startTurn({
253
- prefix: 'sri-legacy-',
254
- responses: [
255
- [saveCall('call_save', { path: 'files/phoenix.png', index: 0 })],
256
- done,
257
- ],
258
- seedHistory: (framework) => {
259
- framework.getAgent('prime').getContextManager().addMessage('user', [{
260
- type: 'tool_result',
261
- toolUseId: 'call_old',
262
- toolName: 'world--snap',
263
- content: 'phoenix over the field\n[image: image/png, ~691KB]',
264
- isError: false,
265
- }]);
266
- },
267
- });
268
- try {
269
- const result = await waitForToolResult(h.framework, 'call_save');
270
- assert.strictEqual(result.isError, true);
271
- assert.match(result.content, /index 0 is a tool-result image from world--snap \(call call_old\) recorded without a retention ref/);
272
- assert.ok((await fileBytes(h, 'files/phoenix.png')) === null, 'the older GIF must not be written under the PNG name');
273
- }
274
- finally {
275
- await h.framework.stop();
276
- rmSync(h.tempDir, { recursive: true, force: true });
277
- }
278
- });
279
- it('`ref` saves by provenance; an unknown ref is a defined miss', async () => {
280
- const ledger = new ToolImageLedger();
281
- const ref1 = `img_${ledger.nonce}_1`;
282
- const h = await startTurn({
283
- prefix: 'sri-ref-',
284
- ledger,
285
- responses: [
286
- [snapCall('call_snap')],
287
- [saveCall('call_by_ref', { path: 'files/by-ref.png', ref: ref1 })],
288
- [saveCall('call_bad_ref', { path: 'files/ghost.png', ref: `img_${ledger.nonce}_42` })],
289
- [saveCall('call_bad_grammar', { path: 'files/ghost2.png', ref: 'img_1' })],
290
- [saveCall('call_mixed', { path: 'files/mixed.png', ref: ref1, index: 0 })],
291
- done,
292
- ],
293
- });
294
- try {
295
- const byRef = saved(await waitForToolResult(h.framework, 'call_by_ref'));
296
- assert.strictEqual(byRef[0].ref, ref1);
297
- assert.strictEqual(byRef[0].sha256, sha(PNG));
298
- assert.strictEqual(byRef[0].toolCallId, 'call_snap');
299
- assert.ok((await fileBytes(h, 'files/by-ref.png'))?.equals(PNG));
300
- const bad = await waitForToolResult(h.framework, 'call_bad_ref');
301
- assert.strictEqual(bad.isError, true);
302
- assert.match(bad.content, new RegExp(`ref img_${ledger.nonce}_42 cannot be saved — this process has no record of that ref`));
303
- assert.ok((await fileBytes(h, 'files/ghost.png')) === null);
304
- // The pre-nonce spelling is not a ref at all.
305
- const grammar = await waitForToolResult(h.framework, 'call_bad_grammar');
306
- assert.strictEqual(grammar.isError, true);
307
- assert.match(grammar.content, /`ref` must look like/);
308
- const mixed = await waitForToolResult(h.framework, 'call_mixed');
309
- assert.strictEqual(mixed.isError, true);
310
- assert.match(mixed.content, /mutually exclusive/);
311
- }
312
- finally {
313
- await h.framework.stop();
314
- rmSync(h.tempDir, { recursive: true, force: true });
315
- }
316
- });
317
- it('a placeholder quoted inside another tool result never resolves to that ref\'s bytes', async () => {
318
- // Refs are sequential and guessable; the quoted text cites the real
319
- // img_1. The slot must fail on provenance mismatch, and index 1 (the
320
- // genuine placeholder in the snap result) must still be the snapshot.
321
- const ledger = new ToolImageLedger();
322
- const h = await startTurn({
323
- prefix: 'sri-forged-',
324
- ledger,
325
- world: (world) => { world.quotedRef = `img_${ledger.nonce}_1`; },
326
- responses: [
327
- [snapCall('call_snap')],
328
- [{ type: 'tool_use', id: 'call_quote', name: 'world--quote', input: {} }],
329
- [saveCall('call_save0', { path: 'files/forged.png', index: 0 })],
330
- [saveCall('call_save1', { path: 'files/real.png', index: 1 })],
331
- done,
332
- ],
333
- });
334
- try {
335
- const quoted = await waitForToolResult(h.framework, 'call_quote');
336
- assert.match(quoted.content, new RegExp(`ref img_${ledger.nonce}_1\\]`), 'the quoted placeholder is in stored text');
337
- const forged = await waitForToolResult(h.framework, 'call_save0');
338
- assert.strictEqual(forged.isError, true);
339
- assert.match(forged.content, new RegExp(`index 0 cites img_${ledger.nonce}_1, but that image belongs to world--snap \\(call call_snap\\), not to world--quote \\(call call_quote\\)`));
340
- assert.match(forged.content, /quoted or forged/);
341
- assert.strictEqual(await fileBytes(h, 'files/forged.png'), null, 'nothing written under the forged slot');
342
- const real = saved(await waitForToolResult(h.framework, 'call_save1'));
343
- assert.strictEqual(real[0].toolCallId, 'call_snap');
344
- assert.strictEqual(real[0].sha256, sha(PNG));
345
- assert.ok((await fileBytes(h, 'files/real.png'))?.equals(PNG));
346
- }
347
- finally {
348
- await h.framework.stop();
349
- rmSync(h.tempDir, { recursive: true, force: true });
350
- }
351
- });
352
- it("a reference whose claimed type carries bidi controls is the image its stub shows, and its refusal shows the type without them", async () => {
353
- // The stub shows `Image/PNG`, so after a restart the stub's text alone
354
- // makes this a slot (matched without regard to case); the record must
355
- // say the same, or the index slides.
356
- const c = classifyBlock({ type: 'image', uri: 'https://example.invalid/sri-label.png', mimeType: '\u202EImage/PNG\u061C' });
357
- assert.strictEqual(c.kind, 'reference');
358
- const record = referenceRegistry.register(c.testimony);
359
- const h = await startTurn({
360
- prefix: 'sri-reference-label-',
361
- responses: [[saveCall('call_save', { path: 'files/label.png', index: 0 })], done],
362
- seedHistory: (framework) => {
363
- framework.getAgent('prime').getContextManager().addMessage('user', [{
364
- type: 'tool_result',
365
- toolUseId: 'call_cam',
366
- toolName: 'mcpl--vst--camera',
367
- content: `[${record.refId}] label.png — Image/PNG, 1.2MB claimed — from tool result — fetch with fetch_reference`,
368
- isError: false,
369
- }]);
370
- },
371
- });
372
- try {
373
- const result = await waitForToolResult(h.framework, 'call_save');
374
- assert.strictEqual(result.isError, true, `index 0 is the reference, not the older GIF: ${result.content}`);
375
- assert.match(result.content, new RegExp(`index 0 is a reference \\(${record.refId}, image/png, from`));
376
- assert.ok(!/[\u061C\u202E]/.test(result.content), result.content);
377
- assert.strictEqual(await fileBytes(h, 'files/label.png'), null);
378
- }
379
- finally {
380
- await h.framework.stop();
381
- rmSync(h.tempDir, { recursive: true, force: true });
382
- }
383
- });
384
- it('an expired reference refuses with its reason; one with a saved copy still points at fetch_reference', async () => {
385
- const register = (name) => {
386
- const c = classifyBlock({ type: 'image', uri: `https://example.invalid/${name}`, mimeType: 'image/png', expiresAt: '2001-01-01T00:00:00Z' });
387
- assert.strictEqual(c.kind, 'reference');
388
- return referenceRegistry.register(c.testimony);
389
- };
390
- const kept = register('sri-expired-kept.png');
391
- kept.fetchedPath = 'files/refs/kept.png'; // saved before its expiry passed: fetch_reference serves it
392
- const expired = register('sri-expired.png');
393
- const h = await startTurn({
394
- prefix: 'sri-reference-expired-',
395
- responses: [
396
- [saveCall('call_save0', { path: 'files/expired.png', index: 0 })],
397
- [saveCall('call_save1', { path: 'files/kept.png', index: 1 })],
398
- done,
399
- ],
400
- seedHistory: (framework) => {
401
- framework.getAgent('prime').getContextManager().addMessage('user', [{
402
- type: 'tool_result',
403
- toolUseId: 'call_cam',
404
- toolName: 'mcpl--vst--camera',
405
- content: `[${kept.refId}] kept.png — image/png — from tool result — saved: files/refs/kept.png\n`
406
- + `[${expired.refId}] expired.png — image/png — from tool result — expired: its expiresAt has passed`,
407
- isError: false,
408
- }]);
409
- },
410
- });
411
- try {
412
- const refused = await waitForToolResult(h.framework, 'call_save0');
413
- assert.strictEqual(refused.isError, true);
414
- assert.match(refused.content, new RegExp(`index 0 is a reference \\(${expired.refId}, image/png, from`));
415
- assert.match(refused.content, /It can't be fetched either — expired: its expiresAt has passed\./);
416
- assert.ok(!refused.content.includes('fetch_reference'), refused.content);
417
- const keptRefusal = await waitForToolResult(h.framework, 'call_save1');
418
- assert.strictEqual(keptRefusal.isError, true);
419
- assert.match(keptRefusal.content, new RegExp(`index 1 is a reference \\(${kept.refId}, image/png, from`));
420
- assert.match(keptRefusal.content, /Fetch it with fetch_reference and save the materialized file instead\./);
421
- }
422
- finally {
423
- await h.framework.stop();
424
- rmSync(h.tempDir, { recursive: true, force: true });
425
- }
426
- });
427
- it("an attachment's media type is shown and stored as a label, and one with nothing left counts as absent", async () => {
428
- // The store keeps a declared type only for bytes it can't sniff as a
429
- // raster image, so these aren't images it knows. Oldest first: a type
430
- // with controls in it, one that sanitizes to nothing, and a url-source
431
- // image with no type at all (index 0: not inline, so not saveable).
432
- const bytes = (tag) => Buffer.from(`not a raster image ${tag}`).toString('base64');
433
- const h = await startTurn({
434
- prefix: 'sri-attachment-type-',
435
- responses: [[saveCall('call_save', { path: 'files/sent.bmp', index: 1, count: 2 })], done],
436
- seedHistory: (framework) => {
437
- framework.getAgent('prime').getContextManager().addMessage('user', [
438
- { type: 'image', source: { type: 'base64', data: bytes('a'), mediaType: 'image/\u202Ebmp\u0085' } },
439
- { type: 'image', source: { type: 'base64', data: bytes('b'), mediaType: '\u202E\u061C' } },
440
- { type: 'image', source: { type: 'url', url: 'https://example.invalid/sri-url.png' } },
441
- ]);
442
- },
443
- });
444
- try {
445
- const result = await waitForToolResult(h.framework, 'call_save');
446
- assert.ok(!/[\u0085\u061C\u202E]/.test(result.content), result.content);
447
- const receipts = saved(result);
448
- assert.deepStrictEqual(receipts.map((r) => [r.imageIndex, r.mediaType, r.mimeType]), [
449
- [1, 'image/png', 'image/png'],
450
- [2, 'image/bmp', 'image/bmp'],
451
- ]);
452
- }
453
- finally {
454
- await h.framework.stop();
455
- rmSync(h.tempDir, { recursive: true, force: true });
456
- }
457
- });
458
- it('a reference with no record shows its type bounded as a stub would, and says to re-run the tool', async () => {
459
- // A stub line the registry has no record of (after a restart, or quoted
460
- // text), whose type is far longer than a stub would show.
461
- const longType = `image/${'x'.repeat(300)}`;
462
- const h = await startTurn({
463
- prefix: 'sri-reference-unknown-',
464
- responses: [[saveCall('call_save', { path: 'files/long.png', index: 0 })], done],
465
- seedHistory: (framework) => {
466
- framework.getAgent('prime').getContextManager().addMessage('user', [{
467
- type: 'tool_result',
468
- toolUseId: 'call_cam',
469
- toolName: 'mcpl--vst--camera',
470
- content: `[ref_3_cc02] long.png — ${longType} — from tool result — fetch with fetch_reference`,
471
- isError: false,
472
- }]);
473
- },
474
- });
475
- try {
476
- const result = await waitForToolResult(h.framework, 'call_save');
477
- assert.strictEqual(result.isError, true);
478
- assert.ok(result.content.includes(`(ref_3_cc02, image/${'x'.repeat(113)}…, from`), result.content);
479
- assert.match(result.content, /holds no record of it .*If its tool can produce it again, re-run that tool for a fresh reference\./);
480
- assert.ok(!result.content.includes('Fetch it with fetch_reference'), result.content);
481
- }
482
- finally {
483
- await h.framework.stop();
484
- rmSync(h.tempDir, { recursive: true, force: true });
485
- }
486
- });
487
- it('an image-typed RFC-005 reference stub with no record still occupies a failing slot', async () => {
488
- const h = await startTurn({
489
- prefix: 'sri-reference-',
490
- responses: [
491
- [saveCall('call_save', { path: 'files/cam.png', index: 0 })],
492
- [saveCall('call_save_gif', { path: 'files/avatar.gif', index: 1 })],
493
- done,
494
- ],
495
- seedHistory: (framework) => {
496
- framework.getAgent('prime').getContextManager().addMessage('user', [{
497
- type: 'tool_result',
498
- toolUseId: 'call_cam',
499
- toolName: 'mcpl--vst--camera',
500
- content: '[ref_1_ab3d] cam.png — image/png, 1.2MB claimed — from tool result — fetch with fetch_reference\n'
501
- + '[ref_2_ff01] notes.txt — text/plain, 2.0KB claimed — from tool result — fetch with fetch_reference',
502
- isError: false,
503
- }]);
504
- },
505
- });
506
- try {
507
- const result = await waitForToolResult(h.framework, 'call_save');
508
- assert.strictEqual(result.isError, true);
509
- assert.match(result.content, /index 0 is a reference \(ref_1_ab3d, image\/png, from mcpl--vst--camera \(call call_cam\)\)/);
510
- assert.match(result.content, /holds no record of it/, 'no record here: fetch_reference could only call it unknown');
511
- assert.strictEqual(await fileBytes(h, 'files/cam.png'), null);
512
- // The text/plain reference is not an image slot: index 1 is the GIF.
513
- const gif = saved(await waitForToolResult(h.framework, 'call_save_gif'));
514
- assert.strictEqual(gif[0].source, 'attachment');
515
- assert.strictEqual(gif[0].sha256, sha(GIF));
516
- }
517
- finally {
518
- await h.framework.stop();
519
- rmSync(h.tempDir, { recursive: true, force: true });
520
- }
521
- });
522
- it('truncation past the inline cap keeps the image slot: index 0 is still the snapshot', async () => {
523
- // [30k text, image] at the smallest allowed cap. The wire delivered the
524
- // image; the stored text must still carry its slot after the cut.
525
- const h = await startTurn({
526
- prefix: 'sri-truncated-',
527
- toolResultInlineMaxChars: 1000,
528
- responses: [
529
- [{ type: 'tool_use', id: 'call_long', name: 'world--snap_long', input: {} }],
530
- [saveCall('call_save', { path: 'files/after-cut.png', index: 0 })],
531
- done,
532
- ],
533
- });
534
- try {
535
- const stored = await waitForToolResult(h.framework, 'call_long');
536
- assert.match(stored.content, /\[truncated — showing \d+ of \d+ chars/);
537
- assert.match(stored.content, /1 image slot\(s\) fell past the cut/);
538
- const slots = parseImagePlaceholders(stored.content);
539
- assert.strictEqual(slots.length, 1, `exactly one slot survives the cut: ${stored.content.slice(-300)}`);
540
- assert.strictEqual(slots[0].kind === 'inline' ? slots[0].ref : null, refOf(h, 1));
541
- const receipt = saved(await waitForToolResult(h.framework, 'call_save'));
542
- assert.strictEqual(receipt[0].toolCallId, 'call_long');
543
- assert.strictEqual(receipt[0].sha256, sha(PNG));
544
- assert.ok((await fileBytes(h, 'files/after-cut.png'))?.equals(PNG), 'the snapshot, not the older GIF');
545
- }
546
- finally {
547
- await h.framework.stop();
548
- rmSync(h.tempDir, { recursive: true, force: true });
549
- }
550
- });
551
- it('[image A, huge text, image B] truncated: index 0 is B, index 1 is A — order survives the cut', async () => {
552
- const h = await startTurn({
553
- prefix: 'sri-sandwich-',
554
- toolResultInlineMaxChars: 1000,
555
- responses: [
556
- [{ type: 'tool_use', id: 'call_sandwich', name: 'world--snap_sandwich', input: {} }],
557
- [saveCall('call_save_b', { path: 'files/b.gif', index: 0 })],
558
- [saveCall('call_save_a', { path: 'files/a.png', index: 1 })],
559
- done,
560
- ],
561
- });
562
- try {
563
- const stored = await waitForToolResult(h.framework, 'call_sandwich');
564
- assert.deepStrictEqual(parseImagePlaceholders(stored.content).map((p) => (p.kind === 'inline' ? p.ref : p.refId)), [refOf(h, 1), refOf(h, 2)], 'both slots, in the original order');
565
- const b = saved(await waitForToolResult(h.framework, 'call_save_b'));
566
- assert.strictEqual(b[0].sha256, sha(GIF));
567
- assert.strictEqual(b[0].ref, refOf(h, 2));
568
- const a = saved(await waitForToolResult(h.framework, 'call_save_a'));
569
- assert.strictEqual(a[0].sha256, sha(PNG));
570
- assert.strictEqual(a[0].ref, refOf(h, 1));
571
- }
572
- finally {
573
- await h.framework.stop();
574
- rmSync(h.tempDir, { recursive: true, force: true });
575
- }
576
- });
577
- it('a save dispatched in the SAME batch as the snapshot waits for it and saves it', async () => {
578
- // One tool_use round: [snap, save]. Module calls are only enqueued while
579
- // the save runs at once — without the barrier the inventory is empty at
580
- // scan time and the older GIF gets saved under the snapshot's name.
581
- const h = await startTurn({
582
- prefix: 'sri-same-batch-',
583
- world: (world) => { world.snapDelayMs = 150; },
584
- responses: [
585
- [snapCall('call_snap'), saveCall('call_save', { path: 'files/batched.png', index: 0 })],
586
- done,
587
- ],
588
- });
589
- try {
590
- const receipt = saved(await waitForToolResult(h.framework, 'call_save'));
591
- assert.strictEqual(receipt[0].source, 'tool-result');
592
- assert.strictEqual(receipt[0].toolCallId, 'call_snap');
593
- assert.strictEqual(receipt[0].sha256, sha(PNG));
594
- assert.ok((await fileBytes(h, 'files/batched.png'))?.equals(PNG));
595
- // Early retention and commit-time serialization agree on the ref.
596
- const snap = await waitForToolResult(h.framework, 'call_snap');
597
- assert.match(snap.content, new RegExp(`ref ${receipt[0].ref}\\]`));
598
- }
599
- finally {
600
- await h.framework.stop();
601
- rmSync(h.tempDir, { recursive: true, force: true });
602
- }
603
- });
604
- it('[save, snap] — a save dispatched BEFORE its sibling snapshot still waits for it (#140 follow-up)', async () => {
605
- // The mirror of the test above, in the order a model is at least as
606
- // likely to emit. The round loop dispatches synchronously, so the save
607
- // starts first and finds no snapshot in the inventory; the sibling
608
- // barrier's first poll must still yield to the snap that follows it in
609
- // the same batch. Pinned separately because it was verified by reading
610
- // at review time and never by a test.
611
- const h = await startTurn({
612
- prefix: 'sri-same-batch-rev-',
613
- world: (world) => { world.snapDelayMs = 150; },
614
- responses: [
615
- [saveCall('call_save', { path: 'files/batched-rev.png', index: 0 }), snapCall('call_snap')],
616
- done,
617
- ],
618
- });
619
- try {
620
- const receipt = saved(await waitForToolResult(h.framework, 'call_save'));
621
- assert.strictEqual(receipt[0].source, 'tool-result');
622
- assert.strictEqual(receipt[0].toolCallId, 'call_snap', 'the sibling snapshot, not the older attachment');
623
- assert.strictEqual(receipt[0].sha256, sha(PNG));
624
- assert.ok((await fileBytes(h, 'files/batched-rev.png'))?.equals(PNG));
625
- const snap = await waitForToolResult(h.framework, 'call_snap');
626
- assert.match(snap.content, new RegExp(`ref ${receipt[0].ref}\\]`));
627
- }
628
- finally {
629
- await h.framework.stop();
630
- rmSync(h.tempDir, { recursive: true, force: true });
631
- }
632
- });
633
- it('same-batch siblings are classified like the commit path: withheld data is no slot, a uri image is a reference slot', async () => {
634
- const h = await startTurn({
635
- prefix: 'sri-same-batch-class-',
636
- responses: [
637
- [
638
- { type: 'tool_use', id: 'call_contra', name: 'world--snap_contradiction', input: {} },
639
- saveCall('call_save_contra', { path: 'files/contra.png', index: 0 }),
640
- ],
641
- [
642
- { type: 'tool_use', id: 'call_uri', name: 'world--snap_uri', input: {} },
643
- saveCall('call_save_uri', { path: 'files/uri.png', index: 0 }),
644
- ],
645
- done,
646
- ],
647
- });
648
- try {
649
- // The withheld image is not a slot in either representation: index 0
650
- // is the GIF attachment, and its bytes never entered the ledger.
651
- const contra = saved(await waitForToolResult(h.framework, 'call_save_contra'));
652
- assert.strictEqual(contra[0].source, 'attachment');
653
- assert.strictEqual(contra[0].sha256, sha(GIF));
654
- assert.strictEqual(h.ledger.size, 0, 'withheld bytes must not be retained');
655
- const stored = await waitForToolResult(h.framework, 'call_contra');
656
- assert.strictEqual(parseImagePlaceholders(stored.content).length, 0);
657
- const uri = await waitForToolResult(h.framework, 'call_save_uri');
658
- assert.strictEqual(uri.isError, true);
659
- assert.match(uri.content, /index 0 is a reference \(ref_[0-9a-z]+_[0-9a-z]+, image\/png, from world--snap_uri \(call call_uri\)\)/);
660
- assert.strictEqual(await fileBytes(h, 'files/uri.png'), null);
661
- }
662
- finally {
663
- await h.framework.stop();
664
- rmSync(h.tempDir, { recursive: true, force: true });
665
- }
666
- });
667
- it('a placeholder from a previous process (other nonce) is a defined miss by index AND by ref', async () => {
668
- const h = await startTurn({
669
- prefix: 'sri-stale-',
670
- responses: [
671
- [saveCall('call_save_idx', { path: 'files/stale.png', index: 0 })],
672
- [saveCall('call_save_ref', { path: 'files/stale-ref.png', ref: 'img_zzzzzz_7' })],
673
- done,
674
- ],
675
- seedHistory: (framework) => {
676
- framework.getAgent('prime').getContextManager().addMessage('user', [{
677
- type: 'tool_result',
678
- toolUseId: 'call_yesterday',
679
- toolName: 'world--snap',
680
- content: 'yesterday\n[image: image/png, ~691KB, ref img_zzzzzz_7]',
681
- isError: false,
682
- }]);
683
- },
684
- });
685
- try {
686
- // Seven fresh images in this process: a bare `img_7` would collide.
687
- for (let i = 1; i <= 7; i++) {
688
- h.ledger.retain({ toolCallId: `call_today_${i}`, toolName: 'world--snap', blockIndex: 0, data: PNG.toString('base64'), mediaType: 'image/png' });
689
- }
690
- const byIndex = await waitForToolResult(h.framework, 'call_save_idx');
691
- assert.strictEqual(byIndex.isError, true);
692
- assert.match(byIndex.content, /index 0 \(img_zzzzzz_7, from world--snap \(call call_yesterday\)\) is no longer retained — this process has no record/);
693
- const byRef = await waitForToolResult(h.framework, 'call_save_ref');
694
- assert.strictEqual(byRef.isError, true);
695
- // The stale placeholder IS visible, so the ref resolves through it and fails the same way.
696
- assert.match(byRef.content, /img_zzzzzz_7, from world--snap \(call call_yesterday\)\) is no longer retained — this process has no record/);
697
- assert.strictEqual(await fileBytes(h, 'files/stale.png'), null);
698
- assert.strictEqual(await fileBytes(h, 'files/stale-ref.png'), null);
699
- }
700
- finally {
701
- await h.framework.stop();
702
- rmSync(h.tempDir, { recursive: true, force: true });
703
- }
704
- });
705
- it('a direct `ref` is saved through its placeholder in context — a retained ref that is not visible is refused', async () => {
706
- const ledger = new ToolImageLedger();
707
- const ghost = ledger.retain({ toolCallId: 'call_ghost', toolName: 'world--snap', blockIndex: 1, data: PNG.toString('base64'), mediaType: 'image/png' });
708
- const h = await startTurn({
709
- prefix: 'sri-ref-context-',
710
- ledger,
711
- responses: [
712
- [saveCall('call_save', { path: 'files/ghost.png', ref: ghost.ref })],
713
- done,
714
- ],
715
- });
716
- try {
717
- const result = await waitForToolResult(h.framework, 'call_save');
718
- assert.strictEqual(result.isError, true);
719
- assert.match(result.content, new RegExp(`ref ${ghost.ref} \\(from world--snap \\(call call_ghost\\)\\) does not appear in your recent context`));
720
- assert.strictEqual(await fileBytes(h, 'files/ghost.png'), null);
721
- }
722
- finally {
723
- await h.framework.stop();
724
- rmSync(h.tempDir, { recursive: true, force: true });
725
- }
726
- });
727
- });
728
- describe('ToolImageLedger', () => {
729
- it('mints one ref per (call, block), idempotently, with digest provenance', () => {
730
- const ledger = new ToolImageLedger();
731
- const a = ledger.retain({ toolCallId: 'c1', toolName: 't', blockIndex: 1, data: PNG.toString('base64'), mediaType: 'image/png' });
732
- const again = ledger.retain({ toolCallId: 'c1', toolName: 't', blockIndex: 1, data: PNG.toString('base64'), mediaType: 'image/png' });
733
- const b = ledger.retain({ toolCallId: 'c1', toolName: 't', blockIndex: 3, data: GIF.toString('base64'), mediaType: 'image/gif' });
734
- assert.match(a.ref, REF_1);
735
- assert.strictEqual(a.ref, `img_${ledger.nonce}_1`);
736
- assert.ok(TOOL_IMAGE_REF_RE.test(a.ref));
737
- assert.strictEqual(again.ref, a.ref);
738
- assert.strictEqual(b.ref, `img_${ledger.nonce}_2`);
739
- assert.strictEqual(a.sha256, sha(PNG));
740
- assert.strictEqual(a.byteSize, PNG.byteLength);
741
- assert.strictEqual(ledger.refFor('c1', 3), b.ref);
742
- const hit = ledger.lookup(a.ref);
743
- assert.strictEqual(hit.status, 'retained');
744
- assert.strictEqual(hit.status === 'retained' ? hit.image.data : null, PNG.toString('base64'));
745
- assert.strictEqual(ledger.lookup(`img_${ledger.nonce}_9`).status, 'unknown');
746
- // Another ledger is another namespace: the same sequence is a stranger.
747
- const other = new ToolImageLedger();
748
- assert.notStrictEqual(other.nonce, ledger.nonce);
749
- assert.strictEqual(other.lookup(a.ref).status, 'unknown');
750
- assert.strictEqual(ledger.lookup('img_1').status, 'unknown');
751
- });
752
- it('digests large payloads chunked with the same result as a whole decode', () => {
753
- const big = Buffer.alloc(1_500_000);
754
- for (let i = 0; i < big.length; i++)
755
- big[i] = (i * 7919) & 0xff;
756
- const ledger = new ToolImageLedger();
757
- const kept = ledger.retain({ toolCallId: 'c', toolName: 't', blockIndex: 0, data: big.toString('base64'), mediaType: 'image/png' });
758
- assert.strictEqual(kept.byteSize, big.byteLength);
759
- assert.strictEqual(kept.sha256, sha(big));
760
- // Whitespace-bearing base64 (legal for decoders) falls back to a whole decode — same answer.
761
- const wrapped = big.toString('base64').replace(/(.{76})/g, '$1\n');
762
- const same = ledger.retain({ toolCallId: 'c2', toolName: 't', blockIndex: 0, data: wrapped, mediaType: 'image/png' });
763
- assert.strictEqual(same.byteSize, Buffer.from(wrapped, 'base64').byteLength);
764
- assert.strictEqual(same.sha256, sha(Buffer.from(wrapped, 'base64')));
765
- });
766
- it('evicts oldest-first over the budget but keeps provenance for the error', () => {
767
- const ledger = new ToolImageLedger({ maxEntries: 2 });
768
- for (let i = 0; i < 3; i++) {
769
- ledger.retain({ toolCallId: `c${i}`, toolName: 'snap', blockIndex: 0, data: PNG.toString('base64'), mediaType: 'image/png' });
770
- }
771
- assert.strictEqual(ledger.size, 2);
772
- const evicted = ledger.lookup(`img_${ledger.nonce}_1`);
773
- assert.strictEqual(evicted.status, 'evicted');
774
- assert.strictEqual(evicted.status === 'evicted' ? evicted.image.toolCallId : null, 'c0');
775
- assert.strictEqual(ledger.lookup(`img_${ledger.nonce}_2`).status, 'retained');
776
- assert.strictEqual(ledger.lookup(`img_${ledger.nonce}_3`).status, 'retained');
777
- });
778
- it('placeholders round-trip through the serializer, with and without a ref', () => {
779
- const data = [
780
- { type: 'text', text: 'two frames' },
781
- { type: 'image', data: PNG.toString('base64'), mimeType: 'image/png' },
782
- { type: 'image', data: GIF.toString('base64'), mimeType: 'image/gif' },
783
- ];
784
- const legacy = toolResultDataToHistoryString(data);
785
- const refsOf = (text) => parseImagePlaceholders(text).map((p) => (p.kind === 'inline' ? p.ref : p.refId));
786
- assert.deepStrictEqual(parseImagePlaceholders(legacy).map((p) => [p.mediaType, p.kind === 'inline' ? p.ref : 'ref']), [
787
- ['image/png', null],
788
- ['image/gif', null],
789
- ]);
790
- const seen = [];
791
- const withRefs = toolResultDataToHistoryString(data, undefined, {
792
- imageRef: (blockIndex) => { seen.push(blockIndex); return `img_abc123_${blockIndex}`; },
793
- });
794
- assert.deepStrictEqual(seen, [1, 2], 'block indices are positions in the content array');
795
- // Size label is the serializer's base64-derived estimate, not the exact byte count.
796
- assert.match(withRefs, /\[image: image\/png, \d+B, ref img_abc123_1\]/);
797
- assert.strictEqual(formatToolImagePlaceholder('image/png', '~691KB', 'img_k7x3q2_7'), '[image: image/png, ~691KB, ref img_k7x3q2_7]');
798
- assert.deepStrictEqual(refsOf(withRefs), ['img_abc123_1', 'img_abc123_2']);
799
- assert.ok(!withRefs.includes(PNG.toString('base64')));
800
- });
801
- it('an uppercase mime from a tool still round-trips (normalized at retain and format time)', () => {
802
- const ledger = new ToolImageLedger();
803
- const retained = ledger.retain({ toolCallId: 'c', toolName: 't', blockIndex: 0, data: PNG.toString('base64'), mediaType: 'Image/PNG ' });
804
- assert.strictEqual(retained.mediaType, 'image/png');
805
- const text = toolResultDataToHistoryString([{ type: 'image', data: PNG.toString('base64'), mimeType: 'Image/PNG' }], undefined, { imageRef: () => retained.ref });
806
- const parsed = parseImagePlaceholders(text);
807
- assert.strictEqual(parsed.length, 1, `placeholder must re-parse: ${text}`);
808
- assert.strictEqual(parsed[0].kind === 'inline' ? parsed[0].ref : null, retained.ref);
809
- assert.strictEqual(parsed[0].mediaType, 'image/png');
810
- });
811
- it('any mime a tool sends normalizes to a type/subtype essence that round-trips', () => {
812
- assert.strictEqual(normalizeMediaType('image/png; charset=binary'), 'image/png');
813
- assert.strictEqual(normalizeMediaType(' IMAGE/JPEG '), 'image/jpeg');
814
- assert.strictEqual(normalizeMediaType('image/x_custom'), 'image/x_custom');
815
- assert.strictEqual(normalizeMediaType('image/svg+xml'), 'image/svg+xml');
816
- assert.strictEqual(normalizeMediaType('garbage'), FALLBACK_MEDIA_TYPE);
817
- assert.strictEqual(normalizeMediaType('image/has space'), FALLBACK_MEDIA_TYPE);
818
- assert.strictEqual(normalizeMediaType(''), FALLBACK_MEDIA_TYPE);
819
- // The invariant the formatter relies on: whatever comes in, the
820
- // placeholder written for it re-parses to the normalized mime.
821
- for (const raw of ['image/png; charset=binary', 'Image/PNG', 'image/x_custom', 'garbage', 'image/a,b', 'image/]', 'text/plain;x=1;y=2']) {
822
- const mime = normalizeMediaType(raw);
823
- const parsed = parseImagePlaceholders(formatToolImagePlaceholder(raw, '~1KB', 'img_abc123_1'));
824
- assert.strictEqual(parsed.length, 1, `must re-parse: ${raw}`);
825
- assert.strictEqual(parsed[0].mediaType, mime, raw);
826
- }
827
- });
828
- it('splitting for truncation keeps every slot at or past the cut, and never leaves a partial one in the head', () => {
829
- const png = '[image: image/png, ~12KB, ref img_abc123_1]';
830
- const stub = '[ref_1_ab3d] cam.png — image/jpeg, 1.2MB claimed — from tool result — fetch with fetch_reference';
831
- const text = `head text\n${png}\nmiddle text that is long enough\n${stub}\ntail\n[image: image/gif, 4B, ref img_abc123_2]`;
832
- // Cut exactly at the start of the middle text: png stays, stub + gif move.
833
- const cut1 = text.indexOf('middle');
834
- const a = splitPreservingImageSlots(text, cut1);
835
- assert.strictEqual(a.head, text.slice(0, cut1));
836
- assert.deepStrictEqual(a.tail, [stub, '[image: image/gif, 4B, ref img_abc123_2]']);
837
- // Cut in the middle of the stub line: the cut moves back to the stub's start.
838
- const cut2 = text.indexOf('1.2MB');
839
- const b = splitPreservingImageSlots(text, cut2);
840
- assert.strictEqual(b.head, text.slice(0, text.indexOf(stub)));
841
- assert.deepStrictEqual(b.tail, [stub, '[image: image/gif, 4B, ref img_abc123_2]']);
842
- // Cut in the middle of the png placeholder.
843
- const cut3 = text.indexOf('~12KB');
844
- const c = splitPreservingImageSlots(text, cut3);
845
- assert.strictEqual(c.head, 'head text\n');
846
- assert.strictEqual(c.tail.length, 3);
847
- // Head + preserved tail parse to exactly the original slots, in order.
848
- for (const cut of [cut1, cut2, cut3, 1, text.length - 3]) {
849
- const { head, tail } = splitPreservingImageSlots(text, cut);
850
- const rejoined = head + '\n\n[truncated — original was N chars]' + formatPreservedImageSlots(tail);
851
- assert.deepStrictEqual(parseImagePlaceholders(rejoined).map((p) => p.text), parseImagePlaceholders(text).map((p) => p.text), `cut at ${cut}`);
852
- }
853
- // The generic history truncation uses the same rule.
854
- const truncated = truncateForHistory(text, cut3);
855
- assert.strictEqual(parseImagePlaceholders(truncated).length, 3);
856
- assert.match(truncated, /\[truncated — original was \d+ chars\]/);
857
- });
858
- it('parses image-typed reference stubs as slots, in text order with inline placeholders', () => {
859
- const text = [
860
- 'frame one',
861
- '[image: image/png, ~12KB, ref img_abc123_3]',
862
- '[ref_1_ab3d] cam.png — image/jpeg, 1.2MB claimed — from tool result — fetch with fetch_reference',
863
- '[ref_2_zz9q] chord.wav — audio/wav, ~4.0MB claimed — from tool result — fetch with fetch_reference',
864
- ].join('\n');
865
- const slots = parseImagePlaceholders(text);
866
- assert.deepStrictEqual(slots.map((s) => (s.kind === 'inline' ? ['inline', s.ref, s.mediaType] : ['reference', s.refId, s.mediaType])), [
867
- ['inline', 'img_abc123_3', 'image/png'],
868
- ['reference', 'ref_1_ab3d', 'image/jpeg'],
869
- ['reference', 'ref_2_zz9q', null],
870
- ]);
871
- });
872
- });
873
- it('a write that throws before its tree entry names the images already saved', async () => {
874
- const h = await startTurn({
875
- prefix: 'sri-throw-',
876
- responses: [
877
- [snapCall('call_snap')],
878
- [saveCall('call_save', { path: 'files/images.png', index: 0, count: 2 })],
879
- done,
880
- ],
881
- workspace: (workspace) => {
882
- const write = workspace.writeBinary.bind(workspace);
883
- let writes = 0;
884
- workspace.writeBinary = async (...args) => {
885
- if (++writes === 2)
886
- throw new Error('injected tree exception');
887
- return write(...args);
888
- };
889
- },
890
- });
891
- try {
892
- const result = await waitForToolResult(h.framework, 'call_save');
893
- assert.equal(result.isError, true);
894
- assert.match(result.content, /injected tree exception/);
895
- assert.match(result.content, /already saved: files\/images-0.png/);
896
- }
897
- finally {
898
- await h.framework.stop();
899
- rmSync(h.tempDir, { recursive: true, force: true });
900
- }
901
- });
902
- it('a save whose write fails after its tree entry, through the workspace itself, is reported saved with its failure', async () => {
903
- const h = await startTurn({
904
- prefix: 'sri-tree-real-',
905
- responses: [
906
- [snapCall('call_snap')],
907
- [saveCall('call_save', { path: 'files/images.png', index: 0, count: 1 })],
908
- done,
909
- ],
910
- workspace: (workspace) => {
911
- const ws = workspace;
912
- const intents = [...ws.intents.values()][0];
913
- intents.update = () => { throw new Error('injected intent exception'); };
914
- },
915
- });
916
- try {
917
- const result = await waitForToolResult(h.framework, 'call_save');
918
- const files = saved(result);
919
- assert.equal(files.length, 1);
920
- assert.equal(files[0].inChronicle, true);
921
- assert.match(String(files[0].diskCopyError), /the rest of the write failed: injected intent exception/);
922
- assert.ok((await fileBytes(h, String(files[0].path)))?.equals(PNG));
923
- }
924
- finally {
925
- await h.framework.stop();
926
- rmSync(h.tempDir, { recursive: true, force: true });
927
- }
928
- });
929
- for (const secondFails of [false, true]) {
930
- it(`a tree-only image save ${secondFails ? 'appears in a later failure receipt' : 'succeeds with a disk-copy warning'}`, async () => {
931
- const h = await startTurn({
932
- prefix: 'sri-tree-only-',
933
- responses: [
934
- [snapCall('call_snap')],
935
- [saveCall('call_save', { path: 'files/images.png', index: 0, count: 2 })],
936
- done,
937
- ],
938
- workspace: (workspace) => {
939
- const write = workspace.writeBinary.bind(workspace);
940
- let writes = 0;
941
- workspace.writeBinary = async (...args) => {
942
- if (++writes === 2 && secondFails)
943
- return { success: false, error: 'tree unavailable', isError: true };
944
- const result = await write(...args);
945
- assert.equal(result.success, true);
946
- return { success: false, data: { inChronicle: true }, error: 'injected disk-copy failure', isError: true };
947
- };
948
- },
949
- });
950
- try {
951
- const result = await waitForToolResult(h.framework, 'call_save');
952
- if (secondFails) {
953
- assert.equal(result.isError, true);
954
- assert.match(result.content, /tree unavailable/);
955
- assert.match(result.content, /already saved: files\/images-0.png/);
956
- }
957
- else {
958
- const files = saved(result);
959
- assert.equal(files.length, 2);
960
- assert.equal(files[0].path, 'files/images-0.png');
961
- assert.equal(files[0].inChronicle, true);
962
- assert.match(String(files[0].diskCopyError), /injected disk-copy failure/);
963
- assert.equal(files[0].size, PNG.byteLength);
964
- assert.equal(files[0].mimeType, 'image/png');
965
- }
966
- assert.ok((await fileBytes(h, 'files/images-0.png'))?.equals(PNG));
967
- }
968
- finally {
969
- await h.framework.stop();
970
- rmSync(h.tempDir, { recursive: true, force: true });
971
- }
972
- });
973
- }
974
- //# sourceMappingURL=save-recent-image.test.js.map