@animalabs/agent-framework 0.22.2 → 0.23.0

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