@animalabs/agent-framework 0.22.1 → 0.22.3

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