@animalabs/agent-framework 0.22.2 → 0.22.4

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 (940) 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 +143 -41
  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/thinking-token-stamp.d.ts +12 -0
  206. package/dist/src/thinking-token-stamp.d.ts.map +1 -1
  207. package/dist/src/thinking-token-stamp.js +34 -3
  208. package/dist/src/thinking-token-stamp.js.map +1 -1
  209. package/dist/src/tool-image-ledger.d.ts +87 -19
  210. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  211. package/dist/src/tool-image-ledger.js +135 -32
  212. package/dist/src/tool-image-ledger.js.map +1 -1
  213. package/dist/src/tool-presentation.d.ts +9 -0
  214. package/dist/src/tool-presentation.d.ts.map +1 -1
  215. package/dist/src/tool-presentation.js +257 -60
  216. package/dist/src/tool-presentation.js.map +1 -1
  217. package/dist/src/tool-result-guard.d.ts.map +1 -1
  218. package/dist/src/tool-result-guard.js +7 -10
  219. package/dist/src/tool-result-guard.js.map +1 -1
  220. package/dist/src/tool-result-history.d.ts +32 -3
  221. package/dist/src/tool-result-history.d.ts.map +1 -1
  222. package/dist/src/tool-result-history.js +106 -19
  223. package/dist/src/tool-result-history.js.map +1 -1
  224. package/dist/src/tune-out/coordinator.d.ts +25 -1
  225. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  226. package/dist/src/tune-out/coordinator.js +260 -65
  227. package/dist/src/tune-out/coordinator.js.map +1 -1
  228. package/dist/src/types/agent.d.ts +17 -5
  229. package/dist/src/types/agent.d.ts.map +1 -1
  230. package/dist/src/types/events.d.ts +17 -0
  231. package/dist/src/types/events.d.ts.map +1 -1
  232. package/dist/src/types/framework.d.ts +21 -5
  233. package/dist/src/types/framework.d.ts.map +1 -1
  234. package/dist/src/types/inference-log.d.ts +1 -1
  235. package/dist/src/types/inference-log.d.ts.map +1 -1
  236. package/dist/src/types/module.d.ts +23 -4
  237. package/dist/src/types/module.d.ts.map +1 -1
  238. package/dist/src/types/trace.d.ts +95 -6
  239. package/dist/src/types/trace.d.ts.map +1 -1
  240. package/dist/src/usage/usage-tracker.d.ts +5 -0
  241. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  242. package/dist/src/usage/usage-tracker.js +35 -8
  243. package/dist/src/usage/usage-tracker.js.map +1 -1
  244. package/package.json +6 -4
  245. package/src/agent.ts +169 -41
  246. package/src/api/mcp-server-core.ts +1 -1
  247. package/src/api/server.ts +4 -1
  248. package/src/api/types.ts +1 -1
  249. package/src/branch-names.ts +19 -0
  250. package/src/closest-names.ts +94 -0
  251. package/src/code-execution/py-runner.ts +200 -76
  252. package/src/code-execution/runtime-py.ts +49 -3
  253. package/src/code-execution/tool-definition.ts +28 -4
  254. package/src/config-checks.ts +48 -0
  255. package/src/deadline-timer.ts +44 -0
  256. package/src/error-message.ts +48 -0
  257. package/src/framework.ts +5352 -1388
  258. package/src/gate/event-gate.ts +28 -3
  259. package/src/index.ts +11 -2
  260. package/src/mcpl/channel-registry.ts +517 -286
  261. package/src/mcpl/checkpoint-manager.ts +104 -29
  262. package/src/mcpl/conversation-router.ts +28 -8
  263. package/src/mcpl/convert-block.ts +97 -0
  264. package/src/mcpl/errors.ts +28 -0
  265. package/src/mcpl/hook-orchestrator.ts +84 -77
  266. package/src/mcpl/inbound-source.ts +156 -0
  267. package/src/mcpl/index.ts +10 -1
  268. package/src/mcpl/inference-router.ts +174 -99
  269. package/src/mcpl/push-coalescer.ts +60 -24
  270. package/src/mcpl/push-handler.ts +20 -90
  271. package/src/mcpl/reference-fetcher.ts +71 -26
  272. package/src/mcpl/references.ts +81 -22
  273. package/src/mcpl/server-connection.ts +427 -132
  274. package/src/mcpl/server-registry.ts +15 -4
  275. package/src/mcpl/skip-reply-wake.ts +73 -0
  276. package/src/mcpl/transport.ts +120 -18
  277. package/src/mcpl/types.ts +14 -3
  278. package/src/mcpl/visible-content.ts +27 -0
  279. package/src/module-registry.ts +58 -23
  280. package/src/modules/api/index.ts +1 -1
  281. package/src/modules/discord/discord-js-client.ts +26 -10
  282. package/src/modules/discord/index.ts +391 -158
  283. package/src/modules/discord/types.ts +15 -2
  284. package/src/modules/health/index.ts +2 -1
  285. package/src/modules/history/index.ts +410 -120
  286. package/src/modules/history/semantic.ts +189 -32
  287. package/src/modules/unknown-tool.ts +16 -0
  288. package/src/modules/workspace/disk-agreement.ts +51 -3
  289. package/src/modules/workspace/effects.ts +279 -35
  290. package/src/modules/workspace/index.ts +810 -260
  291. package/src/modules/workspace/observe.ts +194 -20
  292. package/src/modules/workspace/reconcile.ts +619 -64
  293. package/src/modules/workspace/types.ts +18 -4
  294. package/src/modules/workspace/watcher.ts +27 -4
  295. package/src/prose-drafts.ts +481 -0
  296. package/src/prose-segments.ts +51 -8
  297. package/src/recovery/offline-branch.ts +4 -1
  298. package/src/safe-slice.ts +9 -1
  299. package/src/thinking-token-stamp.ts +34 -3
  300. package/src/tool-image-ledger.ts +153 -34
  301. package/src/tool-presentation.ts +224 -40
  302. package/src/tool-result-guard.ts +7 -8
  303. package/src/tool-result-history.ts +124 -20
  304. package/src/tune-out/coordinator.ts +259 -65
  305. package/src/types/agent.ts +17 -7
  306. package/src/types/events.ts +17 -0
  307. package/src/types/framework.ts +21 -5
  308. package/src/types/inference-log.ts +1 -1
  309. package/src/types/module.ts +24 -4
  310. package/src/types/trace.ts +101 -6
  311. package/src/usage/usage-tracker.ts +32 -8
  312. package/dist/test/activation-request-sanitize.test.d.ts +0 -2
  313. package/dist/test/activation-request-sanitize.test.d.ts.map +0 -1
  314. package/dist/test/activation-request-sanitize.test.js +0 -89
  315. package/dist/test/activation-request-sanitize.test.js.map +0 -1
  316. package/dist/test/agent-cache-ttl.test.d.ts +0 -2
  317. package/dist/test/agent-cache-ttl.test.d.ts.map +0 -1
  318. package/dist/test/agent-cache-ttl.test.js +0 -41
  319. package/dist/test/agent-cache-ttl.test.js.map +0 -1
  320. package/dist/test/agent-runtime-settings.test.d.ts +0 -2
  321. package/dist/test/agent-runtime-settings.test.d.ts.map +0 -1
  322. package/dist/test/agent-runtime-settings.test.js +0 -1598
  323. package/dist/test/agent-runtime-settings.test.js.map +0 -1
  324. package/dist/test/api-admin-gate.test.d.ts +0 -2
  325. package/dist/test/api-admin-gate.test.d.ts.map +0 -1
  326. package/dist/test/api-admin-gate.test.js +0 -116
  327. package/dist/test/api-admin-gate.test.js.map +0 -1
  328. package/dist/test/assistant-persist-refusal.test.d.ts +0 -2
  329. package/dist/test/assistant-persist-refusal.test.d.ts.map +0 -1
  330. package/dist/test/assistant-persist-refusal.test.js +0 -63
  331. package/dist/test/assistant-persist-refusal.test.js.map +0 -1
  332. package/dist/test/batched-wake-locus.test.d.ts +0 -2
  333. package/dist/test/batched-wake-locus.test.d.ts.map +0 -1
  334. package/dist/test/batched-wake-locus.test.js +0 -166
  335. package/dist/test/batched-wake-locus.test.js.map +0 -1
  336. package/dist/test/busy-agent-scheduler.test.d.ts +0 -2
  337. package/dist/test/busy-agent-scheduler.test.d.ts.map +0 -1
  338. package/dist/test/busy-agent-scheduler.test.js +0 -92
  339. package/dist/test/busy-agent-scheduler.test.js.map +0 -1
  340. package/dist/test/channel-incoming-targeting.test.d.ts +0 -2
  341. package/dist/test/channel-incoming-targeting.test.d.ts.map +0 -1
  342. package/dist/test/channel-incoming-targeting.test.js +0 -81
  343. package/dist/test/channel-incoming-targeting.test.js.map +0 -1
  344. package/dist/test/channel-label-history.test.d.ts +0 -2
  345. package/dist/test/channel-label-history.test.d.ts.map +0 -1
  346. package/dist/test/channel-label-history.test.js +0 -424
  347. package/dist/test/channel-label-history.test.js.map +0 -1
  348. package/dist/test/channel-lifecycle-provenance.test.d.ts +0 -2
  349. package/dist/test/channel-lifecycle-provenance.test.d.ts.map +0 -1
  350. package/dist/test/channel-lifecycle-provenance.test.js +0 -47
  351. package/dist/test/channel-lifecycle-provenance.test.js.map +0 -1
  352. package/dist/test/channel-registry-routing.test.d.ts +0 -2
  353. package/dist/test/channel-registry-routing.test.d.ts.map +0 -1
  354. package/dist/test/channel-registry-routing.test.js +0 -446
  355. package/dist/test/channel-registry-routing.test.js.map +0 -1
  356. package/dist/test/channel-registry-typing.test.d.ts +0 -2
  357. package/dist/test/channel-registry-typing.test.d.ts.map +0 -1
  358. package/dist/test/channel-registry-typing.test.js +0 -91
  359. package/dist/test/channel-registry-typing.test.js.map +0 -1
  360. package/dist/test/channels-changed-respond-first.test.d.ts +0 -2
  361. package/dist/test/channels-changed-respond-first.test.d.ts.map +0 -1
  362. package/dist/test/channels-changed-respond-first.test.js +0 -74
  363. package/dist/test/channels-changed-respond-first.test.js.map +0 -1
  364. package/dist/test/checkpoint-attribution.test.d.ts +0 -2
  365. package/dist/test/checkpoint-attribution.test.d.ts.map +0 -1
  366. package/dist/test/checkpoint-attribution.test.js +0 -200
  367. package/dist/test/checkpoint-attribution.test.js.map +0 -1
  368. package/dist/test/code-execution.test.d.ts +0 -11
  369. package/dist/test/code-execution.test.d.ts.map +0 -1
  370. package/dist/test/code-execution.test.js +0 -1272
  371. package/dist/test/code-execution.test.js.map +0 -1
  372. package/dist/test/component-defaults.test.d.ts +0 -2
  373. package/dist/test/component-defaults.test.d.ts.map +0 -1
  374. package/dist/test/component-defaults.test.js +0 -54
  375. package/dist/test/component-defaults.test.js.map +0 -1
  376. package/dist/test/conversation-fork-injection-scoping.test.d.ts +0 -2
  377. package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +0 -1
  378. package/dist/test/conversation-fork-injection-scoping.test.js +0 -200
  379. package/dist/test/conversation-fork-injection-scoping.test.js.map +0 -1
  380. package/dist/test/conversation-router.test.d.ts +0 -2
  381. package/dist/test/conversation-router.test.d.ts.map +0 -1
  382. package/dist/test/conversation-router.test.js +0 -174
  383. package/dist/test/conversation-router.test.js.map +0 -1
  384. package/dist/test/conversation-routing.test.d.ts +0 -7
  385. package/dist/test/conversation-routing.test.d.ts.map +0 -1
  386. package/dist/test/conversation-routing.test.js +0 -621
  387. package/dist/test/conversation-routing.test.js.map +0 -1
  388. package/dist/test/deferred-wake-integrity.test.d.ts +0 -29
  389. package/dist/test/deferred-wake-integrity.test.d.ts.map +0 -1
  390. package/dist/test/deferred-wake-integrity.test.js +0 -320
  391. package/dist/test/deferred-wake-integrity.test.js.map +0 -1
  392. package/dist/test/discord-awareness-outbox.test.d.ts +0 -2
  393. package/dist/test/discord-awareness-outbox.test.d.ts.map +0 -1
  394. package/dist/test/discord-awareness-outbox.test.js +0 -371
  395. package/dist/test/discord-awareness-outbox.test.js.map +0 -1
  396. package/dist/test/discord-history-delete-window.test.d.ts +0 -2
  397. package/dist/test/discord-history-delete-window.test.d.ts.map +0 -1
  398. package/dist/test/discord-history-delete-window.test.js +0 -81
  399. package/dist/test/discord-history-delete-window.test.js.map +0 -1
  400. package/dist/test/discord-history-sync.test.d.ts +0 -2
  401. package/dist/test/discord-history-sync.test.d.ts.map +0 -1
  402. package/dist/test/discord-history-sync.test.js +0 -56
  403. package/dist/test/discord-history-sync.test.js.map +0 -1
  404. package/dist/test/discord-typing-keepalive.test.d.ts +0 -2
  405. package/dist/test/discord-typing-keepalive.test.d.ts.map +0 -1
  406. package/dist/test/discord-typing-keepalive.test.js +0 -77
  407. package/dist/test/discord-typing-keepalive.test.js.map +0 -1
  408. package/dist/test/ephemeral-settle.test.d.ts +0 -2
  409. package/dist/test/ephemeral-settle.test.d.ts.map +0 -1
  410. package/dist/test/ephemeral-settle.test.js +0 -354
  411. package/dist/test/ephemeral-settle.test.js.map +0 -1
  412. package/dist/test/event-gate-passthrough.test.d.ts +0 -15
  413. package/dist/test/event-gate-passthrough.test.d.ts.map +0 -1
  414. package/dist/test/event-gate-passthrough.test.js +0 -289
  415. package/dist/test/event-gate-passthrough.test.js.map +0 -1
  416. package/dist/test/event-gate-policy-mutation.test.d.ts +0 -2
  417. package/dist/test/event-gate-policy-mutation.test.d.ts.map +0 -1
  418. package/dist/test/event-gate-policy-mutation.test.js +0 -137
  419. package/dist/test/event-gate-policy-mutation.test.js.map +0 -1
  420. package/dist/test/event-gate-rate-limit.test.d.ts +0 -2
  421. package/dist/test/event-gate-rate-limit.test.d.ts.map +0 -1
  422. package/dist/test/event-gate-rate-limit.test.js +0 -627
  423. package/dist/test/event-gate-rate-limit.test.js.map +0 -1
  424. package/dist/test/event-gate-script.test.d.ts +0 -2
  425. package/dist/test/event-gate-script.test.d.ts.map +0 -1
  426. package/dist/test/event-gate-script.test.js +0 -95
  427. package/dist/test/event-gate-script.test.js.map +0 -1
  428. package/dist/test/event-gate-selfwake.test.d.ts +0 -15
  429. package/dist/test/event-gate-selfwake.test.d.ts.map +0 -1
  430. package/dist/test/event-gate-selfwake.test.js +0 -125
  431. package/dist/test/event-gate-selfwake.test.js.map +0 -1
  432. package/dist/test/event-gate-tags.test.d.ts +0 -2
  433. package/dist/test/event-gate-tags.test.d.ts.map +0 -1
  434. package/dist/test/event-gate-tags.test.js +0 -76
  435. package/dist/test/event-gate-tags.test.js.map +0 -1
  436. package/dist/test/event-gate.test.d.ts +0 -2
  437. package/dist/test/event-gate.test.d.ts.map +0 -1
  438. package/dist/test/event-gate.test.js +0 -919
  439. package/dist/test/event-gate.test.js.map +0 -1
  440. package/dist/test/explicit-prose-routing.test.d.ts +0 -11
  441. package/dist/test/explicit-prose-routing.test.d.ts.map +0 -1
  442. package/dist/test/explicit-prose-routing.test.js +0 -1715
  443. package/dist/test/explicit-prose-routing.test.js.map +0 -1
  444. package/dist/test/framework.test.d.ts +0 -5
  445. package/dist/test/framework.test.d.ts.map +0 -1
  446. package/dist/test/framework.test.js +0 -1048
  447. package/dist/test/framework.test.js.map +0 -1
  448. package/dist/test/gather-context-timeout.test.d.ts +0 -2
  449. package/dist/test/gather-context-timeout.test.d.ts.map +0 -1
  450. package/dist/test/gather-context-timeout.test.js +0 -120
  451. package/dist/test/gather-context-timeout.test.js.map +0 -1
  452. package/dist/test/health-snapshot.test.d.ts +0 -2
  453. package/dist/test/health-snapshot.test.d.ts.map +0 -1
  454. package/dist/test/health-snapshot.test.js +0 -74
  455. package/dist/test/health-snapshot.test.js.map +0 -1
  456. package/dist/test/helpers/coalescing-fixture.d.ts +0 -74
  457. package/dist/test/helpers/coalescing-fixture.d.ts.map +0 -1
  458. package/dist/test/helpers/coalescing-fixture.js +0 -152
  459. package/dist/test/helpers/coalescing-fixture.js.map +0 -1
  460. package/dist/test/helpers/coalescing-kill-child.d.ts +0 -2
  461. package/dist/test/helpers/coalescing-kill-child.d.ts.map +0 -1
  462. package/dist/test/helpers/coalescing-kill-child.js +0 -24
  463. package/dist/test/helpers/coalescing-kill-child.js.map +0 -1
  464. package/dist/test/helpers/mock-membrane.d.ts +0 -53
  465. package/dist/test/helpers/mock-membrane.d.ts.map +0 -1
  466. package/dist/test/helpers/mock-membrane.js +0 -168
  467. package/dist/test/helpers/mock-membrane.js.map +0 -1
  468. package/dist/test/helpers/pytc-stop-rows.d.ts +0 -11
  469. package/dist/test/helpers/pytc-stop-rows.d.ts.map +0 -1
  470. package/dist/test/helpers/pytc-stop-rows.js +0 -143
  471. package/dist/test/helpers/pytc-stop-rows.js.map +0 -1
  472. package/dist/test/helpers/quiesce-crash-child.d.ts +0 -2
  473. package/dist/test/helpers/quiesce-crash-child.d.ts.map +0 -1
  474. package/dist/test/helpers/quiesce-crash-child.js +0 -156
  475. package/dist/test/helpers/quiesce-crash-child.js.map +0 -1
  476. package/dist/test/hide-then-turn.test.d.ts +0 -2
  477. package/dist/test/hide-then-turn.test.d.ts.map +0 -1
  478. package/dist/test/hide-then-turn.test.js +0 -101
  479. package/dist/test/hide-then-turn.test.js.map +0 -1
  480. package/dist/test/history-module-overview.test.d.ts +0 -2
  481. package/dist/test/history-module-overview.test.d.ts.map +0 -1
  482. package/dist/test/history-module-overview.test.js +0 -711
  483. package/dist/test/history-module-overview.test.js.map +0 -1
  484. package/dist/test/history-module-realstore.test.d.ts +0 -2
  485. package/dist/test/history-module-realstore.test.d.ts.map +0 -1
  486. package/dist/test/history-module-realstore.test.js +0 -363
  487. package/dist/test/history-module-realstore.test.js.map +0 -1
  488. package/dist/test/history-module-semantic.test.d.ts +0 -2
  489. package/dist/test/history-module-semantic.test.d.ts.map +0 -1
  490. package/dist/test/history-module-semantic.test.js +0 -213
  491. package/dist/test/history-module-semantic.test.js.map +0 -1
  492. package/dist/test/history-module-ux.test.d.ts +0 -2
  493. package/dist/test/history-module-ux.test.d.ts.map +0 -1
  494. package/dist/test/history-module-ux.test.js +0 -304
  495. package/dist/test/history-module-ux.test.js.map +0 -1
  496. package/dist/test/history-module.test.d.ts +0 -2
  497. package/dist/test/history-module.test.d.ts.map +0 -1
  498. package/dist/test/history-module.test.js +0 -619
  499. package/dist/test/history-module.test.js.map +0 -1
  500. package/dist/test/history-semantic-review-findings.test.d.ts +0 -2
  501. package/dist/test/history-semantic-review-findings.test.d.ts.map +0 -1
  502. package/dist/test/history-semantic-review-findings.test.js +0 -338
  503. package/dist/test/history-semantic-review-findings.test.js.map +0 -1
  504. package/dist/test/history-semantic-sync-converges.test.d.ts +0 -2
  505. package/dist/test/history-semantic-sync-converges.test.d.ts.map +0 -1
  506. package/dist/test/history-semantic-sync-converges.test.js +0 -109
  507. package/dist/test/history-semantic-sync-converges.test.js.map +0 -1
  508. package/dist/test/host-quiesce.test.d.ts +0 -2
  509. package/dist/test/host-quiesce.test.d.ts.map +0 -1
  510. package/dist/test/host-quiesce.test.js +0 -988
  511. package/dist/test/host-quiesce.test.js.map +0 -1
  512. package/dist/test/inference-exhaustion-rewind.test.d.ts +0 -2
  513. package/dist/test/inference-exhaustion-rewind.test.d.ts.map +0 -1
  514. package/dist/test/inference-exhaustion-rewind.test.js +0 -387
  515. package/dist/test/inference-exhaustion-rewind.test.js.map +0 -1
  516. package/dist/test/inference-failure-bounds.test.d.ts +0 -2
  517. package/dist/test/inference-failure-bounds.test.d.ts.map +0 -1
  518. package/dist/test/inference-failure-bounds.test.js +0 -1202
  519. package/dist/test/inference-failure-bounds.test.js.map +0 -1
  520. package/dist/test/inference-failure-observability.test.d.ts +0 -2
  521. package/dist/test/inference-failure-observability.test.d.ts.map +0 -1
  522. package/dist/test/inference-failure-observability.test.js +0 -84
  523. package/dist/test/inference-failure-observability.test.js.map +0 -1
  524. package/dist/test/journal-tool.test.d.ts +0 -2
  525. package/dist/test/journal-tool.test.d.ts.map +0 -1
  526. package/dist/test/journal-tool.test.js +0 -33
  527. package/dist/test/journal-tool.test.js.map +0 -1
  528. package/dist/test/kv-unified-open-flight-retry.test.d.ts +0 -2
  529. package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +0 -1
  530. package/dist/test/kv-unified-open-flight-retry.test.js +0 -189
  531. package/dist/test/kv-unified-open-flight-retry.test.js.map +0 -1
  532. package/dist/test/kv-unified-wiring.test.d.ts +0 -2
  533. package/dist/test/kv-unified-wiring.test.d.ts.map +0 -1
  534. package/dist/test/kv-unified-wiring.test.js +0 -95
  535. package/dist/test/kv-unified-wiring.test.js.map +0 -1
  536. package/dist/test/late-events-after-stop.test.d.ts +0 -2
  537. package/dist/test/late-events-after-stop.test.d.ts.map +0 -1
  538. package/dist/test/late-events-after-stop.test.js +0 -122
  539. package/dist/test/late-events-after-stop.test.js.map +0 -1
  540. package/dist/test/liveness-watchdog.test.d.ts +0 -2
  541. package/dist/test/liveness-watchdog.test.d.ts.map +0 -1
  542. package/dist/test/liveness-watchdog.test.js +0 -94
  543. package/dist/test/liveness-watchdog.test.js.map +0 -1
  544. package/dist/test/machine-close-provenance.test.d.ts +0 -2
  545. package/dist/test/machine-close-provenance.test.d.ts.map +0 -1
  546. package/dist/test/machine-close-provenance.test.js +0 -116
  547. package/dist/test/machine-close-provenance.test.js.map +0 -1
  548. package/dist/test/maintenance-timer.test.d.ts +0 -2
  549. package/dist/test/maintenance-timer.test.d.ts.map +0 -1
  550. package/dist/test/maintenance-timer.test.js +0 -1827
  551. package/dist/test/maintenance-timer.test.js.map +0 -1
  552. package/dist/test/mcp-server-bin.test.d.ts +0 -2
  553. package/dist/test/mcp-server-bin.test.d.ts.map +0 -1
  554. package/dist/test/mcp-server-bin.test.js +0 -142
  555. package/dist/test/mcp-server-bin.test.js.map +0 -1
  556. package/dist/test/mcpl-access-provider.test.d.ts +0 -2
  557. package/dist/test/mcpl-access-provider.test.d.ts.map +0 -1
  558. package/dist/test/mcpl-access-provider.test.js +0 -20
  559. package/dist/test/mcpl-access-provider.test.js.map +0 -1
  560. package/dist/test/mcpl-awareness-barrier.test.d.ts +0 -2
  561. package/dist/test/mcpl-awareness-barrier.test.d.ts.map +0 -1
  562. package/dist/test/mcpl-awareness-barrier.test.js +0 -769
  563. package/dist/test/mcpl-awareness-barrier.test.js.map +0 -1
  564. package/dist/test/mcpl-capability-grant.test.d.ts +0 -10
  565. package/dist/test/mcpl-capability-grant.test.d.ts.map +0 -1
  566. package/dist/test/mcpl-capability-grant.test.js +0 -111
  567. package/dist/test/mcpl-capability-grant.test.js.map +0 -1
  568. package/dist/test/mcpl-capability-scoping.test.d.ts +0 -2
  569. package/dist/test/mcpl-capability-scoping.test.d.ts.map +0 -1
  570. package/dist/test/mcpl-capability-scoping.test.js +0 -266
  571. package/dist/test/mcpl-capability-scoping.test.js.map +0 -1
  572. package/dist/test/mcpl-child-env.test.d.ts +0 -2
  573. package/dist/test/mcpl-child-env.test.d.ts.map +0 -1
  574. package/dist/test/mcpl-child-env.test.js +0 -158
  575. package/dist/test/mcpl-child-env.test.js.map +0 -1
  576. package/dist/test/mcpl-coalesced-content-parity.test.d.ts +0 -2
  577. package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +0 -1
  578. package/dist/test/mcpl-coalesced-content-parity.test.js +0 -532
  579. package/dist/test/mcpl-coalesced-content-parity.test.js.map +0 -1
  580. package/dist/test/mcpl-coalescing.test.d.ts +0 -2
  581. package/dist/test/mcpl-coalescing.test.d.ts.map +0 -1
  582. package/dist/test/mcpl-coalescing.test.js +0 -1125
  583. package/dist/test/mcpl-coalescing.test.js.map +0 -1
  584. package/dist/test/mcpl-config-diagnostics.test.d.ts +0 -2
  585. package/dist/test/mcpl-config-diagnostics.test.d.ts.map +0 -1
  586. package/dist/test/mcpl-config-diagnostics.test.js +0 -452
  587. package/dist/test/mcpl-config-diagnostics.test.js.map +0 -1
  588. package/dist/test/mcpl-empty-content.test.d.ts +0 -2
  589. package/dist/test/mcpl-empty-content.test.d.ts.map +0 -1
  590. package/dist/test/mcpl-empty-content.test.js +0 -368
  591. package/dist/test/mcpl-empty-content.test.js.map +0 -1
  592. package/dist/test/mcpl-gate-roundtrip.test.d.ts +0 -2
  593. package/dist/test/mcpl-gate-roundtrip.test.d.ts.map +0 -1
  594. package/dist/test/mcpl-gate-roundtrip.test.js +0 -285
  595. package/dist/test/mcpl-gate-roundtrip.test.js.map +0 -1
  596. package/dist/test/mcpl-list-status.test.d.ts +0 -2
  597. package/dist/test/mcpl-list-status.test.d.ts.map +0 -1
  598. package/dist/test/mcpl-list-status.test.js +0 -82
  599. package/dist/test/mcpl-list-status.test.js.map +0 -1
  600. package/dist/test/mcpl-manifest-changed.test.d.ts +0 -2
  601. package/dist/test/mcpl-manifest-changed.test.d.ts.map +0 -1
  602. package/dist/test/mcpl-manifest-changed.test.js +0 -178
  603. package/dist/test/mcpl-manifest-changed.test.js.map +0 -1
  604. package/dist/test/mcpl-push-convert.test.d.ts +0 -2
  605. package/dist/test/mcpl-push-convert.test.d.ts.map +0 -1
  606. package/dist/test/mcpl-push-convert.test.js +0 -42
  607. package/dist/test/mcpl-push-convert.test.js.map +0 -1
  608. package/dist/test/mcpl-reconnect-events.test.d.ts +0 -2
  609. package/dist/test/mcpl-reconnect-events.test.d.ts.map +0 -1
  610. package/dist/test/mcpl-reconnect-events.test.js +0 -144
  611. package/dist/test/mcpl-reconnect-events.test.js.map +0 -1
  612. package/dist/test/mcpl-reconnect-reregistration.test.d.ts +0 -2
  613. package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +0 -1
  614. package/dist/test/mcpl-reconnect-reregistration.test.js +0 -238
  615. package/dist/test/mcpl-reconnect-reregistration.test.js.map +0 -1
  616. package/dist/test/mcpl-reference-fetcher.test.d.ts +0 -2
  617. package/dist/test/mcpl-reference-fetcher.test.d.ts.map +0 -1
  618. package/dist/test/mcpl-reference-fetcher.test.js +0 -427
  619. package/dist/test/mcpl-reference-fetcher.test.js.map +0 -1
  620. package/dist/test/mcpl-reference-fields.test.d.ts +0 -2
  621. package/dist/test/mcpl-reference-fields.test.d.ts.map +0 -1
  622. package/dist/test/mcpl-reference-fields.test.js +0 -149
  623. package/dist/test/mcpl-reference-fields.test.js.map +0 -1
  624. package/dist/test/mcpl-rejection-log-lines.test.d.ts +0 -2
  625. package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +0 -1
  626. package/dist/test/mcpl-rejection-log-lines.test.js +0 -349
  627. package/dist/test/mcpl-rejection-log-lines.test.js.map +0 -1
  628. package/dist/test/mcpl-request-timeout.test.d.ts +0 -2
  629. package/dist/test/mcpl-request-timeout.test.d.ts.map +0 -1
  630. package/dist/test/mcpl-request-timeout.test.js +0 -138
  631. package/dist/test/mcpl-request-timeout.test.js.map +0 -1
  632. package/dist/test/mcpl-runtime-lifecycle.test.d.ts +0 -7
  633. package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +0 -1
  634. package/dist/test/mcpl-runtime-lifecycle.test.js +0 -142
  635. package/dist/test/mcpl-runtime-lifecycle.test.js.map +0 -1
  636. package/dist/test/mcpl-stderr-tail.test.d.ts +0 -2
  637. package/dist/test/mcpl-stderr-tail.test.d.ts.map +0 -1
  638. package/dist/test/mcpl-stderr-tail.test.js +0 -210
  639. package/dist/test/mcpl-stderr-tail.test.js.map +0 -1
  640. package/dist/test/mcpl-tool-class-listing.test.d.ts +0 -2
  641. package/dist/test/mcpl-tool-class-listing.test.d.ts.map +0 -1
  642. package/dist/test/mcpl-tool-class-listing.test.js +0 -147
  643. package/dist/test/mcpl-tool-class-listing.test.js.map +0 -1
  644. package/dist/test/mcpl-tool-classes.test.d.ts +0 -7
  645. package/dist/test/mcpl-tool-classes.test.d.ts.map +0 -1
  646. package/dist/test/mcpl-tool-classes.test.js +0 -80
  647. package/dist/test/mcpl-tool-classes.test.js.map +0 -1
  648. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts +0 -2
  649. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts.map +0 -1
  650. package/dist/test/mcpl-tool-lifecycle-e2e.test.js +0 -237
  651. package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +0 -1
  652. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts +0 -11
  653. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts.map +0 -1
  654. package/dist/test/mcpl-tool-lifecycle-emitter.test.js +0 -412
  655. package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +0 -1
  656. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +0 -2
  657. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +0 -1
  658. package/dist/test/mcpl-tool-lifecycle-script.test.js +0 -288
  659. package/dist/test/mcpl-tool-lifecycle-script.test.js.map +0 -1
  660. package/dist/test/mcpl-tool-lifecycle.test.d.ts +0 -11
  661. package/dist/test/mcpl-tool-lifecycle.test.d.ts.map +0 -1
  662. package/dist/test/mcpl-tool-lifecycle.test.js +0 -270
  663. package/dist/test/mcpl-tool-lifecycle.test.js.map +0 -1
  664. package/dist/test/mcpl-transport-close.test.d.ts +0 -2
  665. package/dist/test/mcpl-transport-close.test.d.ts.map +0 -1
  666. package/dist/test/mcpl-transport-close.test.js +0 -373
  667. package/dist/test/mcpl-transport-close.test.js.map +0 -1
  668. package/dist/test/mcpl-transport-spawn.test.d.ts +0 -2
  669. package/dist/test/mcpl-transport-spawn.test.d.ts.map +0 -1
  670. package/dist/test/mcpl-transport-spawn.test.js +0 -58
  671. package/dist/test/mcpl-transport-spawn.test.js.map +0 -1
  672. package/dist/test/mcpl-ws-transport.test.d.ts +0 -2
  673. package/dist/test/mcpl-ws-transport.test.d.ts.map +0 -1
  674. package/dist/test/mcpl-ws-transport.test.js +0 -259
  675. package/dist/test/mcpl-ws-transport.test.js.map +0 -1
  676. package/dist/test/module-context-integration.test.d.ts +0 -18
  677. package/dist/test/module-context-integration.test.d.ts.map +0 -1
  678. package/dist/test/module-context-integration.test.js +0 -181
  679. package/dist/test/module-context-integration.test.js.map +0 -1
  680. package/dist/test/offline-recovery-branch.test.d.ts +0 -2
  681. package/dist/test/offline-recovery-branch.test.d.ts.map +0 -1
  682. package/dist/test/offline-recovery-branch.test.js +0 -570
  683. package/dist/test/offline-recovery-branch.test.js.map +0 -1
  684. package/dist/test/operator-surgery.test.d.ts +0 -2
  685. package/dist/test/operator-surgery.test.d.ts.map +0 -1
  686. package/dist/test/operator-surgery.test.js +0 -595
  687. package/dist/test/operator-surgery.test.js.map +0 -1
  688. package/dist/test/ops-alert.test.d.ts +0 -2
  689. package/dist/test/ops-alert.test.d.ts.map +0 -1
  690. package/dist/test/ops-alert.test.js +0 -153
  691. package/dist/test/ops-alert.test.js.map +0 -1
  692. package/dist/test/overbudget-drain-breaker.test.d.ts +0 -2
  693. package/dist/test/overbudget-drain-breaker.test.d.ts.map +0 -1
  694. package/dist/test/overbudget-drain-breaker.test.js +0 -198
  695. package/dist/test/overbudget-drain-breaker.test.js.map +0 -1
  696. package/dist/test/pack-installs-alone.test.d.ts +0 -2
  697. package/dist/test/pack-installs-alone.test.d.ts.map +0 -1
  698. package/dist/test/pack-installs-alone.test.js +0 -284
  699. package/dist/test/pack-installs-alone.test.js.map +0 -1
  700. package/dist/test/per-agent-delivery.test.d.ts +0 -2
  701. package/dist/test/per-agent-delivery.test.d.ts.map +0 -1
  702. package/dist/test/per-agent-delivery.test.js +0 -108
  703. package/dist/test/per-agent-delivery.test.js.map +0 -1
  704. package/dist/test/physical-window-restart.test.d.ts +0 -16
  705. package/dist/test/physical-window-restart.test.d.ts.map +0 -1
  706. package/dist/test/physical-window-restart.test.js +0 -487
  707. package/dist/test/physical-window-restart.test.js.map +0 -1
  708. package/dist/test/placed-reaction-baseline.test.d.ts +0 -2
  709. package/dist/test/placed-reaction-baseline.test.d.ts.map +0 -1
  710. package/dist/test/placed-reaction-baseline.test.js +0 -312
  711. package/dist/test/placed-reaction-baseline.test.js.map +0 -1
  712. package/dist/test/present-while-acting.test.d.ts +0 -19
  713. package/dist/test/present-while-acting.test.d.ts.map +0 -1
  714. package/dist/test/present-while-acting.test.js +0 -1649
  715. package/dist/test/present-while-acting.test.js.map +0 -1
  716. package/dist/test/prose-segments.test.d.ts +0 -2
  717. package/dist/test/prose-segments.test.d.ts.map +0 -1
  718. package/dist/test/prose-segments.test.js +0 -52
  719. package/dist/test/prose-segments.test.js.map +0 -1
  720. package/dist/test/prose-stream-router.test.d.ts +0 -2
  721. package/dist/test/prose-stream-router.test.d.ts.map +0 -1
  722. package/dist/test/prose-stream-router.test.js +0 -171
  723. package/dist/test/prose-stream-router.test.js.map +0 -1
  724. package/dist/test/provider-acceleration-cooldown.test.d.ts +0 -2
  725. package/dist/test/provider-acceleration-cooldown.test.d.ts.map +0 -1
  726. package/dist/test/provider-acceleration-cooldown.test.js +0 -435
  727. package/dist/test/provider-acceleration-cooldown.test.js.map +0 -1
  728. package/dist/test/provider-waits.test.d.ts +0 -2
  729. package/dist/test/provider-waits.test.d.ts.map +0 -1
  730. package/dist/test/provider-waits.test.js +0 -2259
  731. package/dist/test/provider-waits.test.js.map +0 -1
  732. package/dist/test/puppet-tool-call.test.d.ts +0 -2
  733. package/dist/test/puppet-tool-call.test.d.ts.map +0 -1
  734. package/dist/test/puppet-tool-call.test.js +0 -226
  735. package/dist/test/puppet-tool-call.test.js.map +0 -1
  736. package/dist/test/puppet-turn-reservation.test.d.ts +0 -2
  737. package/dist/test/puppet-turn-reservation.test.d.ts.map +0 -1
  738. package/dist/test/puppet-turn-reservation.test.js +0 -139
  739. package/dist/test/puppet-turn-reservation.test.js.map +0 -1
  740. package/dist/test/reaction-baseline-platform.test.d.ts +0 -2
  741. package/dist/test/reaction-baseline-platform.test.d.ts.map +0 -1
  742. package/dist/test/reaction-baseline-platform.test.js +0 -124
  743. package/dist/test/reaction-baseline-platform.test.js.map +0 -1
  744. package/dist/test/record-journal.test.d.ts +0 -6
  745. package/dist/test/record-journal.test.d.ts.map +0 -1
  746. package/dist/test/record-journal.test.js +0 -269
  747. package/dist/test/record-journal.test.js.map +0 -1
  748. package/dist/test/refusal-reaction-baseline.test.d.ts +0 -2
  749. package/dist/test/refusal-reaction-baseline.test.d.ts.map +0 -1
  750. package/dist/test/refusal-reaction-baseline.test.js +0 -81
  751. package/dist/test/refusal-reaction-baseline.test.js.map +0 -1
  752. package/dist/test/refusal-rewind.test.d.ts +0 -2
  753. package/dist/test/refusal-rewind.test.d.ts.map +0 -1
  754. package/dist/test/refusal-rewind.test.js +0 -117
  755. package/dist/test/refusal-rewind.test.js.map +0 -1
  756. package/dist/test/refusal-single-call.test.d.ts +0 -2
  757. package/dist/test/refusal-single-call.test.d.ts.map +0 -1
  758. package/dist/test/refusal-single-call.test.js +0 -177
  759. package/dist/test/refusal-single-call.test.js.map +0 -1
  760. package/dist/test/release-bump.test.d.ts +0 -2
  761. package/dist/test/release-bump.test.d.ts.map +0 -1
  762. package/dist/test/release-bump.test.js +0 -248
  763. package/dist/test/release-bump.test.js.map +0 -1
  764. package/dist/test/release-changelog.test.d.ts +0 -2
  765. package/dist/test/release-changelog.test.d.ts.map +0 -1
  766. package/dist/test/release-changelog.test.js +0 -159
  767. package/dist/test/release-changelog.test.js.map +0 -1
  768. package/dist/test/release-holds.test.d.ts +0 -2
  769. package/dist/test/release-holds.test.d.ts.map +0 -1
  770. package/dist/test/release-holds.test.js +0 -456
  771. package/dist/test/release-holds.test.js.map +0 -1
  772. package/dist/test/same-round-think-policy.test.d.ts +0 -2
  773. package/dist/test/same-round-think-policy.test.d.ts.map +0 -1
  774. package/dist/test/same-round-think-policy.test.js +0 -853
  775. package/dist/test/same-round-think-policy.test.js.map +0 -1
  776. package/dist/test/save-recent-image.test.d.ts +0 -26
  777. package/dist/test/save-recent-image.test.d.ts.map +0 -1
  778. package/dist/test/save-recent-image.test.js +0 -974
  779. package/dist/test/save-recent-image.test.js.map +0 -1
  780. package/dist/test/shutdown-errors.test.d.ts +0 -2
  781. package/dist/test/shutdown-errors.test.d.ts.map +0 -1
  782. package/dist/test/shutdown-errors.test.js +0 -662
  783. package/dist/test/shutdown-errors.test.js.map +0 -1
  784. package/dist/test/silent-heartbeat-rows.test.d.ts +0 -2
  785. package/dist/test/silent-heartbeat-rows.test.d.ts.map +0 -1
  786. package/dist/test/silent-heartbeat-rows.test.js +0 -451
  787. package/dist/test/silent-heartbeat-rows.test.js.map +0 -1
  788. package/dist/test/silent-heartbeat.test.d.ts +0 -2
  789. package/dist/test/silent-heartbeat.test.d.ts.map +0 -1
  790. package/dist/test/silent-heartbeat.test.js +0 -165
  791. package/dist/test/silent-heartbeat.test.js.map +0 -1
  792. package/dist/test/state-scaling-e2e.test.d.ts +0 -22
  793. package/dist/test/state-scaling-e2e.test.d.ts.map +0 -1
  794. package/dist/test/state-scaling-e2e.test.js +0 -206
  795. package/dist/test/state-scaling-e2e.test.js.map +0 -1
  796. package/dist/test/state-scaling.test.d.ts +0 -12
  797. package/dist/test/state-scaling.test.d.ts.map +0 -1
  798. package/dist/test/state-scaling.test.js +0 -251
  799. package/dist/test/state-scaling.test.js.map +0 -1
  800. package/dist/test/stop-with-host-operations.test.d.ts +0 -20
  801. package/dist/test/stop-with-host-operations.test.d.ts.map +0 -1
  802. package/dist/test/stop-with-host-operations.test.js +0 -1174
  803. package/dist/test/stop-with-host-operations.test.js.map +0 -1
  804. package/dist/test/stop-with-work-in-flight.test.d.ts +0 -23
  805. package/dist/test/stop-with-work-in-flight.test.d.ts.map +0 -1
  806. package/dist/test/stop-with-work-in-flight.test.js +0 -784
  807. package/dist/test/stop-with-work-in-flight.test.js.map +0 -1
  808. package/dist/test/surgery-marks-receipt.test.d.ts +0 -2
  809. package/dist/test/surgery-marks-receipt.test.d.ts.map +0 -1
  810. package/dist/test/surgery-marks-receipt.test.js +0 -540
  811. package/dist/test/surgery-marks-receipt.test.js.map +0 -1
  812. package/dist/test/thinking-token-stamp.test.d.ts +0 -12
  813. package/dist/test/thinking-token-stamp.test.d.ts.map +0 -1
  814. package/dist/test/thinking-token-stamp.test.js +0 -129
  815. package/dist/test/thinking-token-stamp.test.js.map +0 -1
  816. package/dist/test/timezone.test.d.ts +0 -2
  817. package/dist/test/timezone.test.d.ts.map +0 -1
  818. package/dist/test/timezone.test.js +0 -18
  819. package/dist/test/timezone.test.js.map +0 -1
  820. package/dist/test/tool-batch-hook.test.d.ts +0 -8
  821. package/dist/test/tool-batch-hook.test.d.ts.map +0 -1
  822. package/dist/test/tool-batch-hook.test.js +0 -275
  823. package/dist/test/tool-batch-hook.test.js.map +0 -1
  824. package/dist/test/tool-policy.test.d.ts +0 -2
  825. package/dist/test/tool-policy.test.d.ts.map +0 -1
  826. package/dist/test/tool-policy.test.js +0 -51
  827. package/dist/test/tool-policy.test.js.map +0 -1
  828. package/dist/test/tool-presentation.test.d.ts +0 -2
  829. package/dist/test/tool-presentation.test.d.ts.map +0 -1
  830. package/dist/test/tool-presentation.test.js +0 -268
  831. package/dist/test/tool-presentation.test.js.map +0 -1
  832. package/dist/test/tool-result-content.test.d.ts +0 -2
  833. package/dist/test/tool-result-content.test.d.ts.map +0 -1
  834. package/dist/test/tool-result-content.test.js +0 -37
  835. package/dist/test/tool-result-content.test.js.map +0 -1
  836. package/dist/test/tool-result-guard.test.d.ts +0 -2
  837. package/dist/test/tool-result-guard.test.d.ts.map +0 -1
  838. package/dist/test/tool-result-guard.test.js +0 -3729
  839. package/dist/test/tool-result-guard.test.js.map +0 -1
  840. package/dist/test/tool-result-history.test.d.ts +0 -7
  841. package/dist/test/tool-result-history.test.d.ts.map +0 -1
  842. package/dist/test/tool-result-history.test.js +0 -156
  843. package/dist/test/tool-result-history.test.js.map +0 -1
  844. package/dist/test/tool-result-spill.test.d.ts +0 -13
  845. package/dist/test/tool-result-spill.test.d.ts.map +0 -1
  846. package/dist/test/tool-result-spill.test.js +0 -586
  847. package/dist/test/tool-result-spill.test.js.map +0 -1
  848. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts +0 -2
  849. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts.map +0 -1
  850. package/dist/test/tool-wrapper-prose-guard-integration.test.js +0 -485
  851. package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +0 -1
  852. package/dist/test/tool-wrapper-prose-guard.test.d.ts +0 -2
  853. package/dist/test/tool-wrapper-prose-guard.test.d.ts.map +0 -1
  854. package/dist/test/tool-wrapper-prose-guard.test.js +0 -34
  855. package/dist/test/tool-wrapper-prose-guard.test.js.map +0 -1
  856. package/dist/test/tools-observe-parser-conformance.test.d.ts +0 -2
  857. package/dist/test/tools-observe-parser-conformance.test.d.ts.map +0 -1
  858. package/dist/test/tools-observe-parser-conformance.test.js +0 -129
  859. package/dist/test/tools-observe-parser-conformance.test.js.map +0 -1
  860. package/dist/test/trunk-channel-routing.test.d.ts +0 -19
  861. package/dist/test/trunk-channel-routing.test.d.ts.map +0 -1
  862. package/dist/test/trunk-channel-routing.test.js +0 -652
  863. package/dist/test/trunk-channel-routing.test.js.map +0 -1
  864. package/dist/test/tune-out-e2e.test.d.ts +0 -2
  865. package/dist/test/tune-out-e2e.test.d.ts.map +0 -1
  866. package/dist/test/tune-out-e2e.test.js +0 -142
  867. package/dist/test/tune-out-e2e.test.js.map +0 -1
  868. package/dist/test/tune-out-gate-composition.test.d.ts +0 -2
  869. package/dist/test/tune-out-gate-composition.test.d.ts.map +0 -1
  870. package/dist/test/tune-out-gate-composition.test.js +0 -221
  871. package/dist/test/tune-out-gate-composition.test.js.map +0 -1
  872. package/dist/test/tune-out-lifecycle.test.d.ts +0 -2
  873. package/dist/test/tune-out-lifecycle.test.d.ts.map +0 -1
  874. package/dist/test/tune-out-lifecycle.test.js +0 -121
  875. package/dist/test/tune-out-lifecycle.test.js.map +0 -1
  876. package/dist/test/turn-trigger-provenance.test.d.ts +0 -2
  877. package/dist/test/turn-trigger-provenance.test.d.ts.map +0 -1
  878. package/dist/test/turn-trigger-provenance.test.js +0 -137
  879. package/dist/test/turn-trigger-provenance.test.js.map +0 -1
  880. package/dist/test/unconvertible-tool-result.test.d.ts +0 -2
  881. package/dist/test/unconvertible-tool-result.test.d.ts.map +0 -1
  882. package/dist/test/unconvertible-tool-result.test.js +0 -358
  883. package/dist/test/unconvertible-tool-result.test.js.map +0 -1
  884. package/dist/test/unsent-compiles.test.d.ts +0 -2
  885. package/dist/test/unsent-compiles.test.d.ts.map +0 -1
  886. package/dist/test/unsent-compiles.test.js +0 -139
  887. package/dist/test/unsent-compiles.test.js.map +0 -1
  888. package/dist/test/usage-accounting.test.d.ts +0 -2
  889. package/dist/test/usage-accounting.test.d.ts.map +0 -1
  890. package/dist/test/usage-accounting.test.js +0 -13
  891. package/dist/test/usage-accounting.test.js.map +0 -1
  892. package/dist/test/usage-tracker.test.d.ts +0 -2
  893. package/dist/test/usage-tracker.test.d.ts.map +0 -1
  894. package/dist/test/usage-tracker.test.js +0 -129
  895. package/dist/test/usage-tracker.test.js.map +0 -1
  896. package/dist/test/utils-surface.test.d.ts +0 -2
  897. package/dist/test/utils-surface.test.d.ts.map +0 -1
  898. package/dist/test/utils-surface.test.js +0 -145
  899. package/dist/test/utils-surface.test.js.map +0 -1
  900. package/dist/test/workspace-branch-continuation.test.d.ts +0 -15
  901. package/dist/test/workspace-branch-continuation.test.d.ts.map +0 -1
  902. package/dist/test/workspace-branch-continuation.test.js +0 -183
  903. package/dist/test/workspace-branch-continuation.test.js.map +0 -1
  904. package/dist/test/workspace-materialize-freshness.test.d.ts +0 -16
  905. package/dist/test/workspace-materialize-freshness.test.d.ts.map +0 -1
  906. package/dist/test/workspace-materialize-freshness.test.js +0 -252
  907. package/dist/test/workspace-materialize-freshness.test.js.map +0 -1
  908. package/dist/test/workspace-path-containment.test.d.ts +0 -19
  909. package/dist/test/workspace-path-containment.test.d.ts.map +0 -1
  910. package/dist/test/workspace-path-containment.test.js +0 -91
  911. package/dist/test/workspace-path-containment.test.js.map +0 -1
  912. package/dist/test/workspace-read-file-from-disk.test.d.ts +0 -17
  913. package/dist/test/workspace-read-file-from-disk.test.d.ts.map +0 -1
  914. package/dist/test/workspace-read-file-from-disk.test.js +0 -174
  915. package/dist/test/workspace-read-file-from-disk.test.js.map +0 -1
  916. package/dist/test/workspace-read-image.test.d.ts +0 -2
  917. package/dist/test/workspace-read-image.test.d.ts.map +0 -1
  918. package/dist/test/workspace-read-image.test.js +0 -597
  919. package/dist/test/workspace-read-image.test.js.map +0 -1
  920. package/dist/test/workspace-read-text.test.d.ts +0 -2
  921. package/dist/test/workspace-read-text.test.d.ts.map +0 -1
  922. package/dist/test/workspace-read-text.test.js +0 -101
  923. package/dist/test/workspace-read-text.test.js.map +0 -1
  924. package/dist/test/workspace-restore-order.test.d.ts +0 -16
  925. package/dist/test/workspace-restore-order.test.d.ts.map +0 -1
  926. package/dist/test/workspace-restore-order.test.js +0 -140
  927. package/dist/test/workspace-restore-order.test.js.map +0 -1
  928. package/dist/test/workspace-three-way.test.d.ts +0 -12
  929. package/dist/test/workspace-three-way.test.d.ts.map +0 -1
  930. package/dist/test/workspace-three-way.test.js +0 -4094
  931. package/dist/test/workspace-three-way.test.js.map +0 -1
  932. package/dist/test/workspace-watcher.test.d.ts +0 -2
  933. package/dist/test/workspace-watcher.test.d.ts.map +0 -1
  934. package/dist/test/workspace-watcher.test.js +0 -281
  935. package/dist/test/workspace-watcher.test.js.map +0 -1
  936. package/dist/test/xml-round-persist.test.d.ts +0 -19
  937. package/dist/test/xml-round-persist.test.d.ts.map +0 -1
  938. package/dist/test/xml-round-persist.test.js +0 -235
  939. package/dist/test/xml-round-persist.test.js.map +0 -1
  940. 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