@animalabs/agent-framework 0.22.2 → 0.22.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (935) hide show
  1. package/README.md +3 -1
  2. package/dist/src/agent.d.ts +77 -12
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +136 -40
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/mcp-server-core.js +1 -1
  7. package/dist/src/api/mcp-server-core.js.map +1 -1
  8. package/dist/src/api/server.d.ts.map +1 -1
  9. package/dist/src/api/server.js +5 -1
  10. package/dist/src/api/server.js.map +1 -1
  11. package/dist/src/api/types.d.ts +1 -1
  12. package/dist/src/api/types.d.ts.map +1 -1
  13. package/dist/src/branch-names.d.ts +14 -0
  14. package/dist/src/branch-names.d.ts.map +1 -0
  15. package/dist/src/branch-names.js +22 -0
  16. package/dist/src/branch-names.js.map +1 -0
  17. package/dist/src/closest-names.d.ts +28 -0
  18. package/dist/src/closest-names.d.ts.map +1 -0
  19. package/dist/src/closest-names.js +98 -0
  20. package/dist/src/closest-names.js.map +1 -0
  21. package/dist/src/code-execution/py-runner.d.ts +16 -5
  22. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  23. package/dist/src/code-execution/py-runner.js +186 -74
  24. package/dist/src/code-execution/py-runner.js.map +1 -1
  25. package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
  26. package/dist/src/code-execution/runtime-py.js +49 -3
  27. package/dist/src/code-execution/runtime-py.js.map +1 -1
  28. package/dist/src/code-execution/tool-definition.d.ts +12 -1
  29. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  30. package/dist/src/code-execution/tool-definition.js +21 -3
  31. package/dist/src/code-execution/tool-definition.js.map +1 -1
  32. package/dist/src/config-checks.d.ts +28 -0
  33. package/dist/src/config-checks.d.ts.map +1 -0
  34. package/dist/src/config-checks.js +52 -0
  35. package/dist/src/config-checks.js.map +1 -0
  36. package/dist/src/deadline-timer.d.ts +21 -0
  37. package/dist/src/deadline-timer.d.ts.map +1 -0
  38. package/dist/src/deadline-timer.js +43 -0
  39. package/dist/src/deadline-timer.js.map +1 -0
  40. package/dist/src/error-message.d.ts +30 -0
  41. package/dist/src/error-message.d.ts.map +1 -0
  42. package/dist/src/error-message.js +50 -0
  43. package/dist/src/error-message.js.map +1 -0
  44. package/dist/src/framework.d.ts +828 -88
  45. package/dist/src/framework.d.ts.map +1 -1
  46. package/dist/src/framework.js +5011 -1326
  47. package/dist/src/framework.js.map +1 -1
  48. package/dist/src/gate/event-gate.d.ts +8 -0
  49. package/dist/src/gate/event-gate.d.ts.map +1 -1
  50. package/dist/src/gate/event-gate.js +27 -3
  51. package/dist/src/gate/event-gate.js.map +1 -1
  52. package/dist/src/index.d.ts +4 -2
  53. package/dist/src/index.d.ts.map +1 -1
  54. package/dist/src/index.js +4 -2
  55. package/dist/src/index.js.map +1 -1
  56. package/dist/src/mcpl/channel-registry.d.ts +144 -6
  57. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  58. package/dist/src/mcpl/channel-registry.js +424 -263
  59. package/dist/src/mcpl/channel-registry.js.map +1 -1
  60. package/dist/src/mcpl/checkpoint-manager.d.ts +21 -1
  61. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  62. package/dist/src/mcpl/checkpoint-manager.js +91 -30
  63. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  64. package/dist/src/mcpl/conversation-router.d.ts +19 -3
  65. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  66. package/dist/src/mcpl/conversation-router.js +23 -8
  67. package/dist/src/mcpl/conversation-router.js.map +1 -1
  68. package/dist/src/mcpl/convert-block.d.ts +20 -0
  69. package/dist/src/mcpl/convert-block.d.ts.map +1 -0
  70. package/dist/src/mcpl/convert-block.js +89 -0
  71. package/dist/src/mcpl/convert-block.js.map +1 -0
  72. package/dist/src/mcpl/errors.d.ts +13 -0
  73. package/dist/src/mcpl/errors.d.ts.map +1 -1
  74. package/dist/src/mcpl/errors.js +28 -0
  75. package/dist/src/mcpl/errors.js.map +1 -1
  76. package/dist/src/mcpl/hook-orchestrator.d.ts +23 -0
  77. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  78. package/dist/src/mcpl/hook-orchestrator.js +83 -72
  79. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  80. package/dist/src/mcpl/inbound-source.d.ts +114 -0
  81. package/dist/src/mcpl/inbound-source.d.ts.map +1 -0
  82. package/dist/src/mcpl/inbound-source.js +83 -0
  83. package/dist/src/mcpl/inbound-source.js.map +1 -0
  84. package/dist/src/mcpl/index.d.ts +3 -1
  85. package/dist/src/mcpl/index.d.ts.map +1 -1
  86. package/dist/src/mcpl/index.js +3 -1
  87. package/dist/src/mcpl/index.js.map +1 -1
  88. package/dist/src/mcpl/inference-router.d.ts +15 -11
  89. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  90. package/dist/src/mcpl/inference-router.js +153 -84
  91. package/dist/src/mcpl/inference-router.js.map +1 -1
  92. package/dist/src/mcpl/push-coalescer.d.ts +33 -11
  93. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  94. package/dist/src/mcpl/push-coalescer.js +52 -20
  95. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  96. package/dist/src/mcpl/push-handler.d.ts +16 -9
  97. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  98. package/dist/src/mcpl/push-handler.js +18 -83
  99. package/dist/src/mcpl/push-handler.js.map +1 -1
  100. package/dist/src/mcpl/reference-fetcher.d.ts +14 -2
  101. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  102. package/dist/src/mcpl/reference-fetcher.js +72 -30
  103. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  104. package/dist/src/mcpl/references.d.ts +47 -12
  105. package/dist/src/mcpl/references.d.ts.map +1 -1
  106. package/dist/src/mcpl/references.js +74 -21
  107. package/dist/src/mcpl/references.js.map +1 -1
  108. package/dist/src/mcpl/server-connection.d.ts +89 -10
  109. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  110. package/dist/src/mcpl/server-connection.js +405 -129
  111. package/dist/src/mcpl/server-connection.js.map +1 -1
  112. package/dist/src/mcpl/server-registry.d.ts +10 -1
  113. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  114. package/dist/src/mcpl/server-registry.js +14 -3
  115. package/dist/src/mcpl/server-registry.js.map +1 -1
  116. package/dist/src/mcpl/skip-reply-wake.d.ts +36 -0
  117. package/dist/src/mcpl/skip-reply-wake.d.ts.map +1 -0
  118. package/dist/src/mcpl/skip-reply-wake.js +55 -0
  119. package/dist/src/mcpl/skip-reply-wake.js.map +1 -0
  120. package/dist/src/mcpl/transport.d.ts +51 -2
  121. package/dist/src/mcpl/transport.d.ts.map +1 -1
  122. package/dist/src/mcpl/transport.js +114 -20
  123. package/dist/src/mcpl/transport.js.map +1 -1
  124. package/dist/src/mcpl/types.d.ts +14 -3
  125. package/dist/src/mcpl/types.d.ts.map +1 -1
  126. package/dist/src/mcpl/types.js.map +1 -1
  127. package/dist/src/mcpl/visible-content.d.ts +15 -0
  128. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  129. package/dist/src/mcpl/visible-content.js +25 -0
  130. package/dist/src/mcpl/visible-content.js.map +1 -1
  131. package/dist/src/module-registry.d.ts +19 -1
  132. package/dist/src/module-registry.d.ts.map +1 -1
  133. package/dist/src/module-registry.js +54 -25
  134. package/dist/src/module-registry.js.map +1 -1
  135. package/dist/src/modules/api/index.js +1 -1
  136. package/dist/src/modules/api/index.js.map +1 -1
  137. package/dist/src/modules/discord/discord-js-client.d.ts +1 -0
  138. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
  139. package/dist/src/modules/discord/discord-js-client.js +25 -9
  140. package/dist/src/modules/discord/discord-js-client.js.map +1 -1
  141. package/dist/src/modules/discord/index.d.ts +29 -3
  142. package/dist/src/modules/discord/index.d.ts.map +1 -1
  143. package/dist/src/modules/discord/index.js +379 -143
  144. package/dist/src/modules/discord/index.js.map +1 -1
  145. package/dist/src/modules/discord/types.d.ts +14 -1
  146. package/dist/src/modules/discord/types.d.ts.map +1 -1
  147. package/dist/src/modules/health/index.d.ts.map +1 -1
  148. package/dist/src/modules/health/index.js +2 -1
  149. package/dist/src/modules/health/index.js.map +1 -1
  150. package/dist/src/modules/history/index.d.ts +3 -1
  151. package/dist/src/modules/history/index.d.ts.map +1 -1
  152. package/dist/src/modules/history/index.js +359 -127
  153. package/dist/src/modules/history/index.js.map +1 -1
  154. package/dist/src/modules/history/semantic.d.ts +28 -1
  155. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  156. package/dist/src/modules/history/semantic.js +177 -32
  157. package/dist/src/modules/history/semantic.js.map +1 -1
  158. package/dist/src/modules/unknown-tool.d.ts +10 -0
  159. package/dist/src/modules/unknown-tool.d.ts.map +1 -0
  160. package/dist/src/modules/unknown-tool.js +17 -0
  161. package/dist/src/modules/unknown-tool.js.map +1 -0
  162. package/dist/src/modules/workspace/disk-agreement.d.ts +32 -2
  163. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
  164. package/dist/src/modules/workspace/disk-agreement.js +30 -2
  165. package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
  166. package/dist/src/modules/workspace/effects.d.ts +64 -6
  167. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  168. package/dist/src/modules/workspace/effects.js +291 -44
  169. package/dist/src/modules/workspace/effects.js.map +1 -1
  170. package/dist/src/modules/workspace/index.d.ts +119 -23
  171. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  172. package/dist/src/modules/workspace/index.js +793 -247
  173. package/dist/src/modules/workspace/index.js.map +1 -1
  174. package/dist/src/modules/workspace/observe.d.ts +75 -8
  175. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  176. package/dist/src/modules/workspace/observe.js +174 -14
  177. package/dist/src/modules/workspace/observe.js.map +1 -1
  178. package/dist/src/modules/workspace/reconcile.d.ts +58 -7
  179. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  180. package/dist/src/modules/workspace/reconcile.js +521 -56
  181. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  182. package/dist/src/modules/workspace/types.d.ts +18 -4
  183. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  184. package/dist/src/modules/workspace/types.js.map +1 -1
  185. package/dist/src/modules/workspace/watcher.d.ts +7 -2
  186. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  187. package/dist/src/modules/workspace/watcher.js +28 -4
  188. package/dist/src/modules/workspace/watcher.js.map +1 -1
  189. package/dist/src/prose-drafts.d.ts +240 -0
  190. package/dist/src/prose-drafts.d.ts.map +1 -0
  191. package/dist/src/prose-drafts.js +362 -0
  192. package/dist/src/prose-drafts.js.map +1 -0
  193. package/dist/src/prose-segments.d.ts +21 -2
  194. package/dist/src/prose-segments.d.ts.map +1 -1
  195. package/dist/src/prose-segments.js +46 -9
  196. package/dist/src/prose-segments.js.map +1 -1
  197. package/dist/src/recovery/offline-branch.d.ts +1 -0
  198. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  199. package/dist/src/recovery/offline-branch.js +3 -1
  200. package/dist/src/recovery/offline-branch.js.map +1 -1
  201. package/dist/src/safe-slice.d.ts +5 -1
  202. package/dist/src/safe-slice.d.ts.map +1 -1
  203. package/dist/src/safe-slice.js +9 -1
  204. package/dist/src/safe-slice.js.map +1 -1
  205. package/dist/src/tool-image-ledger.d.ts +87 -19
  206. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  207. package/dist/src/tool-image-ledger.js +135 -32
  208. package/dist/src/tool-image-ledger.js.map +1 -1
  209. package/dist/src/tool-presentation.d.ts +9 -0
  210. package/dist/src/tool-presentation.d.ts.map +1 -1
  211. package/dist/src/tool-presentation.js +257 -60
  212. package/dist/src/tool-presentation.js.map +1 -1
  213. package/dist/src/tool-result-guard.d.ts.map +1 -1
  214. package/dist/src/tool-result-guard.js +7 -10
  215. package/dist/src/tool-result-guard.js.map +1 -1
  216. package/dist/src/tool-result-history.d.ts +32 -3
  217. package/dist/src/tool-result-history.d.ts.map +1 -1
  218. package/dist/src/tool-result-history.js +106 -19
  219. package/dist/src/tool-result-history.js.map +1 -1
  220. package/dist/src/tune-out/coordinator.d.ts +25 -1
  221. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  222. package/dist/src/tune-out/coordinator.js +260 -65
  223. package/dist/src/tune-out/coordinator.js.map +1 -1
  224. package/dist/src/types/agent.d.ts +17 -5
  225. package/dist/src/types/agent.d.ts.map +1 -1
  226. package/dist/src/types/events.d.ts +17 -0
  227. package/dist/src/types/events.d.ts.map +1 -1
  228. package/dist/src/types/framework.d.ts +21 -5
  229. package/dist/src/types/framework.d.ts.map +1 -1
  230. package/dist/src/types/inference-log.d.ts +1 -1
  231. package/dist/src/types/inference-log.d.ts.map +1 -1
  232. package/dist/src/types/module.d.ts +23 -4
  233. package/dist/src/types/module.d.ts.map +1 -1
  234. package/dist/src/types/trace.d.ts +95 -6
  235. package/dist/src/types/trace.d.ts.map +1 -1
  236. package/dist/src/usage/usage-tracker.d.ts +5 -0
  237. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  238. package/dist/src/usage/usage-tracker.js +35 -8
  239. package/dist/src/usage/usage-tracker.js.map +1 -1
  240. package/package.json +3 -4
  241. package/src/agent.ts +162 -40
  242. package/src/api/mcp-server-core.ts +1 -1
  243. package/src/api/server.ts +4 -1
  244. package/src/api/types.ts +1 -1
  245. package/src/branch-names.ts +19 -0
  246. package/src/closest-names.ts +94 -0
  247. package/src/code-execution/py-runner.ts +200 -76
  248. package/src/code-execution/runtime-py.ts +49 -3
  249. package/src/code-execution/tool-definition.ts +28 -4
  250. package/src/config-checks.ts +48 -0
  251. package/src/deadline-timer.ts +44 -0
  252. package/src/error-message.ts +48 -0
  253. package/src/framework.ts +5352 -1388
  254. package/src/gate/event-gate.ts +28 -3
  255. package/src/index.ts +11 -2
  256. package/src/mcpl/channel-registry.ts +517 -286
  257. package/src/mcpl/checkpoint-manager.ts +104 -29
  258. package/src/mcpl/conversation-router.ts +28 -8
  259. package/src/mcpl/convert-block.ts +97 -0
  260. package/src/mcpl/errors.ts +28 -0
  261. package/src/mcpl/hook-orchestrator.ts +84 -77
  262. package/src/mcpl/inbound-source.ts +156 -0
  263. package/src/mcpl/index.ts +10 -1
  264. package/src/mcpl/inference-router.ts +174 -99
  265. package/src/mcpl/push-coalescer.ts +60 -24
  266. package/src/mcpl/push-handler.ts +20 -90
  267. package/src/mcpl/reference-fetcher.ts +71 -26
  268. package/src/mcpl/references.ts +81 -22
  269. package/src/mcpl/server-connection.ts +427 -132
  270. package/src/mcpl/server-registry.ts +15 -4
  271. package/src/mcpl/skip-reply-wake.ts +73 -0
  272. package/src/mcpl/transport.ts +120 -18
  273. package/src/mcpl/types.ts +14 -3
  274. package/src/mcpl/visible-content.ts +27 -0
  275. package/src/module-registry.ts +58 -23
  276. package/src/modules/api/index.ts +1 -1
  277. package/src/modules/discord/discord-js-client.ts +26 -10
  278. package/src/modules/discord/index.ts +391 -158
  279. package/src/modules/discord/types.ts +15 -2
  280. package/src/modules/health/index.ts +2 -1
  281. package/src/modules/history/index.ts +410 -120
  282. package/src/modules/history/semantic.ts +189 -32
  283. package/src/modules/unknown-tool.ts +16 -0
  284. package/src/modules/workspace/disk-agreement.ts +51 -3
  285. package/src/modules/workspace/effects.ts +279 -35
  286. package/src/modules/workspace/index.ts +810 -260
  287. package/src/modules/workspace/observe.ts +194 -20
  288. package/src/modules/workspace/reconcile.ts +619 -64
  289. package/src/modules/workspace/types.ts +18 -4
  290. package/src/modules/workspace/watcher.ts +27 -4
  291. package/src/prose-drafts.ts +481 -0
  292. package/src/prose-segments.ts +51 -8
  293. package/src/recovery/offline-branch.ts +4 -1
  294. package/src/safe-slice.ts +9 -1
  295. package/src/tool-image-ledger.ts +153 -34
  296. package/src/tool-presentation.ts +224 -40
  297. package/src/tool-result-guard.ts +7 -8
  298. package/src/tool-result-history.ts +124 -20
  299. package/src/tune-out/coordinator.ts +259 -65
  300. package/src/types/agent.ts +17 -7
  301. package/src/types/events.ts +17 -0
  302. package/src/types/framework.ts +21 -5
  303. package/src/types/inference-log.ts +1 -1
  304. package/src/types/module.ts +24 -4
  305. package/src/types/trace.ts +101 -6
  306. package/src/usage/usage-tracker.ts +32 -8
  307. package/dist/test/activation-request-sanitize.test.d.ts +0 -2
  308. package/dist/test/activation-request-sanitize.test.d.ts.map +0 -1
  309. package/dist/test/activation-request-sanitize.test.js +0 -89
  310. package/dist/test/activation-request-sanitize.test.js.map +0 -1
  311. package/dist/test/agent-cache-ttl.test.d.ts +0 -2
  312. package/dist/test/agent-cache-ttl.test.d.ts.map +0 -1
  313. package/dist/test/agent-cache-ttl.test.js +0 -41
  314. package/dist/test/agent-cache-ttl.test.js.map +0 -1
  315. package/dist/test/agent-runtime-settings.test.d.ts +0 -2
  316. package/dist/test/agent-runtime-settings.test.d.ts.map +0 -1
  317. package/dist/test/agent-runtime-settings.test.js +0 -1598
  318. package/dist/test/agent-runtime-settings.test.js.map +0 -1
  319. package/dist/test/api-admin-gate.test.d.ts +0 -2
  320. package/dist/test/api-admin-gate.test.d.ts.map +0 -1
  321. package/dist/test/api-admin-gate.test.js +0 -116
  322. package/dist/test/api-admin-gate.test.js.map +0 -1
  323. package/dist/test/assistant-persist-refusal.test.d.ts +0 -2
  324. package/dist/test/assistant-persist-refusal.test.d.ts.map +0 -1
  325. package/dist/test/assistant-persist-refusal.test.js +0 -63
  326. package/dist/test/assistant-persist-refusal.test.js.map +0 -1
  327. package/dist/test/batched-wake-locus.test.d.ts +0 -2
  328. package/dist/test/batched-wake-locus.test.d.ts.map +0 -1
  329. package/dist/test/batched-wake-locus.test.js +0 -166
  330. package/dist/test/batched-wake-locus.test.js.map +0 -1
  331. package/dist/test/busy-agent-scheduler.test.d.ts +0 -2
  332. package/dist/test/busy-agent-scheduler.test.d.ts.map +0 -1
  333. package/dist/test/busy-agent-scheduler.test.js +0 -92
  334. package/dist/test/busy-agent-scheduler.test.js.map +0 -1
  335. package/dist/test/channel-incoming-targeting.test.d.ts +0 -2
  336. package/dist/test/channel-incoming-targeting.test.d.ts.map +0 -1
  337. package/dist/test/channel-incoming-targeting.test.js +0 -81
  338. package/dist/test/channel-incoming-targeting.test.js.map +0 -1
  339. package/dist/test/channel-label-history.test.d.ts +0 -2
  340. package/dist/test/channel-label-history.test.d.ts.map +0 -1
  341. package/dist/test/channel-label-history.test.js +0 -424
  342. package/dist/test/channel-label-history.test.js.map +0 -1
  343. package/dist/test/channel-lifecycle-provenance.test.d.ts +0 -2
  344. package/dist/test/channel-lifecycle-provenance.test.d.ts.map +0 -1
  345. package/dist/test/channel-lifecycle-provenance.test.js +0 -47
  346. package/dist/test/channel-lifecycle-provenance.test.js.map +0 -1
  347. package/dist/test/channel-registry-routing.test.d.ts +0 -2
  348. package/dist/test/channel-registry-routing.test.d.ts.map +0 -1
  349. package/dist/test/channel-registry-routing.test.js +0 -446
  350. package/dist/test/channel-registry-routing.test.js.map +0 -1
  351. package/dist/test/channel-registry-typing.test.d.ts +0 -2
  352. package/dist/test/channel-registry-typing.test.d.ts.map +0 -1
  353. package/dist/test/channel-registry-typing.test.js +0 -91
  354. package/dist/test/channel-registry-typing.test.js.map +0 -1
  355. package/dist/test/channels-changed-respond-first.test.d.ts +0 -2
  356. package/dist/test/channels-changed-respond-first.test.d.ts.map +0 -1
  357. package/dist/test/channels-changed-respond-first.test.js +0 -74
  358. package/dist/test/channels-changed-respond-first.test.js.map +0 -1
  359. package/dist/test/checkpoint-attribution.test.d.ts +0 -2
  360. package/dist/test/checkpoint-attribution.test.d.ts.map +0 -1
  361. package/dist/test/checkpoint-attribution.test.js +0 -200
  362. package/dist/test/checkpoint-attribution.test.js.map +0 -1
  363. package/dist/test/code-execution.test.d.ts +0 -11
  364. package/dist/test/code-execution.test.d.ts.map +0 -1
  365. package/dist/test/code-execution.test.js +0 -1272
  366. package/dist/test/code-execution.test.js.map +0 -1
  367. package/dist/test/component-defaults.test.d.ts +0 -2
  368. package/dist/test/component-defaults.test.d.ts.map +0 -1
  369. package/dist/test/component-defaults.test.js +0 -54
  370. package/dist/test/component-defaults.test.js.map +0 -1
  371. package/dist/test/conversation-fork-injection-scoping.test.d.ts +0 -2
  372. package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +0 -1
  373. package/dist/test/conversation-fork-injection-scoping.test.js +0 -200
  374. package/dist/test/conversation-fork-injection-scoping.test.js.map +0 -1
  375. package/dist/test/conversation-router.test.d.ts +0 -2
  376. package/dist/test/conversation-router.test.d.ts.map +0 -1
  377. package/dist/test/conversation-router.test.js +0 -174
  378. package/dist/test/conversation-router.test.js.map +0 -1
  379. package/dist/test/conversation-routing.test.d.ts +0 -7
  380. package/dist/test/conversation-routing.test.d.ts.map +0 -1
  381. package/dist/test/conversation-routing.test.js +0 -621
  382. package/dist/test/conversation-routing.test.js.map +0 -1
  383. package/dist/test/deferred-wake-integrity.test.d.ts +0 -29
  384. package/dist/test/deferred-wake-integrity.test.d.ts.map +0 -1
  385. package/dist/test/deferred-wake-integrity.test.js +0 -320
  386. package/dist/test/deferred-wake-integrity.test.js.map +0 -1
  387. package/dist/test/discord-awareness-outbox.test.d.ts +0 -2
  388. package/dist/test/discord-awareness-outbox.test.d.ts.map +0 -1
  389. package/dist/test/discord-awareness-outbox.test.js +0 -371
  390. package/dist/test/discord-awareness-outbox.test.js.map +0 -1
  391. package/dist/test/discord-history-delete-window.test.d.ts +0 -2
  392. package/dist/test/discord-history-delete-window.test.d.ts.map +0 -1
  393. package/dist/test/discord-history-delete-window.test.js +0 -81
  394. package/dist/test/discord-history-delete-window.test.js.map +0 -1
  395. package/dist/test/discord-history-sync.test.d.ts +0 -2
  396. package/dist/test/discord-history-sync.test.d.ts.map +0 -1
  397. package/dist/test/discord-history-sync.test.js +0 -56
  398. package/dist/test/discord-history-sync.test.js.map +0 -1
  399. package/dist/test/discord-typing-keepalive.test.d.ts +0 -2
  400. package/dist/test/discord-typing-keepalive.test.d.ts.map +0 -1
  401. package/dist/test/discord-typing-keepalive.test.js +0 -77
  402. package/dist/test/discord-typing-keepalive.test.js.map +0 -1
  403. package/dist/test/ephemeral-settle.test.d.ts +0 -2
  404. package/dist/test/ephemeral-settle.test.d.ts.map +0 -1
  405. package/dist/test/ephemeral-settle.test.js +0 -354
  406. package/dist/test/ephemeral-settle.test.js.map +0 -1
  407. package/dist/test/event-gate-passthrough.test.d.ts +0 -15
  408. package/dist/test/event-gate-passthrough.test.d.ts.map +0 -1
  409. package/dist/test/event-gate-passthrough.test.js +0 -289
  410. package/dist/test/event-gate-passthrough.test.js.map +0 -1
  411. package/dist/test/event-gate-policy-mutation.test.d.ts +0 -2
  412. package/dist/test/event-gate-policy-mutation.test.d.ts.map +0 -1
  413. package/dist/test/event-gate-policy-mutation.test.js +0 -137
  414. package/dist/test/event-gate-policy-mutation.test.js.map +0 -1
  415. package/dist/test/event-gate-rate-limit.test.d.ts +0 -2
  416. package/dist/test/event-gate-rate-limit.test.d.ts.map +0 -1
  417. package/dist/test/event-gate-rate-limit.test.js +0 -627
  418. package/dist/test/event-gate-rate-limit.test.js.map +0 -1
  419. package/dist/test/event-gate-script.test.d.ts +0 -2
  420. package/dist/test/event-gate-script.test.d.ts.map +0 -1
  421. package/dist/test/event-gate-script.test.js +0 -95
  422. package/dist/test/event-gate-script.test.js.map +0 -1
  423. package/dist/test/event-gate-selfwake.test.d.ts +0 -15
  424. package/dist/test/event-gate-selfwake.test.d.ts.map +0 -1
  425. package/dist/test/event-gate-selfwake.test.js +0 -125
  426. package/dist/test/event-gate-selfwake.test.js.map +0 -1
  427. package/dist/test/event-gate-tags.test.d.ts +0 -2
  428. package/dist/test/event-gate-tags.test.d.ts.map +0 -1
  429. package/dist/test/event-gate-tags.test.js +0 -76
  430. package/dist/test/event-gate-tags.test.js.map +0 -1
  431. package/dist/test/event-gate.test.d.ts +0 -2
  432. package/dist/test/event-gate.test.d.ts.map +0 -1
  433. package/dist/test/event-gate.test.js +0 -919
  434. package/dist/test/event-gate.test.js.map +0 -1
  435. package/dist/test/explicit-prose-routing.test.d.ts +0 -11
  436. package/dist/test/explicit-prose-routing.test.d.ts.map +0 -1
  437. package/dist/test/explicit-prose-routing.test.js +0 -1715
  438. package/dist/test/explicit-prose-routing.test.js.map +0 -1
  439. package/dist/test/framework.test.d.ts +0 -5
  440. package/dist/test/framework.test.d.ts.map +0 -1
  441. package/dist/test/framework.test.js +0 -1048
  442. package/dist/test/framework.test.js.map +0 -1
  443. package/dist/test/gather-context-timeout.test.d.ts +0 -2
  444. package/dist/test/gather-context-timeout.test.d.ts.map +0 -1
  445. package/dist/test/gather-context-timeout.test.js +0 -120
  446. package/dist/test/gather-context-timeout.test.js.map +0 -1
  447. package/dist/test/health-snapshot.test.d.ts +0 -2
  448. package/dist/test/health-snapshot.test.d.ts.map +0 -1
  449. package/dist/test/health-snapshot.test.js +0 -74
  450. package/dist/test/health-snapshot.test.js.map +0 -1
  451. package/dist/test/helpers/coalescing-fixture.d.ts +0 -74
  452. package/dist/test/helpers/coalescing-fixture.d.ts.map +0 -1
  453. package/dist/test/helpers/coalescing-fixture.js +0 -152
  454. package/dist/test/helpers/coalescing-fixture.js.map +0 -1
  455. package/dist/test/helpers/coalescing-kill-child.d.ts +0 -2
  456. package/dist/test/helpers/coalescing-kill-child.d.ts.map +0 -1
  457. package/dist/test/helpers/coalescing-kill-child.js +0 -24
  458. package/dist/test/helpers/coalescing-kill-child.js.map +0 -1
  459. package/dist/test/helpers/mock-membrane.d.ts +0 -53
  460. package/dist/test/helpers/mock-membrane.d.ts.map +0 -1
  461. package/dist/test/helpers/mock-membrane.js +0 -168
  462. package/dist/test/helpers/mock-membrane.js.map +0 -1
  463. package/dist/test/helpers/pytc-stop-rows.d.ts +0 -11
  464. package/dist/test/helpers/pytc-stop-rows.d.ts.map +0 -1
  465. package/dist/test/helpers/pytc-stop-rows.js +0 -143
  466. package/dist/test/helpers/pytc-stop-rows.js.map +0 -1
  467. package/dist/test/helpers/quiesce-crash-child.d.ts +0 -2
  468. package/dist/test/helpers/quiesce-crash-child.d.ts.map +0 -1
  469. package/dist/test/helpers/quiesce-crash-child.js +0 -156
  470. package/dist/test/helpers/quiesce-crash-child.js.map +0 -1
  471. package/dist/test/hide-then-turn.test.d.ts +0 -2
  472. package/dist/test/hide-then-turn.test.d.ts.map +0 -1
  473. package/dist/test/hide-then-turn.test.js +0 -101
  474. package/dist/test/hide-then-turn.test.js.map +0 -1
  475. package/dist/test/history-module-overview.test.d.ts +0 -2
  476. package/dist/test/history-module-overview.test.d.ts.map +0 -1
  477. package/dist/test/history-module-overview.test.js +0 -711
  478. package/dist/test/history-module-overview.test.js.map +0 -1
  479. package/dist/test/history-module-realstore.test.d.ts +0 -2
  480. package/dist/test/history-module-realstore.test.d.ts.map +0 -1
  481. package/dist/test/history-module-realstore.test.js +0 -363
  482. package/dist/test/history-module-realstore.test.js.map +0 -1
  483. package/dist/test/history-module-semantic.test.d.ts +0 -2
  484. package/dist/test/history-module-semantic.test.d.ts.map +0 -1
  485. package/dist/test/history-module-semantic.test.js +0 -213
  486. package/dist/test/history-module-semantic.test.js.map +0 -1
  487. package/dist/test/history-module-ux.test.d.ts +0 -2
  488. package/dist/test/history-module-ux.test.d.ts.map +0 -1
  489. package/dist/test/history-module-ux.test.js +0 -304
  490. package/dist/test/history-module-ux.test.js.map +0 -1
  491. package/dist/test/history-module.test.d.ts +0 -2
  492. package/dist/test/history-module.test.d.ts.map +0 -1
  493. package/dist/test/history-module.test.js +0 -619
  494. package/dist/test/history-module.test.js.map +0 -1
  495. package/dist/test/history-semantic-review-findings.test.d.ts +0 -2
  496. package/dist/test/history-semantic-review-findings.test.d.ts.map +0 -1
  497. package/dist/test/history-semantic-review-findings.test.js +0 -338
  498. package/dist/test/history-semantic-review-findings.test.js.map +0 -1
  499. package/dist/test/history-semantic-sync-converges.test.d.ts +0 -2
  500. package/dist/test/history-semantic-sync-converges.test.d.ts.map +0 -1
  501. package/dist/test/history-semantic-sync-converges.test.js +0 -109
  502. package/dist/test/history-semantic-sync-converges.test.js.map +0 -1
  503. package/dist/test/host-quiesce.test.d.ts +0 -2
  504. package/dist/test/host-quiesce.test.d.ts.map +0 -1
  505. package/dist/test/host-quiesce.test.js +0 -988
  506. package/dist/test/host-quiesce.test.js.map +0 -1
  507. package/dist/test/inference-exhaustion-rewind.test.d.ts +0 -2
  508. package/dist/test/inference-exhaustion-rewind.test.d.ts.map +0 -1
  509. package/dist/test/inference-exhaustion-rewind.test.js +0 -387
  510. package/dist/test/inference-exhaustion-rewind.test.js.map +0 -1
  511. package/dist/test/inference-failure-bounds.test.d.ts +0 -2
  512. package/dist/test/inference-failure-bounds.test.d.ts.map +0 -1
  513. package/dist/test/inference-failure-bounds.test.js +0 -1202
  514. package/dist/test/inference-failure-bounds.test.js.map +0 -1
  515. package/dist/test/inference-failure-observability.test.d.ts +0 -2
  516. package/dist/test/inference-failure-observability.test.d.ts.map +0 -1
  517. package/dist/test/inference-failure-observability.test.js +0 -84
  518. package/dist/test/inference-failure-observability.test.js.map +0 -1
  519. package/dist/test/journal-tool.test.d.ts +0 -2
  520. package/dist/test/journal-tool.test.d.ts.map +0 -1
  521. package/dist/test/journal-tool.test.js +0 -33
  522. package/dist/test/journal-tool.test.js.map +0 -1
  523. package/dist/test/kv-unified-open-flight-retry.test.d.ts +0 -2
  524. package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +0 -1
  525. package/dist/test/kv-unified-open-flight-retry.test.js +0 -189
  526. package/dist/test/kv-unified-open-flight-retry.test.js.map +0 -1
  527. package/dist/test/kv-unified-wiring.test.d.ts +0 -2
  528. package/dist/test/kv-unified-wiring.test.d.ts.map +0 -1
  529. package/dist/test/kv-unified-wiring.test.js +0 -95
  530. package/dist/test/kv-unified-wiring.test.js.map +0 -1
  531. package/dist/test/late-events-after-stop.test.d.ts +0 -2
  532. package/dist/test/late-events-after-stop.test.d.ts.map +0 -1
  533. package/dist/test/late-events-after-stop.test.js +0 -122
  534. package/dist/test/late-events-after-stop.test.js.map +0 -1
  535. package/dist/test/liveness-watchdog.test.d.ts +0 -2
  536. package/dist/test/liveness-watchdog.test.d.ts.map +0 -1
  537. package/dist/test/liveness-watchdog.test.js +0 -94
  538. package/dist/test/liveness-watchdog.test.js.map +0 -1
  539. package/dist/test/machine-close-provenance.test.d.ts +0 -2
  540. package/dist/test/machine-close-provenance.test.d.ts.map +0 -1
  541. package/dist/test/machine-close-provenance.test.js +0 -116
  542. package/dist/test/machine-close-provenance.test.js.map +0 -1
  543. package/dist/test/maintenance-timer.test.d.ts +0 -2
  544. package/dist/test/maintenance-timer.test.d.ts.map +0 -1
  545. package/dist/test/maintenance-timer.test.js +0 -1827
  546. package/dist/test/maintenance-timer.test.js.map +0 -1
  547. package/dist/test/mcp-server-bin.test.d.ts +0 -2
  548. package/dist/test/mcp-server-bin.test.d.ts.map +0 -1
  549. package/dist/test/mcp-server-bin.test.js +0 -142
  550. package/dist/test/mcp-server-bin.test.js.map +0 -1
  551. package/dist/test/mcpl-access-provider.test.d.ts +0 -2
  552. package/dist/test/mcpl-access-provider.test.d.ts.map +0 -1
  553. package/dist/test/mcpl-access-provider.test.js +0 -20
  554. package/dist/test/mcpl-access-provider.test.js.map +0 -1
  555. package/dist/test/mcpl-awareness-barrier.test.d.ts +0 -2
  556. package/dist/test/mcpl-awareness-barrier.test.d.ts.map +0 -1
  557. package/dist/test/mcpl-awareness-barrier.test.js +0 -769
  558. package/dist/test/mcpl-awareness-barrier.test.js.map +0 -1
  559. package/dist/test/mcpl-capability-grant.test.d.ts +0 -10
  560. package/dist/test/mcpl-capability-grant.test.d.ts.map +0 -1
  561. package/dist/test/mcpl-capability-grant.test.js +0 -111
  562. package/dist/test/mcpl-capability-grant.test.js.map +0 -1
  563. package/dist/test/mcpl-capability-scoping.test.d.ts +0 -2
  564. package/dist/test/mcpl-capability-scoping.test.d.ts.map +0 -1
  565. package/dist/test/mcpl-capability-scoping.test.js +0 -266
  566. package/dist/test/mcpl-capability-scoping.test.js.map +0 -1
  567. package/dist/test/mcpl-child-env.test.d.ts +0 -2
  568. package/dist/test/mcpl-child-env.test.d.ts.map +0 -1
  569. package/dist/test/mcpl-child-env.test.js +0 -158
  570. package/dist/test/mcpl-child-env.test.js.map +0 -1
  571. package/dist/test/mcpl-coalesced-content-parity.test.d.ts +0 -2
  572. package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +0 -1
  573. package/dist/test/mcpl-coalesced-content-parity.test.js +0 -532
  574. package/dist/test/mcpl-coalesced-content-parity.test.js.map +0 -1
  575. package/dist/test/mcpl-coalescing.test.d.ts +0 -2
  576. package/dist/test/mcpl-coalescing.test.d.ts.map +0 -1
  577. package/dist/test/mcpl-coalescing.test.js +0 -1125
  578. package/dist/test/mcpl-coalescing.test.js.map +0 -1
  579. package/dist/test/mcpl-config-diagnostics.test.d.ts +0 -2
  580. package/dist/test/mcpl-config-diagnostics.test.d.ts.map +0 -1
  581. package/dist/test/mcpl-config-diagnostics.test.js +0 -452
  582. package/dist/test/mcpl-config-diagnostics.test.js.map +0 -1
  583. package/dist/test/mcpl-empty-content.test.d.ts +0 -2
  584. package/dist/test/mcpl-empty-content.test.d.ts.map +0 -1
  585. package/dist/test/mcpl-empty-content.test.js +0 -368
  586. package/dist/test/mcpl-empty-content.test.js.map +0 -1
  587. package/dist/test/mcpl-gate-roundtrip.test.d.ts +0 -2
  588. package/dist/test/mcpl-gate-roundtrip.test.d.ts.map +0 -1
  589. package/dist/test/mcpl-gate-roundtrip.test.js +0 -285
  590. package/dist/test/mcpl-gate-roundtrip.test.js.map +0 -1
  591. package/dist/test/mcpl-list-status.test.d.ts +0 -2
  592. package/dist/test/mcpl-list-status.test.d.ts.map +0 -1
  593. package/dist/test/mcpl-list-status.test.js +0 -82
  594. package/dist/test/mcpl-list-status.test.js.map +0 -1
  595. package/dist/test/mcpl-manifest-changed.test.d.ts +0 -2
  596. package/dist/test/mcpl-manifest-changed.test.d.ts.map +0 -1
  597. package/dist/test/mcpl-manifest-changed.test.js +0 -178
  598. package/dist/test/mcpl-manifest-changed.test.js.map +0 -1
  599. package/dist/test/mcpl-push-convert.test.d.ts +0 -2
  600. package/dist/test/mcpl-push-convert.test.d.ts.map +0 -1
  601. package/dist/test/mcpl-push-convert.test.js +0 -42
  602. package/dist/test/mcpl-push-convert.test.js.map +0 -1
  603. package/dist/test/mcpl-reconnect-events.test.d.ts +0 -2
  604. package/dist/test/mcpl-reconnect-events.test.d.ts.map +0 -1
  605. package/dist/test/mcpl-reconnect-events.test.js +0 -144
  606. package/dist/test/mcpl-reconnect-events.test.js.map +0 -1
  607. package/dist/test/mcpl-reconnect-reregistration.test.d.ts +0 -2
  608. package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +0 -1
  609. package/dist/test/mcpl-reconnect-reregistration.test.js +0 -238
  610. package/dist/test/mcpl-reconnect-reregistration.test.js.map +0 -1
  611. package/dist/test/mcpl-reference-fetcher.test.d.ts +0 -2
  612. package/dist/test/mcpl-reference-fetcher.test.d.ts.map +0 -1
  613. package/dist/test/mcpl-reference-fetcher.test.js +0 -427
  614. package/dist/test/mcpl-reference-fetcher.test.js.map +0 -1
  615. package/dist/test/mcpl-reference-fields.test.d.ts +0 -2
  616. package/dist/test/mcpl-reference-fields.test.d.ts.map +0 -1
  617. package/dist/test/mcpl-reference-fields.test.js +0 -149
  618. package/dist/test/mcpl-reference-fields.test.js.map +0 -1
  619. package/dist/test/mcpl-rejection-log-lines.test.d.ts +0 -2
  620. package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +0 -1
  621. package/dist/test/mcpl-rejection-log-lines.test.js +0 -349
  622. package/dist/test/mcpl-rejection-log-lines.test.js.map +0 -1
  623. package/dist/test/mcpl-request-timeout.test.d.ts +0 -2
  624. package/dist/test/mcpl-request-timeout.test.d.ts.map +0 -1
  625. package/dist/test/mcpl-request-timeout.test.js +0 -138
  626. package/dist/test/mcpl-request-timeout.test.js.map +0 -1
  627. package/dist/test/mcpl-runtime-lifecycle.test.d.ts +0 -7
  628. package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +0 -1
  629. package/dist/test/mcpl-runtime-lifecycle.test.js +0 -142
  630. package/dist/test/mcpl-runtime-lifecycle.test.js.map +0 -1
  631. package/dist/test/mcpl-stderr-tail.test.d.ts +0 -2
  632. package/dist/test/mcpl-stderr-tail.test.d.ts.map +0 -1
  633. package/dist/test/mcpl-stderr-tail.test.js +0 -210
  634. package/dist/test/mcpl-stderr-tail.test.js.map +0 -1
  635. package/dist/test/mcpl-tool-class-listing.test.d.ts +0 -2
  636. package/dist/test/mcpl-tool-class-listing.test.d.ts.map +0 -1
  637. package/dist/test/mcpl-tool-class-listing.test.js +0 -147
  638. package/dist/test/mcpl-tool-class-listing.test.js.map +0 -1
  639. package/dist/test/mcpl-tool-classes.test.d.ts +0 -7
  640. package/dist/test/mcpl-tool-classes.test.d.ts.map +0 -1
  641. package/dist/test/mcpl-tool-classes.test.js +0 -80
  642. package/dist/test/mcpl-tool-classes.test.js.map +0 -1
  643. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts +0 -2
  644. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts.map +0 -1
  645. package/dist/test/mcpl-tool-lifecycle-e2e.test.js +0 -237
  646. package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +0 -1
  647. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts +0 -11
  648. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts.map +0 -1
  649. package/dist/test/mcpl-tool-lifecycle-emitter.test.js +0 -412
  650. package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +0 -1
  651. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +0 -2
  652. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +0 -1
  653. package/dist/test/mcpl-tool-lifecycle-script.test.js +0 -288
  654. package/dist/test/mcpl-tool-lifecycle-script.test.js.map +0 -1
  655. package/dist/test/mcpl-tool-lifecycle.test.d.ts +0 -11
  656. package/dist/test/mcpl-tool-lifecycle.test.d.ts.map +0 -1
  657. package/dist/test/mcpl-tool-lifecycle.test.js +0 -270
  658. package/dist/test/mcpl-tool-lifecycle.test.js.map +0 -1
  659. package/dist/test/mcpl-transport-close.test.d.ts +0 -2
  660. package/dist/test/mcpl-transport-close.test.d.ts.map +0 -1
  661. package/dist/test/mcpl-transport-close.test.js +0 -373
  662. package/dist/test/mcpl-transport-close.test.js.map +0 -1
  663. package/dist/test/mcpl-transport-spawn.test.d.ts +0 -2
  664. package/dist/test/mcpl-transport-spawn.test.d.ts.map +0 -1
  665. package/dist/test/mcpl-transport-spawn.test.js +0 -58
  666. package/dist/test/mcpl-transport-spawn.test.js.map +0 -1
  667. package/dist/test/mcpl-ws-transport.test.d.ts +0 -2
  668. package/dist/test/mcpl-ws-transport.test.d.ts.map +0 -1
  669. package/dist/test/mcpl-ws-transport.test.js +0 -259
  670. package/dist/test/mcpl-ws-transport.test.js.map +0 -1
  671. package/dist/test/module-context-integration.test.d.ts +0 -18
  672. package/dist/test/module-context-integration.test.d.ts.map +0 -1
  673. package/dist/test/module-context-integration.test.js +0 -181
  674. package/dist/test/module-context-integration.test.js.map +0 -1
  675. package/dist/test/offline-recovery-branch.test.d.ts +0 -2
  676. package/dist/test/offline-recovery-branch.test.d.ts.map +0 -1
  677. package/dist/test/offline-recovery-branch.test.js +0 -570
  678. package/dist/test/offline-recovery-branch.test.js.map +0 -1
  679. package/dist/test/operator-surgery.test.d.ts +0 -2
  680. package/dist/test/operator-surgery.test.d.ts.map +0 -1
  681. package/dist/test/operator-surgery.test.js +0 -595
  682. package/dist/test/operator-surgery.test.js.map +0 -1
  683. package/dist/test/ops-alert.test.d.ts +0 -2
  684. package/dist/test/ops-alert.test.d.ts.map +0 -1
  685. package/dist/test/ops-alert.test.js +0 -153
  686. package/dist/test/ops-alert.test.js.map +0 -1
  687. package/dist/test/overbudget-drain-breaker.test.d.ts +0 -2
  688. package/dist/test/overbudget-drain-breaker.test.d.ts.map +0 -1
  689. package/dist/test/overbudget-drain-breaker.test.js +0 -198
  690. package/dist/test/overbudget-drain-breaker.test.js.map +0 -1
  691. package/dist/test/pack-installs-alone.test.d.ts +0 -2
  692. package/dist/test/pack-installs-alone.test.d.ts.map +0 -1
  693. package/dist/test/pack-installs-alone.test.js +0 -284
  694. package/dist/test/pack-installs-alone.test.js.map +0 -1
  695. package/dist/test/per-agent-delivery.test.d.ts +0 -2
  696. package/dist/test/per-agent-delivery.test.d.ts.map +0 -1
  697. package/dist/test/per-agent-delivery.test.js +0 -108
  698. package/dist/test/per-agent-delivery.test.js.map +0 -1
  699. package/dist/test/physical-window-restart.test.d.ts +0 -16
  700. package/dist/test/physical-window-restart.test.d.ts.map +0 -1
  701. package/dist/test/physical-window-restart.test.js +0 -487
  702. package/dist/test/physical-window-restart.test.js.map +0 -1
  703. package/dist/test/placed-reaction-baseline.test.d.ts +0 -2
  704. package/dist/test/placed-reaction-baseline.test.d.ts.map +0 -1
  705. package/dist/test/placed-reaction-baseline.test.js +0 -312
  706. package/dist/test/placed-reaction-baseline.test.js.map +0 -1
  707. package/dist/test/present-while-acting.test.d.ts +0 -19
  708. package/dist/test/present-while-acting.test.d.ts.map +0 -1
  709. package/dist/test/present-while-acting.test.js +0 -1649
  710. package/dist/test/present-while-acting.test.js.map +0 -1
  711. package/dist/test/prose-segments.test.d.ts +0 -2
  712. package/dist/test/prose-segments.test.d.ts.map +0 -1
  713. package/dist/test/prose-segments.test.js +0 -52
  714. package/dist/test/prose-segments.test.js.map +0 -1
  715. package/dist/test/prose-stream-router.test.d.ts +0 -2
  716. package/dist/test/prose-stream-router.test.d.ts.map +0 -1
  717. package/dist/test/prose-stream-router.test.js +0 -171
  718. package/dist/test/prose-stream-router.test.js.map +0 -1
  719. package/dist/test/provider-acceleration-cooldown.test.d.ts +0 -2
  720. package/dist/test/provider-acceleration-cooldown.test.d.ts.map +0 -1
  721. package/dist/test/provider-acceleration-cooldown.test.js +0 -435
  722. package/dist/test/provider-acceleration-cooldown.test.js.map +0 -1
  723. package/dist/test/provider-waits.test.d.ts +0 -2
  724. package/dist/test/provider-waits.test.d.ts.map +0 -1
  725. package/dist/test/provider-waits.test.js +0 -2259
  726. package/dist/test/provider-waits.test.js.map +0 -1
  727. package/dist/test/puppet-tool-call.test.d.ts +0 -2
  728. package/dist/test/puppet-tool-call.test.d.ts.map +0 -1
  729. package/dist/test/puppet-tool-call.test.js +0 -226
  730. package/dist/test/puppet-tool-call.test.js.map +0 -1
  731. package/dist/test/puppet-turn-reservation.test.d.ts +0 -2
  732. package/dist/test/puppet-turn-reservation.test.d.ts.map +0 -1
  733. package/dist/test/puppet-turn-reservation.test.js +0 -139
  734. package/dist/test/puppet-turn-reservation.test.js.map +0 -1
  735. package/dist/test/reaction-baseline-platform.test.d.ts +0 -2
  736. package/dist/test/reaction-baseline-platform.test.d.ts.map +0 -1
  737. package/dist/test/reaction-baseline-platform.test.js +0 -124
  738. package/dist/test/reaction-baseline-platform.test.js.map +0 -1
  739. package/dist/test/record-journal.test.d.ts +0 -6
  740. package/dist/test/record-journal.test.d.ts.map +0 -1
  741. package/dist/test/record-journal.test.js +0 -269
  742. package/dist/test/record-journal.test.js.map +0 -1
  743. package/dist/test/refusal-reaction-baseline.test.d.ts +0 -2
  744. package/dist/test/refusal-reaction-baseline.test.d.ts.map +0 -1
  745. package/dist/test/refusal-reaction-baseline.test.js +0 -81
  746. package/dist/test/refusal-reaction-baseline.test.js.map +0 -1
  747. package/dist/test/refusal-rewind.test.d.ts +0 -2
  748. package/dist/test/refusal-rewind.test.d.ts.map +0 -1
  749. package/dist/test/refusal-rewind.test.js +0 -117
  750. package/dist/test/refusal-rewind.test.js.map +0 -1
  751. package/dist/test/refusal-single-call.test.d.ts +0 -2
  752. package/dist/test/refusal-single-call.test.d.ts.map +0 -1
  753. package/dist/test/refusal-single-call.test.js +0 -177
  754. package/dist/test/refusal-single-call.test.js.map +0 -1
  755. package/dist/test/release-bump.test.d.ts +0 -2
  756. package/dist/test/release-bump.test.d.ts.map +0 -1
  757. package/dist/test/release-bump.test.js +0 -248
  758. package/dist/test/release-bump.test.js.map +0 -1
  759. package/dist/test/release-changelog.test.d.ts +0 -2
  760. package/dist/test/release-changelog.test.d.ts.map +0 -1
  761. package/dist/test/release-changelog.test.js +0 -159
  762. package/dist/test/release-changelog.test.js.map +0 -1
  763. package/dist/test/release-holds.test.d.ts +0 -2
  764. package/dist/test/release-holds.test.d.ts.map +0 -1
  765. package/dist/test/release-holds.test.js +0 -456
  766. package/dist/test/release-holds.test.js.map +0 -1
  767. package/dist/test/same-round-think-policy.test.d.ts +0 -2
  768. package/dist/test/same-round-think-policy.test.d.ts.map +0 -1
  769. package/dist/test/same-round-think-policy.test.js +0 -853
  770. package/dist/test/same-round-think-policy.test.js.map +0 -1
  771. package/dist/test/save-recent-image.test.d.ts +0 -26
  772. package/dist/test/save-recent-image.test.d.ts.map +0 -1
  773. package/dist/test/save-recent-image.test.js +0 -974
  774. package/dist/test/save-recent-image.test.js.map +0 -1
  775. package/dist/test/shutdown-errors.test.d.ts +0 -2
  776. package/dist/test/shutdown-errors.test.d.ts.map +0 -1
  777. package/dist/test/shutdown-errors.test.js +0 -662
  778. package/dist/test/shutdown-errors.test.js.map +0 -1
  779. package/dist/test/silent-heartbeat-rows.test.d.ts +0 -2
  780. package/dist/test/silent-heartbeat-rows.test.d.ts.map +0 -1
  781. package/dist/test/silent-heartbeat-rows.test.js +0 -451
  782. package/dist/test/silent-heartbeat-rows.test.js.map +0 -1
  783. package/dist/test/silent-heartbeat.test.d.ts +0 -2
  784. package/dist/test/silent-heartbeat.test.d.ts.map +0 -1
  785. package/dist/test/silent-heartbeat.test.js +0 -165
  786. package/dist/test/silent-heartbeat.test.js.map +0 -1
  787. package/dist/test/state-scaling-e2e.test.d.ts +0 -22
  788. package/dist/test/state-scaling-e2e.test.d.ts.map +0 -1
  789. package/dist/test/state-scaling-e2e.test.js +0 -206
  790. package/dist/test/state-scaling-e2e.test.js.map +0 -1
  791. package/dist/test/state-scaling.test.d.ts +0 -12
  792. package/dist/test/state-scaling.test.d.ts.map +0 -1
  793. package/dist/test/state-scaling.test.js +0 -251
  794. package/dist/test/state-scaling.test.js.map +0 -1
  795. package/dist/test/stop-with-host-operations.test.d.ts +0 -20
  796. package/dist/test/stop-with-host-operations.test.d.ts.map +0 -1
  797. package/dist/test/stop-with-host-operations.test.js +0 -1174
  798. package/dist/test/stop-with-host-operations.test.js.map +0 -1
  799. package/dist/test/stop-with-work-in-flight.test.d.ts +0 -23
  800. package/dist/test/stop-with-work-in-flight.test.d.ts.map +0 -1
  801. package/dist/test/stop-with-work-in-flight.test.js +0 -784
  802. package/dist/test/stop-with-work-in-flight.test.js.map +0 -1
  803. package/dist/test/surgery-marks-receipt.test.d.ts +0 -2
  804. package/dist/test/surgery-marks-receipt.test.d.ts.map +0 -1
  805. package/dist/test/surgery-marks-receipt.test.js +0 -540
  806. package/dist/test/surgery-marks-receipt.test.js.map +0 -1
  807. package/dist/test/thinking-token-stamp.test.d.ts +0 -12
  808. package/dist/test/thinking-token-stamp.test.d.ts.map +0 -1
  809. package/dist/test/thinking-token-stamp.test.js +0 -129
  810. package/dist/test/thinking-token-stamp.test.js.map +0 -1
  811. package/dist/test/timezone.test.d.ts +0 -2
  812. package/dist/test/timezone.test.d.ts.map +0 -1
  813. package/dist/test/timezone.test.js +0 -18
  814. package/dist/test/timezone.test.js.map +0 -1
  815. package/dist/test/tool-batch-hook.test.d.ts +0 -8
  816. package/dist/test/tool-batch-hook.test.d.ts.map +0 -1
  817. package/dist/test/tool-batch-hook.test.js +0 -275
  818. package/dist/test/tool-batch-hook.test.js.map +0 -1
  819. package/dist/test/tool-policy.test.d.ts +0 -2
  820. package/dist/test/tool-policy.test.d.ts.map +0 -1
  821. package/dist/test/tool-policy.test.js +0 -51
  822. package/dist/test/tool-policy.test.js.map +0 -1
  823. package/dist/test/tool-presentation.test.d.ts +0 -2
  824. package/dist/test/tool-presentation.test.d.ts.map +0 -1
  825. package/dist/test/tool-presentation.test.js +0 -268
  826. package/dist/test/tool-presentation.test.js.map +0 -1
  827. package/dist/test/tool-result-content.test.d.ts +0 -2
  828. package/dist/test/tool-result-content.test.d.ts.map +0 -1
  829. package/dist/test/tool-result-content.test.js +0 -37
  830. package/dist/test/tool-result-content.test.js.map +0 -1
  831. package/dist/test/tool-result-guard.test.d.ts +0 -2
  832. package/dist/test/tool-result-guard.test.d.ts.map +0 -1
  833. package/dist/test/tool-result-guard.test.js +0 -3729
  834. package/dist/test/tool-result-guard.test.js.map +0 -1
  835. package/dist/test/tool-result-history.test.d.ts +0 -7
  836. package/dist/test/tool-result-history.test.d.ts.map +0 -1
  837. package/dist/test/tool-result-history.test.js +0 -156
  838. package/dist/test/tool-result-history.test.js.map +0 -1
  839. package/dist/test/tool-result-spill.test.d.ts +0 -13
  840. package/dist/test/tool-result-spill.test.d.ts.map +0 -1
  841. package/dist/test/tool-result-spill.test.js +0 -586
  842. package/dist/test/tool-result-spill.test.js.map +0 -1
  843. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts +0 -2
  844. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts.map +0 -1
  845. package/dist/test/tool-wrapper-prose-guard-integration.test.js +0 -485
  846. package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +0 -1
  847. package/dist/test/tool-wrapper-prose-guard.test.d.ts +0 -2
  848. package/dist/test/tool-wrapper-prose-guard.test.d.ts.map +0 -1
  849. package/dist/test/tool-wrapper-prose-guard.test.js +0 -34
  850. package/dist/test/tool-wrapper-prose-guard.test.js.map +0 -1
  851. package/dist/test/tools-observe-parser-conformance.test.d.ts +0 -2
  852. package/dist/test/tools-observe-parser-conformance.test.d.ts.map +0 -1
  853. package/dist/test/tools-observe-parser-conformance.test.js +0 -129
  854. package/dist/test/tools-observe-parser-conformance.test.js.map +0 -1
  855. package/dist/test/trunk-channel-routing.test.d.ts +0 -19
  856. package/dist/test/trunk-channel-routing.test.d.ts.map +0 -1
  857. package/dist/test/trunk-channel-routing.test.js +0 -652
  858. package/dist/test/trunk-channel-routing.test.js.map +0 -1
  859. package/dist/test/tune-out-e2e.test.d.ts +0 -2
  860. package/dist/test/tune-out-e2e.test.d.ts.map +0 -1
  861. package/dist/test/tune-out-e2e.test.js +0 -142
  862. package/dist/test/tune-out-e2e.test.js.map +0 -1
  863. package/dist/test/tune-out-gate-composition.test.d.ts +0 -2
  864. package/dist/test/tune-out-gate-composition.test.d.ts.map +0 -1
  865. package/dist/test/tune-out-gate-composition.test.js +0 -221
  866. package/dist/test/tune-out-gate-composition.test.js.map +0 -1
  867. package/dist/test/tune-out-lifecycle.test.d.ts +0 -2
  868. package/dist/test/tune-out-lifecycle.test.d.ts.map +0 -1
  869. package/dist/test/tune-out-lifecycle.test.js +0 -121
  870. package/dist/test/tune-out-lifecycle.test.js.map +0 -1
  871. package/dist/test/turn-trigger-provenance.test.d.ts +0 -2
  872. package/dist/test/turn-trigger-provenance.test.d.ts.map +0 -1
  873. package/dist/test/turn-trigger-provenance.test.js +0 -137
  874. package/dist/test/turn-trigger-provenance.test.js.map +0 -1
  875. package/dist/test/unconvertible-tool-result.test.d.ts +0 -2
  876. package/dist/test/unconvertible-tool-result.test.d.ts.map +0 -1
  877. package/dist/test/unconvertible-tool-result.test.js +0 -358
  878. package/dist/test/unconvertible-tool-result.test.js.map +0 -1
  879. package/dist/test/unsent-compiles.test.d.ts +0 -2
  880. package/dist/test/unsent-compiles.test.d.ts.map +0 -1
  881. package/dist/test/unsent-compiles.test.js +0 -139
  882. package/dist/test/unsent-compiles.test.js.map +0 -1
  883. package/dist/test/usage-accounting.test.d.ts +0 -2
  884. package/dist/test/usage-accounting.test.d.ts.map +0 -1
  885. package/dist/test/usage-accounting.test.js +0 -13
  886. package/dist/test/usage-accounting.test.js.map +0 -1
  887. package/dist/test/usage-tracker.test.d.ts +0 -2
  888. package/dist/test/usage-tracker.test.d.ts.map +0 -1
  889. package/dist/test/usage-tracker.test.js +0 -129
  890. package/dist/test/usage-tracker.test.js.map +0 -1
  891. package/dist/test/utils-surface.test.d.ts +0 -2
  892. package/dist/test/utils-surface.test.d.ts.map +0 -1
  893. package/dist/test/utils-surface.test.js +0 -145
  894. package/dist/test/utils-surface.test.js.map +0 -1
  895. package/dist/test/workspace-branch-continuation.test.d.ts +0 -15
  896. package/dist/test/workspace-branch-continuation.test.d.ts.map +0 -1
  897. package/dist/test/workspace-branch-continuation.test.js +0 -183
  898. package/dist/test/workspace-branch-continuation.test.js.map +0 -1
  899. package/dist/test/workspace-materialize-freshness.test.d.ts +0 -16
  900. package/dist/test/workspace-materialize-freshness.test.d.ts.map +0 -1
  901. package/dist/test/workspace-materialize-freshness.test.js +0 -252
  902. package/dist/test/workspace-materialize-freshness.test.js.map +0 -1
  903. package/dist/test/workspace-path-containment.test.d.ts +0 -19
  904. package/dist/test/workspace-path-containment.test.d.ts.map +0 -1
  905. package/dist/test/workspace-path-containment.test.js +0 -91
  906. package/dist/test/workspace-path-containment.test.js.map +0 -1
  907. package/dist/test/workspace-read-file-from-disk.test.d.ts +0 -17
  908. package/dist/test/workspace-read-file-from-disk.test.d.ts.map +0 -1
  909. package/dist/test/workspace-read-file-from-disk.test.js +0 -174
  910. package/dist/test/workspace-read-file-from-disk.test.js.map +0 -1
  911. package/dist/test/workspace-read-image.test.d.ts +0 -2
  912. package/dist/test/workspace-read-image.test.d.ts.map +0 -1
  913. package/dist/test/workspace-read-image.test.js +0 -597
  914. package/dist/test/workspace-read-image.test.js.map +0 -1
  915. package/dist/test/workspace-read-text.test.d.ts +0 -2
  916. package/dist/test/workspace-read-text.test.d.ts.map +0 -1
  917. package/dist/test/workspace-read-text.test.js +0 -101
  918. package/dist/test/workspace-read-text.test.js.map +0 -1
  919. package/dist/test/workspace-restore-order.test.d.ts +0 -16
  920. package/dist/test/workspace-restore-order.test.d.ts.map +0 -1
  921. package/dist/test/workspace-restore-order.test.js +0 -140
  922. package/dist/test/workspace-restore-order.test.js.map +0 -1
  923. package/dist/test/workspace-three-way.test.d.ts +0 -12
  924. package/dist/test/workspace-three-way.test.d.ts.map +0 -1
  925. package/dist/test/workspace-three-way.test.js +0 -4094
  926. package/dist/test/workspace-three-way.test.js.map +0 -1
  927. package/dist/test/workspace-watcher.test.d.ts +0 -2
  928. package/dist/test/workspace-watcher.test.d.ts.map +0 -1
  929. package/dist/test/workspace-watcher.test.js +0 -281
  930. package/dist/test/workspace-watcher.test.js.map +0 -1
  931. package/dist/test/xml-round-persist.test.d.ts +0 -19
  932. package/dist/test/xml-round-persist.test.d.ts.map +0 -1
  933. package/dist/test/xml-round-persist.test.js +0 -235
  934. package/dist/test/xml-round-persist.test.js.map +0 -1
  935. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,1125 +0,0 @@
1
- /**
2
- * RFC-006 event coalescing (mcpl PR #5, revision 7) — wire-level tests over a
3
- * real loopback WebSocket MCPL server and a mock model provider.
4
- *
5
- * Vector numbers refer to RFC-006 §14.
6
- */
7
- import { test } from 'node:test';
8
- import assert from 'node:assert/strict';
9
- import { fork } from 'node:child_process';
10
- import { once } from 'node:events';
11
- import { mkdtempSync, rmSync } from 'node:fs';
12
- import { tmpdir } from 'node:os';
13
- import { join, dirname } from 'node:path';
14
- import { fileURLToPath } from 'node:url';
15
- import { createMockResponse } from './helpers/mock-membrane.js';
16
- import { fixture, eventually, crash, TS } from './helpers/coalescing-fixture.js';
17
- // ---------------------------------------------------------------------------
18
- // Plain coalescing, feature-set scope (vectors 1–6, 35)
19
- // ---------------------------------------------------------------------------
20
- test('host advertises eventCoalescing with the default retry window', async (t) => {
21
- const f = await fixture();
22
- t.after(f.close);
23
- assert.deepEqual(f.hostCaps[0].eventCoalescing, { pushEvents: true, channelsIncoming: true, deferred: true, channelScopedPush: true, retryWindowMs: 3_600_000 });
24
- });
25
- test('vectors 2, 6, 35: unread replacement keeps only the newest content; a retry returns the receipt', async (t) => {
26
- const f = await fixture();
27
- t.after(f.close);
28
- const first = await f.send('push/event', f.params('1', 'edit_original', { initial: true }));
29
- const second = await f.send('push/event', f.params('2', 'edit_middle'));
30
- const last = await f.send('push/event', f.params('3', 'edit_latest'));
31
- assert.equal(first.result.coalesce.outcome, 'first');
32
- assert.equal(second.result.coalesce.outcome, 'replaced');
33
- assert.deepEqual(last.result.coalesce, { outcome: 'replaced', priorEventId: '2' });
34
- // In context immediately (not staged), but only the newest occurrence.
35
- assert(f.context().includes('edit_latest'));
36
- assert(!f.context().includes('edit_original') && !f.context().includes('edit_middle'));
37
- await f.framework.runUntilIdle();
38
- assert.equal(f.membrane.calls.length, 1);
39
- assert(f.lastRequest().includes('edit_latest') && !f.lastRequest().includes('edit_original'));
40
- const retry = await f.send('push/event', f.params('3', 'edit_latest'));
41
- assert.deepEqual(retry.result, last.result);
42
- await f.framework.runUntilIdle();
43
- assert.equal(f.membrane.calls.length, 1, 'a retry has no second effect');
44
- });
45
- test('vector 3: a consumed occurrence is history; the next one appends', async (t) => {
46
- const f = await fixture();
47
- t.after(f.close);
48
- await f.send('push/event', f.params('1', 'battery_79', { initial: true }));
49
- await f.framework.runUntilIdle();
50
- const r = await f.send('push/event', f.params('2', 'battery_78'));
51
- assert.deepEqual(r.result.coalesce, { outcome: 'appended', priorEventId: '1' });
52
- await f.turn();
53
- const req = f.lastRequest();
54
- assert(req.includes('battery_79') && req.includes('battery_78'));
55
- assert(req.indexOf('battery_79') < req.indexOf('battery_78'));
56
- });
57
- test('vector 28: retraction of never-read content leaves no trace and no turn', async (t) => {
58
- const f = await fixture();
59
- t.after(f.close);
60
- await f.send('push/event', f.params('1', 'unread_original', { initial: true }));
61
- const r = await f.send('push/event', f.params('2', 'deletion_notice', { retract: true }));
62
- assert.equal(r.result.coalesce.outcome, 'retracted');
63
- await f.framework.runUntilIdle();
64
- assert.equal(f.membrane.calls.length, 0);
65
- assert(!f.context().includes('unread_original') && !f.context().includes('deletion_notice'));
66
- });
67
- test('vectors 29/34: read original → unread edit → delete keeps the original and appends only the notice', async (t) => {
68
- const f = await fixture();
69
- t.after(f.close);
70
- await f.send('push/event', f.params('1', 'read_original', { initial: true }));
71
- await f.framework.runUntilIdle();
72
- const edit = await f.send('push/event', f.params('2', 'unread_edit'));
73
- assert.equal(edit.result.coalesce.outcome, 'appended');
74
- const del = await f.send('push/event', f.params('3', 'deletion_notice', { retract: true }));
75
- assert.equal(del.result.coalesce.outcome, 'noted');
76
- await f.turn();
77
- const req = f.lastRequest();
78
- assert(req.includes('read_original') && req.includes('deletion_notice') && !req.includes('unread_edit'));
79
- // History persists past the empty slot: a new create under the same key, then delete → noted.
80
- await f.send('push/event', f.params('4', 'new_create'));
81
- const again = await f.send('push/event', f.params('5', 'second_notice', { retract: true }));
82
- assert.equal(again.result.coalesce.outcome, 'noted');
83
- });
84
- test('vector 30/33: empty retraction of consumed content is "consumed" and appends nothing', async (t) => {
85
- const f = await fixture();
86
- t.after(f.close);
87
- await f.register();
88
- await f.send('channels/incoming', { messages: [f.channel('c', 'presence_line', { initial: true }, 'chat', 'p1')] });
89
- await f.framework.runUntilIdle();
90
- const count = f.framework.getAgent('agent').getContextManager().getMessageCount();
91
- const r = await f.send('push/event', { featureSet: 'doc', eventId: 'gone', timestamp: TS, coalesce: { channelId: 'chat', key: 'message:p1', retract: true }, payload: { content: [] } });
92
- assert.equal(r.result.coalesce.outcome, 'consumed');
93
- assert.equal(f.framework.getAgent('agent').getContextManager().getMessageCount(), count);
94
- });
95
- test('vectors 31/32/34a: history is unknown without `initial`; `initial` makes "retracted" reachable after a restart', async (t) => {
96
- const f = await fixture();
97
- t.after(f.close);
98
- await f.framework.stop();
99
- await f.create();
100
- const plainCreate = await f.send('push/event', f.params('a', 'no_birth_marker', { key: 'k1' }));
101
- assert.equal(plainCreate.result.coalesce.outcome, 'appended');
102
- const plainDelete = await f.send('push/event', f.params('b', '[deleted]', { key: 'k1', retract: true }));
103
- assert.equal(plainDelete.result.coalesce.outcome, 'noted', 'unknown history yields the notice');
104
- const born = await f.send('push/event', f.params('c', 'born_here', { key: 'k2', initial: true }));
105
- assert.equal(born.result.coalesce.outcome, 'first');
106
- const gone = await f.send('push/event', f.params('d', '[deleted]', { key: 'k2', retract: true }));
107
- assert.equal(gone.result.coalesce.outcome, 'retracted');
108
- assert(!f.context().includes('born_here'));
109
- // vector 34b: initial cannot lower history.
110
- await f.send('push/event', f.params('e', 'seen', { key: 'k3', initial: true }));
111
- await f.framework.runUntilIdle();
112
- await f.send('push/event', f.params('f', 'again', { key: 'k3', initial: true }));
113
- const later = await f.send('push/event', f.params('g', 'notice', { key: 'k3', retract: true }));
114
- assert.equal(later.result.coalesce.outcome, 'noted');
115
- });
116
- test('vector 9: feature set and key isolate subjects', async (t) => {
117
- const f = await fixture();
118
- t.after(f.close);
119
- await f.send('push/event', f.params('1', 'doc_a', { key: 'A', initial: true }));
120
- await f.send('push/event', f.params('2', 'doc_b', { key: 'B', initial: true }));
121
- const r = await f.send('push/event', f.params('3', 'doc_a2', { key: 'A' }));
122
- assert.equal(r.result.coalesce.outcome, 'replaced');
123
- assert(f.context().includes('doc_b') && f.context().includes('doc_a2') && !f.context().includes('doc_a"'));
124
- });
125
- test('vector 38: malformed coalesce is a -32602 on push/event and per-message on channels/incoming', async (t) => {
126
- const f = await fixture();
127
- t.after(f.close);
128
- await f.register();
129
- const bad = await f.send('push/event', f.params('1', 'x', { key: 'k'.repeat(257) }));
130
- assert.equal(bad.error.code, -32602);
131
- const both = await f.send('push/event', f.params('2', 'x', { deferred: true, retract: true }));
132
- assert.equal(both.error.code, -32602);
133
- const missing = { ...f.channel('bad', 'bad'), eventId: undefined };
134
- const deferred = f.channel('bad2', 'bad2', { deferred: true });
135
- const r = await f.send('channels/incoming', { messages: [missing, f.channel('good', 'good', { initial: true }), deferred] });
136
- assert.deepEqual(r.result.results.map((x) => x.accepted), [false, true, false]);
137
- assert.equal(r.result.results[0].reason, 'coalesce_invalid');
138
- assert.equal(r.result.results[2].reason, 'coalesce_invalid');
139
- await f.framework.runUntilIdle();
140
- assert(f.lastRequest().includes('good'));
141
- });
142
- // ---------------------------------------------------------------------------
143
- // Channel scope, mixed lanes (vectors 8, 10, 13, 14, 15a, 43)
144
- // ---------------------------------------------------------------------------
145
- test('vector 10: channel create, push edit, push delete share one unread subject', async (t) => {
146
- const f = await fixture();
147
- t.after(f.close);
148
- await f.register();
149
- const first = await f.send('channels/incoming', { messages: [f.channel('c', 'chat_original', { initial: true })] });
150
- const edit = await f.send('push/event', f.params('e', 'chat_edit', { channelId: 'chat', key: 'message:m' }));
151
- const del = await f.send('push/event', f.params('d', 'chat_deleted', { channelId: 'chat', key: 'message:m', retract: true }));
152
- assert.equal(first.result.results[0].coalesce.outcome, 'first');
153
- assert.equal(edit.result.coalesce.outcome, 'replaced');
154
- assert.equal(del.result.coalesce.outcome, 'retracted');
155
- await f.framework.runUntilIdle();
156
- assert.equal(f.membrane.calls.length, 0);
157
- for (const text of ['chat_original', 'chat_edit', 'chat_deleted'])
158
- assert(!f.context().includes(text));
159
- });
160
- test('vector 8: repeated channel edits keep the platform message id and deduplicate a retried occurrence', async (t) => {
161
- const f = await fixture();
162
- t.after(f.close);
163
- await f.register();
164
- await f.send('channels/incoming', { messages: [f.channel('c', 'original', { initial: true })] });
165
- const edit = await f.send('channels/incoming', { messages: [f.channel('e', 'newest')] });
166
- const retry = await f.send('channels/incoming', { messages: [f.channel('e', 'newest')] });
167
- assert.deepEqual(retry.result, edit.result);
168
- assert.equal(edit.result.results[0].coalesce.outcome, 'replaced');
169
- await f.framework.runUntilIdle();
170
- assert.equal(f.membrane.calls.length, 1);
171
- assert(f.lastRequest().includes('newest') && !f.lastRequest().includes('original'));
172
- const stored = f.framework.getAgent('agent').getContextManager().getAllMessages().find((m) => m.metadata?.eventId === 'e');
173
- assert.equal(stored?.metadata?.messageId, 'm');
174
- });
175
- test('vector 43: a mixed-lane edit keeps the stable reply target', async (t) => {
176
- const f = await fixture();
177
- t.after(f.close);
178
- await f.register();
179
- await f.send('channels/incoming', { messages: [{ ...f.channel('c', 'first_version', { initial: true }), threadId: 'thread' }] });
180
- await f.framework.runUntilIdle();
181
- await f.send('push/event', { ...f.params('e', 'second_version', { channelId: 'chat', key: 'message:m' }), origin: { messageId: 'm', authorId: 'u', authorName: 'User', threadId: 'thread' } });
182
- await f.turn();
183
- assert.equal(f.published.at(-1)?.channelId, 'chat');
184
- });
185
- test('vectors 13/15: a channel-scoped push needs current channel authority and a declared channel', async (t) => {
186
- const f = await fixture();
187
- t.after(f.close);
188
- const unknown = await f.send('push/event', f.params('1', 'text', { channelId: 'chat' }));
189
- assert.equal(unknown.error.code, -32023);
190
- // A channel id that only ever appeared in a push's origin is not declared.
191
- await f.send('push/event', { featureSet: 'doc', eventId: 'dm', timestamp: TS, payload: { content: [{ type: 'text', text: 'dm' }] },
192
- tags: ['chat:dm'], origin: { source: 'discord', channelId: '42', guildId: null, authorId: 'u', authorName: 'User' } });
193
- await f.framework.runUntilIdle();
194
- const forged = await f.send('push/event', f.params('2', 'text', { channelId: 'discord:dm:42' }));
195
- assert.equal(forged.error.code, -32023);
196
- await f.register();
197
- const okNow = await f.send('push/event', f.params('3', 'text', { channelId: 'chat', initial: true }));
198
- assert.equal(okNow.result.coalesce.outcome, 'first');
199
- });
200
- test('vector 14: scope is never inferred from origin', async (t) => {
201
- const f = await fixture();
202
- t.after(f.close);
203
- await f.register('chat:one');
204
- await f.send('channels/incoming', { messages: [f.channel('a', 'channel_one', { initial: true }, 'chat:one')] });
205
- await f.send('push/event', { ...f.params('feature', 'feature_scope', { key: 'message:m' }), origin: { channelId: 'chat:one' } });
206
- await f.framework.runUntilIdle();
207
- assert(f.lastRequest().includes('channel_one') && f.lastRequest().includes('feature_scope'));
208
- });
209
- test('vector 33 (channel lane): an ordinary channels/incoming message without coalesce is unchanged', async (t) => {
210
- const f = await fixture();
211
- t.after(f.close);
212
- await f.register();
213
- const r = await f.send('channels/incoming', { messages: [{ channelId: 'chat', messageId: 'p', timestamp: TS, author: { id: 'u', name: 'User' }, content: [{ type: 'text', text: 'plain' }] }] });
214
- assert.deepEqual(r.result.results[0], { messageId: 'p', accepted: true });
215
- });
216
- // ---------------------------------------------------------------------------
217
- // Wake treatment (vectors 36, 43 rev-6 behavioural)
218
- // ---------------------------------------------------------------------------
219
- test('a retraction withdraws the unstarted wake its subject queued', async (t) => {
220
- const f = await fixture({ server: { shouldTriggerInference: () => true } });
221
- t.after(f.close);
222
- const internals = f.framework;
223
- await f.send('push/event', f.params('1', 'withdrawn', { initial: true }));
224
- assert.equal(internals.pendingRequests.length, 1);
225
- await f.send('push/event', f.params('2', 'gone', { retract: true }));
226
- assert.equal(internals.pendingRequests.length, 0);
227
- await f.framework.runUntilIdle();
228
- assert.equal(f.membrane.calls.length, 0);
229
- });
230
- test('a replacement is evaluated by the gate as a fresh occurrence; a qualifying replacement wakes once', async (t) => {
231
- const f = await fixture({ framework: { gate: { config: { policies: [{ name: 'attention', match: { tagsAny: ['doc:wake'] }, behavior: 'always' }], default: 'skip' } } } });
232
- t.after(f.close);
233
- const internals = f.framework;
234
- await f.send('push/event', { ...f.params('1', 'quiet', { initial: true }), tags: ['doc:quiet'] });
235
- assert.equal(internals.pendingRequests.length, 0);
236
- await f.send('push/event', { ...f.params('2', 'loud', {}), tags: ['doc:wake'] });
237
- assert.equal(internals.pendingRequests.length, 1);
238
- await f.send('push/event', { ...f.params('3', 'louder', {}), tags: ['doc:wake'] });
239
- assert.equal(internals.pendingRequests.length, 1, 'no second wake for the same subject');
240
- await f.send('push/event', { ...f.params('4', 'quiet_again', {}), tags: ['doc:quiet'] });
241
- assert.equal(internals.pendingRequests.length, 0, 'a non-qualifying replacement withdraws the wake');
242
- await f.framework.runUntilIdle();
243
- assert.equal(f.membrane.calls.length, 0);
244
- });
245
- // ---------------------------------------------------------------------------
246
- // Deferred rendering (vectors 16–27)
247
- // ---------------------------------------------------------------------------
248
- test('vectors 16/17/18: notices render once at assembly with their private data; never the fallback', async (t) => {
249
- const f = await fixture();
250
- t.after(f.close);
251
- await f.send('push/event', f.params('1', 'fallback_unavailable', { deferred: true, data: { private: 'hidden_notice' } }));
252
- const joined = await f.send('push/event', f.params('2', 'fallback_unavailable', { deferred: true, data: { private: 'hidden_notice_2' } }));
253
- assert.equal(joined.result.coalesce.outcome, 'replaced');
254
- assert.equal(f.renders.length, 0);
255
- assert(!f.context().includes('fallback_unavailable'), 'a batch is not model-visible');
256
- await f.framework.runUntilIdle();
257
- assert.equal(f.renders.length, 1);
258
- assert.equal(f.renders[0].notices ? f.renders[0].notices.length : 0, 2);
259
- assert.deepEqual(f.renders[0].notices.map((n) => n.data), [{ private: 'hidden_notice' }, { private: 'hidden_notice_2' }]);
260
- const req = f.lastRequest();
261
- assert(req.includes('document_diff') && !req.includes('fallback_unavailable') && !req.includes('hidden_notice'));
262
- // vector 18: a second inference issues no render; vector 19: a new notice does.
263
- await f.turn();
264
- assert.equal(f.renders.length, 1);
265
- const again = await f.send('push/event', f.params('3', 'fallback', { deferred: true }));
266
- assert.equal(again.result.coalesce.outcome, 'first');
267
- await f.turn();
268
- assert.equal(f.renders.length, 2);
269
- });
270
- test('vector 20: an empty render appends nothing', async (t) => {
271
- const f = await fixture();
272
- t.after(f.close);
273
- f.renderer(async () => ({ content: [] }));
274
- await f.send('push/event', f.params('1', 'fallback', { deferred: true }));
275
- await f.framework.runUntilIdle();
276
- assert.equal(f.renders.length, 1);
277
- assert(!f.context().includes('fallback'));
278
- // The batch was the turn's only cause and rendered nothing: no inference
279
- // (an uncaused wake would show the model only older context).
280
- assert.equal(f.membrane.calls.length, 0);
281
- });
282
- test('vectors 21/23: a render past the deadline materializes the fallback; the late result is discarded', async (t) => {
283
- const f = await fixture();
284
- t.after(f.close);
285
- f.renderer(async () => { await new Promise((r) => setTimeout(r, 5300)); return { content: [{ type: 'text', text: 'wire_late_payload' }] }; });
286
- await f.send('push/event', f.params('1', 'bounded_fallback', { deferred: true }));
287
- await f.framework.runUntilIdle();
288
- assert(f.lastRequest().includes('bounded_fallback') && !f.lastRequest().includes('wire_late_payload'));
289
- await new Promise((r) => setTimeout(r, 400));
290
- assert(!f.context().includes('wire_late_payload'));
291
- });
292
- test('vector 21: a render error uses the fallback', async (t) => {
293
- const f = await fixture();
294
- t.after(f.close);
295
- f.renderer(async () => ({ content: [null] }));
296
- await f.send('push/event', f.params('1', 'fallback_text', { deferred: true }));
297
- await f.framework.runUntilIdle();
298
- assert(f.lastRequest().includes('fallback_text'));
299
- });
300
- test('vector 26: inference/request during push/render is refused', async (t) => {
301
- const f = await fixture();
302
- t.after(f.close);
303
- let refused;
304
- f.renderer(async () => { refused = (await f.send('inference/request', { featureSet: 'doc', messages: [] })).error?.code; return { content: [{ type: 'text', text: 'rendered' }] }; });
305
- await f.send('push/event', f.params('1', 'fallback', { deferred: true }));
306
- await f.framework.runUntilIdle();
307
- assert.equal(refused, -32600);
308
- });
309
- test('vectors 27a/27c: retract or plain replacement during a render cancels the frozen batch', async (t) => {
310
- for (const operation of ['retract', 'plain']) {
311
- const f = await fixture();
312
- try {
313
- let release;
314
- let began;
315
- const started = new Promise((resolve) => { began = resolve; });
316
- f.renderer(() => { began(); return new Promise((resolve) => { release = resolve; }); });
317
- await f.send('push/event', f.params('1', 'fallback_one', { deferred: true, initial: true }));
318
- const run = f.framework.runUntilIdle();
319
- await started;
320
- const r = await f.send('push/event', operation === 'retract'
321
- ? f.params('2', 'deletion_notice', { retract: true })
322
- : f.params('2', 'complete_snapshot'));
323
- assert.equal(r.result.coalesce.outcome, operation === 'retract' ? 'retracted' : 'replaced');
324
- // The plain replacement arrived while the turn was alive: it lands at
325
- // the turn boundary and wakes the agent again, so a second turn follows.
326
- if (operation === 'plain')
327
- f.alwaysRespond();
328
- release({ content: [{ type: 'text', text: 'late_old_render' }] });
329
- await run;
330
- if (operation === 'retract') {
331
- // The cancelled batch was the turn's only cause and never-read
332
- // content was withdrawn (vector 28): no inference at all.
333
- assert.equal(f.membrane.calls.length, 0);
334
- assert(!f.context().includes('late_old_render') && !f.context().includes('fallback_one') && !f.context().includes('deletion_notice'));
335
- }
336
- else {
337
- const req = f.lastRequest();
338
- assert(!req.includes('late_old_render') && !req.includes('fallback_one') && !req.includes('deletion_notice'));
339
- assert(req.includes('complete_snapshot'));
340
- }
341
- }
342
- finally {
343
- await f.close();
344
- }
345
- }
346
- });
347
- test('vector 24: revoked authority before assembly drops the batch without a render', async (t) => {
348
- const f = await fixture();
349
- t.after(f.close);
350
- await f.send('push/event', f.params('1', 'previously_permitted', { deferred: true }));
351
- await f.framework.stop();
352
- await f.create(false);
353
- await f.framework.runUntilIdle();
354
- assert.equal(f.renders.length, 0);
355
- assert(!f.context().includes('previously_permitted'));
356
- });
357
- test('vector 27i: a deferred notice displaces an unread plain occurrence', async (t) => {
358
- const f = await fixture();
359
- t.after(f.close);
360
- await f.send('push/event', f.params('1', 'plain_unread', { initial: true }));
361
- const r = await f.send('push/event', f.params('2', 'fallback', { deferred: true }));
362
- assert.equal(r.result.coalesce.outcome, 'replaced');
363
- assert(!f.context().includes('plain_unread'));
364
- await f.framework.runUntilIdle();
365
- assert(f.lastRequest().includes('document_diff') && !f.lastRequest().includes('plain_unread'));
366
- });
367
- // ---------------------------------------------------------------------------
368
- // Recovery (vectors 7, 39, 40, 41, 47) and the findings from PR #196
369
- // ---------------------------------------------------------------------------
370
- test('vector 41 (#196 finding 1): a transient reconnect does not reject ordinary channel traffic', async (t) => {
371
- const f = await fixture({ server: { reconnect: true, reconnectIntervalMs: 20 } });
372
- t.after(f.close);
373
- await f.register();
374
- const plain = (id) => ({ channelId: 'chat', messageId: id, timestamp: TS, author: { id: 'u', name: 'User' }, content: [{ type: 'text', text: 'plain_' + id }] });
375
- const before = await f.send('channels/incoming', { messages: [plain('1')] });
376
- f.disconnect();
377
- await eventually(() => f.hostCaps.length === 2, 'reconnect');
378
- await new Promise((r) => setTimeout(r, 100));
379
- const after = await f.send('channels/incoming', { messages: [plain('2')] });
380
- assert.equal(before.result.results[0].accepted, true);
381
- assert.equal(after.result.results[0].accepted, true);
382
- });
383
- test('vector 39/40: a reconnect keeps accepted content and answers a retry with its receipt', async (t) => {
384
- const f = await fixture({ server: { reconnect: true, reconnectIntervalMs: 20 } });
385
- t.after(f.close);
386
- await f.register();
387
- const params = { messages: [f.channel('c', 'survives_disconnect', { initial: true })] };
388
- const accepted = await f.send('channels/incoming', params);
389
- f.disconnect();
390
- await eventually(() => f.hostCaps.length === 2, 'reconnect');
391
- await f.register();
392
- const retry = await f.send('channels/incoming', params);
393
- assert.deepEqual(retry.result, accepted.result);
394
- await f.framework.runUntilIdle();
395
- assert.equal(f.membrane.calls.length, 1);
396
- const req = f.lastRequest();
397
- assert.equal(req.split('survives_disconnect').length - 1, 1, 'exactly once');
398
- });
399
- test('vectors 7/47: restart keeps accepted content, receipts, and appends after it', async (t) => {
400
- const f = await fixture();
401
- t.after(f.close);
402
- const params = f.params('once', 'exactly_once', { initial: true });
403
- const original = await f.send('push/event', params);
404
- await f.framework.stop();
405
- await f.create();
406
- const retry = await f.send('push/event', params);
407
- assert.deepEqual(retry.result, original.result);
408
- const next = await f.send('push/event', f.params('two', 'after_restart'));
409
- assert.equal(next.result.coalesce.outcome, 'appended');
410
- await f.framework.runUntilIdle();
411
- const req = f.lastRequest();
412
- assert(req.includes('exactly_once') && req.includes('after_restart'));
413
- assert.equal(req.split('exactly_once').length - 1, 1);
414
- });
415
- test('#196 finding 2 / vector 34c: a busy channel never hits a subject cap', async (t) => {
416
- const f = await fixture({ framework: { gate: { config: { policies: [{ name: 'attention', match: { tagsAny: ['chat:mention'] }, behavior: 'always' }], default: 'skip' } } } });
417
- t.after(f.close);
418
- await f.register();
419
- const msg = (i, tags) => ({ channelId: 'chat', messageId: 'm' + i, eventId: 'e' + i, timestamp: TS, author: { id: 'u', name: 'User' }, tags,
420
- content: [{ type: 'text', text: 'text_' + i }], coalesce: { key: 'message:m' + i, initial: true } });
421
- const t0 = Date.now();
422
- for (let i = 0; i < 300; i++) {
423
- const r = await f.send('channels/incoming', { messages: [msg(i, [])] });
424
- assert.equal(r.result.results[0].accepted, true);
425
- }
426
- const elapsed = Date.now() - t0;
427
- await f.framework.runUntilIdle();
428
- assert.equal(f.membrane.calls.length, 0);
429
- const mention = await f.send('channels/incoming', { messages: [msg('mention', ['chat:mention'])] });
430
- assert.equal(mention.result.results[0].accepted, true);
431
- await f.framework.runUntilIdle();
432
- assert.equal(f.membrane.calls.length, 1);
433
- assert(f.lastRequest().includes('text_mention') && f.lastRequest().includes('text_0'));
434
- // Each acceptance awaits a group-committed fsync (RFC-006 durability); serial
435
- // sends cannot batch, so this is ~300 fsyncs. Bounded, and reported.
436
- console.log(`# 300 serial coalesced channel messages: ${elapsed}ms (${(elapsed / 300).toFixed(1)} ms/msg incl. fsync)`);
437
- assert(elapsed < 30_000, `300 coalesced messages took ${elapsed}ms`);
438
- });
439
- test('#196 finding 4: a context-budget restart does not assemble a pending batch', async (t) => {
440
- const f = await fixture();
441
- t.after(f.close);
442
- await f.send('push/event', f.params('1', 'pending_batch', { deferred: true }));
443
- const internals = f.framework;
444
- internals.pendingRequests.length = 0;
445
- await internals.startAgentStream(f.framework.getAgent('agent'), { agentName: 'agent', reason: 'context_budget_restart', source: 'probe', timestamp: Date.now() });
446
- await f.framework.runUntilIdle();
447
- assert.equal(f.renders.length, 0, 'not rendered mid-logical-turn');
448
- assert(!f.lastRequest().includes('document_diff'));
449
- const joined = await f.send('push/event', f.params('2', 'pending_batch', { deferred: true }));
450
- assert.equal(joined.result.coalesce.outcome, 'replaced', 'the batch survived the restart turn');
451
- await f.turn();
452
- assert.equal(f.renders.length, 1, 'rendered at the next fresh turn');
453
- assert.equal(f.renders[0].notices.length, 2);
454
- });
455
- test('#196 finding 5: undo of a turn keeps the coalesced input that preceded it', async (t) => {
456
- const f = await fixture({ server: { shouldTriggerInference: () => true } });
457
- t.after(f.close);
458
- await f.send('push/event', f.params('a', 'turn_one', { key: 'a', initial: true }));
459
- await f.framework.runUntilIdle();
460
- await f.send('push/event', f.params('b', 'unread_before_undo', { key: 'b', initial: true }));
461
- await f.turn();
462
- assert(f.lastRequest().includes('unread_before_undo'));
463
- f.framework.undoLastTurn('agent');
464
- assert(f.context().includes('unread_before_undo'), 'the input survives the undo of the turn that read it');
465
- const r = await f.send('push/event', f.params('c', 'after_undo', { key: 'b' }));
466
- assert.equal(r.result.coalesce.outcome, 'appended', 'after a branch switch nothing is replaceable');
467
- });
468
- test('a replacement while a turn is alive edits the deferred queue, not the live turn', async (t) => {
469
- const f = await fixture();
470
- t.after(f.close);
471
- const internals = f.framework;
472
- internals.activeTurnTokens.set('agent', 100);
473
- await f.send('push/event', f.params('1', 'queued_original', { initial: true }));
474
- const r = await f.send('push/event', f.params('2', 'queued_replacement'));
475
- assert.equal(r.result.coalesce.outcome, 'replaced');
476
- assert.equal(internals.deferredMessages.length, 1);
477
- assert(!f.context().includes('queued_original') && !f.context().includes('queued_replacement'));
478
- internals.activeTurnTokens.delete('agent');
479
- await f.turn();
480
- assert(f.lastRequest().includes('queued_replacement') && !f.lastRequest().includes('queued_original'));
481
- });
482
- test('compression consumes: a message folded into a summary is no longer replaceable', async (t) => {
483
- const { AutobiographicalStrategy } = await import('@animalabs/context-manager');
484
- const strategy = new AutobiographicalStrategy({
485
- adaptiveResolution: true, foldingStrategy: 'kv-stable', recentWindowTokens: 1_000, targetChunkTokens: 300,
486
- kvStableReachTokens: 300, autoTickOnNewMessage: false, compressionModel: 'summarizer',
487
- });
488
- const f = await fixture({ agents: [{ name: 'agent', model: 'test', systemPrompt: 'test', strategy }] });
489
- t.after(f.close);
490
- f.membrane.complete = async (req) => { f.membrane.calls.push(req); return createMockResponse([{ type: 'text', text: 'SUMMARY' }]); };
491
- await f.send('push/event', f.params('1', 'folded_original', { initial: true }));
492
- const cm = f.framework.getAgent('agent').getContextManager();
493
- for (let i = 0; i < 30; i++)
494
- cm.addMessage('user', [{ type: 'text', text: `filler_${i} ` + 'lorem ipsum '.repeat(40) }]);
495
- for (let i = 0; i < 12; i++)
496
- await cm.tick();
497
- assert(cm.getMaxSummaryLevel() > 0, 'folding happened');
498
- const r = await f.send('push/event', f.params('2', 'after_fold'));
499
- assert.equal(r.result.coalesce.outcome, 'appended');
500
- assert(f.context().includes('folded_original') && f.context().includes('after_fold'));
501
- });
502
- test('conversation forks: a channel-scoped edit follows the routed fork and pins its reply channel', async (t) => {
503
- const f = await fixture({ framework: { conversations: { templateAgent: 'agent', bind: { channel: 'always' }, trigger: { channel: 'always' } } } });
504
- t.after(f.close);
505
- await f.register();
506
- await f.send('channels/incoming', { messages: [f.channel('c', 'fork_original', { initial: true })] });
507
- const edit = await f.send('push/event', f.params('e', 'fork_latest', { channelId: 'chat', key: 'message:m' }));
508
- assert.equal(edit.result.coalesce.outcome, 'replaced');
509
- f.alwaysRespond();
510
- await f.framework.runUntilIdle();
511
- assert.equal(f.membrane.calls.length, 1);
512
- assert(f.lastRequest().includes('fork_latest') && !f.lastRequest().includes('fork_original'));
513
- assert(!f.context().includes('fork_latest'), 'the trunk never sees channel traffic');
514
- assert.equal(f.published.at(-1)?.channelId, 'chat');
515
- });
516
- // ---------------------------------------------------------------------------
517
- // Review round 1 (#197): Greptile G1–G15, Codex C1–C3
518
- // ---------------------------------------------------------------------------
519
- test('C1: a channel-scoped render result is dropped when channels.incoming is revoked mid-render', async (t) => {
520
- const f = await fixture();
521
- t.after(f.close);
522
- await f.register();
523
- const { CapabilityGrant } = await import('../src/mcpl/capability-grant.js');
524
- let release;
525
- const started = new Promise((resolve) => { f.renderer(() => { resolve(); return new Promise((r) => { release = r; }); }); });
526
- await f.send('push/event', f.params('1', 'fallback', { deferred: true, channelId: 'chat', key: 'activity' }));
527
- const run = f.framework.runUntilIdle();
528
- await started;
529
- const conn = f.framework.mcplServerRegistry.getServer('editor');
530
- conn.establishGrant(new CapabilityGrant(new Set(['pushEvents']), []));
531
- release({ content: [{ type: 'text', text: 'revoked_render' }] });
532
- await run;
533
- assert(!f.context().includes('revoked_render') && !f.context().includes('fallback'));
534
- });
535
- test('C2 / vector 42: reassigning the server id to another endpoint starts a new binding', async (t) => {
536
- const f = await fixture();
537
- t.after(f.close);
538
- const a = await f.send('push/event', f.params('E', 'from_endpoint_a', { key: 'K', initial: true }));
539
- assert.equal(a.result.coalesce.outcome, 'first');
540
- const config = f.framework.mcplServerConfigs.get('editor');
541
- await f.framework.restartMcplServer('editor', { ...config, url: `${config.url}/reassigned` });
542
- await eventually(() => f.hostCaps.length === 2, 'reconnect to the new endpoint');
543
- const b = await f.send('push/event', f.params('E', 'from_endpoint_b', { key: 'K', initial: true }));
544
- assert.equal(b.result.coalesce.outcome, 'first', "B's reuse of A's event id is a new occurrence");
545
- const b2 = await f.send('push/event', f.params('E2', 'from_endpoint_b_2', { key: 'K' }));
546
- assert.equal(b2.result.coalesce.outcome, 'replaced');
547
- assert.equal(b2.result.coalesce.priorEventId, 'E');
548
- assert(f.context().includes('from_endpoint_a'), "A's unread content is not touched by B");
549
- assert(f.context().includes('from_endpoint_b_2') && !f.context().includes('from_endpoint_b"'));
550
- });
551
- test('C3: a replacement follows the prior delivery into its fork, never into a fresh one', async (t) => {
552
- const f = await fixture({ framework: { conversations: { templateAgent: 'agent', bind: { channel: 'always' }, trigger: { channel: 'always' } } } });
553
- t.after(f.close);
554
- await f.register();
555
- await f.send('channels/incoming', { messages: [f.channel('c', 'fork_original', { initial: true })] });
556
- const router = f.framework.conversationRouter;
557
- const g1 = router.getBinding('chat').agentName;
558
- router.unbind('chat');
559
- const edit = await f.send('push/event', f.params('e', 'fork_edit', { channelId: 'chat', key: 'message:m' }));
560
- assert.equal(edit.result.coalesce.outcome, 'replaced');
561
- assert.equal(router.getBinding('chat'), undefined, 'no fresh fork was spawned for the edit');
562
- const g1ctx = JSON.stringify(f.framework.getAgent(g1).getContextManager().getAllMessages());
563
- assert(g1ctx.includes('fork_edit') && !g1ctx.includes('fork_original'));
564
- });
565
- test('G1: malformed content on a coalesced channel item fails that item only', async (t) => {
566
- const f = await fixture();
567
- t.after(f.close);
568
- await f.register();
569
- const bad = { ...f.channel('bad', 'x', { initial: true }, 'chat', 'b'), content: 'not-an-array' };
570
- const r = await f.send('channels/incoming', { messages: [bad, f.channel('good', 'good_sibling', { initial: true }, 'chat', 'g')] });
571
- assert.deepEqual(r.result.results.map((x) => x.accepted), [false, true]);
572
- assert.equal(r.result.results[0].reason, 'coalesce_invalid');
573
- const badPush = await f.send('push/event', { ...f.params('p', 'x'), payload: { content: [{ type: 'text' }] } });
574
- assert.equal(badPush.error.code, -32602);
575
- });
576
- test('G3/G5: a deferred channel-scoped push renders with its feature set and wakes with its channel', async (t) => {
577
- const f = await fixture({ server: { shouldTriggerInference: () => true } });
578
- t.after(f.close);
579
- await f.register();
580
- await f.send('push/event', { ...f.params('1', 'fallback', { deferred: true, channelId: 'chat', key: 'activity' }), origin: { authorId: 'u', authorName: 'User' } });
581
- const internals = f.framework;
582
- assert.equal(internals.pendingRequests[0]?.channelId, 'chat');
583
- assert.equal(internals.pendingRequests[0]?.counterparty, 'editor:user:u');
584
- await f.framework.runUntilIdle();
585
- assert.equal(f.renders[0]?.featureSet, 'doc');
586
- assert.equal(f.renders[0]?.channelId, 'chat');
587
- assert.equal(f.published.at(-1)?.channelId, 'chat', 'the reply goes to the batch\'s channel');
588
- });
589
- test('G4: a deferred channel-scoped push spawns the conversation fork it needs', async (t) => {
590
- const f = await fixture({ framework: { conversations: { templateAgent: 'agent', bind: { channel: 'always' }, trigger: { channel: 'always' } } }, server: { shouldTriggerInference: () => true } });
591
- t.after(f.close);
592
- await f.register();
593
- await f.send('push/event', f.params('1', 'fallback', { deferred: true, channelId: 'chat', key: 'activity' }));
594
- f.alwaysRespond();
595
- await f.framework.runUntilIdle();
596
- assert.equal(f.renders.length, 1);
597
- const router = f.framework.conversationRouter;
598
- const fork = router.getBinding('chat')?.agentName;
599
- assert(fork, 'a fork was bound');
600
- assert(JSON.stringify(f.framework.getAgent(fork).getContextManager().getAllMessages()).includes('document_diff'));
601
- assert(!f.context().includes('document_diff'));
602
- });
603
- test('G6: a replacement finds its prior after the deferred flush stored it', async (t) => {
604
- const f = await fixture();
605
- t.after(f.close);
606
- const internals = f.framework;
607
- internals.activeTurnTokens.set('agent', 100);
608
- await f.send('push/event', f.params('1', 'queued_then_stored', { initial: true }));
609
- internals.activeTurnTokens.delete('agent');
610
- await internals.flushDeferredWrites('test');
611
- assert(f.context().includes('queued_then_stored'), 'flushed into context, unread');
612
- const r = await f.send('push/event', f.params('2', 'replacement_after_flush'));
613
- assert.equal(r.result.coalesce.outcome, 'replaced');
614
- assert(!f.context().includes('queued_then_stored') && f.context().includes('replacement_after_flush'));
615
- const del = await f.send('push/event', f.params('3', 'notice', { retract: true }));
616
- assert.equal(del.result.coalesce.outcome, 'retracted');
617
- assert(!f.context().includes('replacement_after_flush'));
618
- });
619
- test('G9: a receipt is durable before the acceptance is acknowledged', async (t) => {
620
- const f = await fixture();
621
- t.after(f.close);
622
- await f.send('push/event', f.params('durable', 'x', { initial: true }));
623
- const store = f.framework.store;
624
- const recent = store.getStateJson('mcpl/coalescing-recent');
625
- assert.equal(recent.length, 1);
626
- assert(recent[0].key.includes('"durable"'));
627
- assert.equal(recent[0].born, 'none');
628
- });
629
- test('G10: a batch that never began rendering renders after a clean restart', async (t) => {
630
- const f = await fixture();
631
- t.after(f.close);
632
- await f.send('push/event', f.params('1', 'fallback_text', { deferred: true }));
633
- await f.framework.stop();
634
- await f.create();
635
- await f.framework.runUntilIdle();
636
- assert.equal(f.renders.length, 1, 'push/render issued after restart');
637
- assert(f.lastRequest().includes('document_diff') && !f.lastRequest().includes('fallback_text'));
638
- });
639
- test('G11: a push edit without origin identity keeps the channel message identity', async (t) => {
640
- const f = await fixture();
641
- t.after(f.close);
642
- await f.register();
643
- await f.send('channels/incoming', { messages: [{ ...f.channel('c', 'first_version', { initial: true }), threadId: 'thread' }] });
644
- await f.send('push/event', f.params('e', 'second_version', { channelId: 'chat', key: 'message:m' }));
645
- const stored = f.framework.getAgent('agent').getContextManager().getAllMessages().find((m) => m.metadata?.eventId === 'e');
646
- assert.equal(stored?.metadata?.messageId, 'm');
647
- assert.equal(stored?.metadata?.threadId, 'thread');
648
- assert.deepEqual(stored?.metadata?.author, { id: 'u', name: 'User' });
649
- });
650
- test('G13: modules see coalesced deliveries', async (t) => {
651
- const seen = [];
652
- const spy = { name: 'spy', async start() { }, async stop() { }, async onProcess(e) { seen.push(e.type); return {}; } };
653
- const f = await fixture({ framework: { modules: [spy] } });
654
- t.after(f.close);
655
- await f.register();
656
- await f.send('push/event', f.params('1', 'x', { initial: true }));
657
- await f.send('channels/incoming', { messages: [f.channel('c', 'y', { initial: true })] });
658
- assert.deepEqual(seen.filter((x) => x.startsWith('mcpl:')), ['mcpl:push-event', 'mcpl:channel-incoming']);
659
- });
660
- test('G15: inference/request is refused while a cancelled render is still outstanding', async (t) => {
661
- const f = await fixture();
662
- t.after(f.close);
663
- let refused;
664
- f.renderer(async () => {
665
- await f.send('push/event', f.params('2', 'notice', { retract: true }));
666
- refused = (await f.send('inference/request', { featureSet: 'doc', messages: [] })).error?.code;
667
- return { content: [{ type: 'text', text: 'late' }] };
668
- });
669
- await f.send('push/event', f.params('1', 'fallback', { deferred: true, initial: true }));
670
- await f.framework.runUntilIdle();
671
- assert.equal(refused, -32600);
672
- // The withdrawn batch was the turn's only cause: no inference at all.
673
- assert.equal(f.membrane.calls.length, 0);
674
- assert(!f.context().includes('late') && !f.context().includes('fallback'));
675
- });
676
- // ---------------------------------------------------------------------------
677
- // Review round 2 (#197): Codex R1–R4 on d86afc1
678
- // ---------------------------------------------------------------------------
679
- test('R1: a deferred acceptance survives a crash before the snapshot flush, with its receipt', async (t) => {
680
- const f = await fixture();
681
- t.after(f.close);
682
- const params = f.params('1', 'crash_fallback', { deferred: true, data: { k: 'v' } });
683
- const original = await f.send('push/event', params);
684
- await crash(f);
685
- await f.create();
686
- const retry = await f.send('push/event', params);
687
- assert.deepEqual(retry.result, original.result, 'the receipt survived');
688
- await f.framework.runUntilIdle();
689
- assert.equal(f.membrane.calls.length, 1, 'the accepted work was delivered');
690
- assert(f.lastRequest().includes('crash_fallback'), 'as its fallback: the host cannot prove no render started');
691
- assert.equal(f.renders.length, 0);
692
- });
693
- test('R2: work admitted under a former binding never reaches the replacement endpoint', async (t) => {
694
- const f = await fixture();
695
- t.after(f.close);
696
- await f.send('push/event', f.params('1', 'old_binding_fallback', { deferred: true, data: { secret: 'private_notice_data' } }));
697
- const config = f.framework.mcplServerConfigs.get('editor');
698
- await f.framework.restartMcplServer('editor', { ...config, url: `${config.url}/reassigned` });
699
- await eventually(() => f.hostCaps.length === 2, 'reconnect to the new endpoint');
700
- await f.framework.runUntilIdle();
701
- assert.equal(f.renders.length, 0, 'no push/render to the new peer');
702
- assert(!JSON.stringify(f.renders).includes('private_notice_data'));
703
- assert(!f.context().includes('old_binding_fallback'));
704
- });
705
- test('R3: a deferred notice displacing an unread plain occurrence stays in its fork', async (t) => {
706
- const f = await fixture({ framework: { conversations: { templateAgent: 'agent', bind: { channel: 'always' }, trigger: { channel: 'always' } } }, server: { shouldTriggerInference: () => true } });
707
- t.after(f.close);
708
- await f.register();
709
- await f.send('channels/incoming', { messages: [f.channel('c', 'fork_plain', { initial: true })] });
710
- const router = f.framework.conversationRouter;
711
- const g1 = router.getBinding('chat').agentName;
712
- router.unbind('chat');
713
- const r = await f.send('push/event', f.params('n', 'fallback', { deferred: true, channelId: 'chat', key: 'message:m' }));
714
- assert.equal(r.result.coalesce.outcome, 'replaced');
715
- f.alwaysRespond();
716
- await f.framework.runUntilIdle();
717
- assert.equal(router.getBinding('chat'), undefined, 'no fresh fork');
718
- const g1ctx = JSON.stringify(f.framework.getAgent(g1).getContextManager().getAllMessages());
719
- assert(g1ctx.includes('document_diff') && !g1ctx.includes('fork_plain'));
720
- });
721
- test('R4: the render-start boundary is persisted before the RPC is issued', async (t) => {
722
- const f = await fixture();
723
- t.after(f.close);
724
- let release;
725
- const started = new Promise((resolve) => { f.renderer(() => { resolve(); return new Promise((r) => { release = r; }); }); });
726
- await f.send('push/event', f.params('1', 'fallback', { deferred: true }));
727
- const run = f.framework.runUntilIdle();
728
- await started;
729
- const store = f.framework.store;
730
- assert.equal(store.getStateJson('mcpl/coalescing').subjects[0]?.batch?.rendering, true, 'persisted as RENDERING while the RPC is outstanding');
731
- release({ content: [{ type: 'text', text: 'rendered' }] });
732
- await run;
733
- assert(f.lastRequest().includes('rendered'));
734
- });
735
- // ---------------------------------------------------------------------------
736
- // Review round 3 (#197): Codex on c03b23b — recovery from a mid-window image
737
- // ---------------------------------------------------------------------------
738
- test('R5: an acknowledged retraction is recoverable; a retry cannot resurrect the batch', async (t) => {
739
- const f = await fixture();
740
- t.after(f.close);
741
- await f.send('push/event', f.params('1', 'withdrawn_fallback', { deferred: true, initial: true }));
742
- const retract = f.params('2', 'notice', { retract: true });
743
- const first = await f.send('push/event', retract);
744
- assert.equal(first.result.coalesce.outcome, 'retracted');
745
- await crash(f);
746
- await f.create();
747
- const retry = await f.send('push/event', retract);
748
- assert.deepEqual(retry.result, first.result);
749
- await f.framework.runUntilIdle();
750
- assert.equal(f.membrane.calls.length, 0);
751
- assert(!f.context().includes('withdrawn_fallback') && !f.context().includes('notice'));
752
- });
753
- test('R6: a completed render is not delivered again as fallback after recovery', async (t) => {
754
- const f = await fixture();
755
- t.after(f.close);
756
- await f.send('push/event', f.params('1', 'stale_fallback', { deferred: true }));
757
- await f.framework.runUntilIdle();
758
- assert(f.lastRequest().includes('document_diff'));
759
- await crash(f);
760
- await f.create();
761
- await f.framework.runUntilIdle();
762
- const ctx = f.context();
763
- assert.equal(ctx.split('document_diff').length - 1, 1, 'rendered content exactly once');
764
- assert(!ctx.includes('stale_fallback'), 'no second delivery as fallback');
765
- assert.equal(f.renders.length, 1);
766
- // History is known: a retraction now appends the notice rather than withdrawing.
767
- const r = await f.send('push/event', f.params('2', 'deleted_notice', { retract: true }));
768
- assert.equal(r.result.coalesce.outcome, 'noted');
769
- });
770
- test('R7: a read plain occurrence recovered from a mid-window image still gets its deletion notice', async (t) => {
771
- const f = await fixture();
772
- t.after(f.close);
773
- await f.send('push/event', f.params('1', 'read_before_crash', { initial: true }));
774
- await f.framework.runUntilIdle();
775
- assert(f.lastRequest().includes('read_before_crash'));
776
- await crash(f);
777
- await f.create();
778
- const r = await f.send('push/event', f.params('2', 'deletion_notice_after_crash', { retract: true }));
779
- assert.equal(r.result.coalesce.outcome, 'noted');
780
- assert(f.context().includes('read_before_crash') && f.context().includes('deletion_notice_after_crash'));
781
- // And the unread variant stays traceless: born, never read, crash, retract.
782
- await f.send('push/event', f.params('3', 'never_read', { key: 'k2', initial: true }));
783
- await crash(f);
784
- await f.create();
785
- const r2 = await f.send('push/event', f.params('4', 'notice2', { key: 'k2', retract: true }));
786
- assert.equal(r2.result.coalesce.outcome, 'noted', 'a stored occurrence counts as read after recovery (watermark at head)');
787
- assert(f.context().includes('never_read') && f.context().includes('notice2'));
788
- });
789
- // ---------------------------------------------------------------------------
790
- // Review round 5 (#197): durability under SIGKILL (no stop, no close, no sync)
791
- // ---------------------------------------------------------------------------
792
- async function killChild(mode) {
793
- const dir = mkdtempSync(join(tmpdir(), 'coalescing-kill-'));
794
- const child = fork(join(dirname(fileURLToPath(import.meta.url)), 'helpers', 'coalescing-kill-child.js'), [dir, mode], { stdio: ['ignore', 'ignore', 'inherit', 'ipc'] });
795
- const [msg] = await once(child, 'message');
796
- child.kill('SIGKILL');
797
- await once(child, 'exit');
798
- return { dir, port: msg.port, receipt: msg.receipt };
799
- }
800
- test('R8 (kill, acceptance): the receipt and the accepted work survive SIGKILL after the wire ack', async (t) => {
801
- const { dir, port, receipt } = await killChild('accept');
802
- const f = await fixture({ dir, port });
803
- t.after(async () => { await f.close(); rmSync(dir, { recursive: true, force: true }); });
804
- const retry = await f.send('push/event', f.params('1', 'killed_fallback', { deferred: true, data: { k: 'v' } }));
805
- assert.deepEqual(retry.result, receipt, 'the receipt survived the kill');
806
- await f.framework.runUntilIdle();
807
- assert.equal(f.membrane.calls.length, 1, 'the accepted work survived the kill');
808
- assert(f.lastRequest().includes('killed_fallback'));
809
- });
810
- test('R9 (kill, render-start): a render that may have run is not issued again after SIGKILL', async (t) => {
811
- const { dir, port } = await killChild('render');
812
- const f = await fixture({ dir, port });
813
- t.after(async () => { await f.close(); rmSync(dir, { recursive: true, force: true }); });
814
- await f.framework.runUntilIdle();
815
- assert.equal(f.renders.length, 0, 'no second push/render');
816
- assert(f.lastRequest().includes('render_fallback'), 'the fallback is delivered instead');
817
- });
818
- // ---------------------------------------------------------------------------
819
- // Review round 6 (#197): the asynchronous barrier's failure and interleaving paths
820
- // ---------------------------------------------------------------------------
821
- test('R10: a retry after a failed commit is acknowledged only once the commit succeeds', async (t) => {
822
- const f = await fixture();
823
- t.after(f.close);
824
- const store = f.framework.store;
825
- const orig = store.sync.bind(store);
826
- let syncs = 0;
827
- let failing = 1;
828
- store.sync = () => { syncs++; if (failing > 0) {
829
- failing--;
830
- throw new Error('EIO simulated');
831
- } orig(); };
832
- const params = f.params('1', 'fallback', { deferred: true, initial: true });
833
- const first = await f.send('push/event', params);
834
- assert.equal(first.error?.code, -32000);
835
- const before = syncs;
836
- const retry = await f.send('push/event', params);
837
- assert.equal(retry.result?.coalesce?.outcome, 'first', 'acknowledged on the retry');
838
- assert(syncs > before, 'the retry committed; it did not just return the pending receipt');
839
- // Still failing: the retry fails again rather than acknowledging.
840
- failing = 1;
841
- const again = await f.send('push/event', f.params('2', 'other', { key: 'k2', initial: true }));
842
- assert.equal(again.error?.code, -32000);
843
- const again2 = await f.send('push/event', f.params('2', 'other', { key: 'k2', initial: true }));
844
- assert.equal(again2.result?.coalesce?.outcome, 'first');
845
- assert.equal(f.framework.getAgent('agent').getContextManager().getAllMessages().filter((m) => JSON.stringify(m.content).includes('other')).length, 1, 'plain: delivered exactly once across the failed and successful acknowledgement');
846
- });
847
- /** Hold the host's next durability commit; returns a release function once it is held. */
848
- function holdNextCommit(f) {
849
- const internals = f.framework;
850
- const orig = internals.commitCoalescingDurable.bind(internals);
851
- return new Promise((held) => {
852
- internals.commitCoalescingDurable = () => {
853
- internals.commitCoalescingDurable = orig;
854
- return new Promise((resolve) => { held(() => orig().then(resolve, resolve)); });
855
- };
856
- });
857
- }
858
- test('R11: a retraction during the render-start commit wait is final; the batch is not restored', async (t) => {
859
- const f = await fixture();
860
- t.after(f.close);
861
- await f.send('push/event', f.params('1', 'withdrawn_fallback', { deferred: true, initial: true }));
862
- const held = holdNextCommit(f);
863
- const run = f.framework.runUntilIdle();
864
- const release = await held;
865
- // The freeze (including this commit wait) is one critical section with
866
- // admission: the retraction is admitted only after it, so it is sent
867
- // without awaiting here and settles either way as a cancellation.
868
- const pending = f.send('push/event', f.params('2', 'notice', { retract: true }));
869
- release();
870
- const r = await pending;
871
- assert.equal(r.result.coalesce.outcome, 'retracted');
872
- await run;
873
- assert.equal(f.framework.pushCoalescer.pendingBatches(), 0);
874
- assert(!f.context().includes('withdrawn_fallback') && !f.context().includes('document_diff'), 'nothing of the withdrawn batch was published');
875
- assert.equal(f.membrane.calls.length, 0, 'and the withdrawn batch, the only cause, started no inference');
876
- const renders = f.renders.length;
877
- await f.turn();
878
- assert.equal(f.renders.length, renders, 'and nothing renders later');
879
- assert(!f.context().includes('withdrawn_fallback') && !f.context().includes('document_diff'));
880
- });
881
- test('R12: endpoint reassignment during the render-start commit wait never sends the batch to the new peer', async (t) => {
882
- const f = await fixture();
883
- t.after(f.close);
884
- await f.send('push/event', f.params('1', 'old_fallback', { deferred: true, data: { secret: 'private_notice_data' } }));
885
- const held = holdNextCommit(f);
886
- const run = f.framework.runUntilIdle();
887
- const release = await held;
888
- const config = f.framework.mcplServerConfigs.get('editor');
889
- await f.framework.restartMcplServer('editor', { ...config, url: `${config.url}/reassigned` });
890
- await eventually(() => f.hostCaps.length === 2, 'reconnect to the new endpoint');
891
- release();
892
- await run;
893
- assert.equal(f.renders.length, 0, 'no push/render to the new peer');
894
- assert(!JSON.stringify(f.renders).includes('private_notice_data'));
895
- assert(!f.context().includes('old_fallback'));
896
- });
897
- // ---------------------------------------------------------------------------
898
- // Review round 7 (#197): neighbours of the round-6 fixes
899
- // ---------------------------------------------------------------------------
900
- test('R13: a retraction during the audience lookup is final; the stale batch is not frozen', async (t) => {
901
- const f = await fixture();
902
- t.after(f.close);
903
- await f.send('push/event', f.params('1', 'withdrawn_fallback', { deferred: true, initial: true }));
904
- const internals = f.framework;
905
- const orig = internals.coalescedAudience.bind(internals);
906
- let release;
907
- const held = new Promise((resolve) => {
908
- internals.coalescedAudience = (occ) => { internals.coalescedAudience = orig; return new Promise((res) => { release = () => orig(occ).then(res); resolve(); }); };
909
- });
910
- const run = f.framework.runUntilIdle();
911
- await held;
912
- const pending = f.send('push/event', f.params('2', 'notice', { retract: true }));
913
- release();
914
- const r = await pending;
915
- assert.equal(r.result.coalesce.outcome, 'retracted');
916
- await run;
917
- assert(!f.context().includes('withdrawn_fallback') && !f.context().includes('document_diff'), 'nothing of the withdrawn batch was published');
918
- await f.turn();
919
- assert(!f.context().includes('document_diff'));
920
- });
921
- test('R14: two consecutive crash windows with an acceptance in between keep both recovered batches', async (t) => {
922
- const f = await fixture();
923
- t.after(f.close);
924
- await f.send('push/event', f.params('a', 'first_window_fallback', { deferred: true, key: 'A' }));
925
- await crash(f);
926
- await f.create();
927
- await f.send('push/event', f.params('b', 'second_window_fallback', { deferred: true, key: 'B' }));
928
- await crash(f);
929
- await f.create();
930
- const retryA = await f.send('push/event', f.params('a', 'first_window_fallback', { deferred: true, key: 'A' }));
931
- assert.equal(retryA.result?.coalesce?.outcome, 'first', "A's receipt survived the second window");
932
- await f.framework.runUntilIdle();
933
- const req = f.lastRequest();
934
- assert(req.includes('first_window_fallback') && req.includes('second_window_fallback'), 'both recovered batches delivered');
935
- });
936
- test('R15: a retraction arriving between the render result and its publication waits for the settlement', async (t) => {
937
- const f = await fixture();
938
- t.after(f.close);
939
- await f.send('push/event', f.params('1', 'fallback', { deferred: true, initial: true }));
940
- const internals = f.framework;
941
- const orig = internals.deliverCoalesced.bind(internals);
942
- let release;
943
- const held = new Promise((resolve) => {
944
- internals.deliverCoalesced = (...a) => { internals.deliverCoalesced = orig; return new Promise((res) => { release = () => orig(...a).then(res); resolve(); }); };
945
- });
946
- f.alwaysRespond(); // the notice wakes a second turn
947
- const run = f.framework.runUntilIdle();
948
- await held; // the render result is in hand; publication is being settled
949
- const pending = f.send('push/event', f.params('2', 'deleted_notice', { retract: true }));
950
- release();
951
- const r = await pending;
952
- assert.equal(r.result.coalesce.outcome, 'noted', 'the retraction saw the published render as read');
953
- await run;
954
- const ctx = f.context();
955
- assert.equal(ctx.split('document_diff').length - 1, 1, 'rendered exactly once');
956
- assert(ctx.includes('deleted_notice'), 'and corrected by the notice');
957
- });
958
- // ---------------------------------------------------------------------------
959
- // Review round 8 (#197): liveness after a failed render-start commit
960
- // ---------------------------------------------------------------------------
961
- test('R16: a batch whose render-start commit failed is re-woken once storage recovers', async (t) => {
962
- const f = await fixture();
963
- t.after(f.close);
964
- const coalescer = f.framework.pushCoalescer;
965
- coalescer.options.recoveryBackoffMs = 50;
966
- const internals = f.framework;
967
- const store = f.framework.store;
968
- const orig = store.sync.bind(store);
969
- let failing = 0;
970
- store.sync = () => { if (failing > 0) {
971
- failing--;
972
- throw new Error('EIO simulated');
973
- } orig(); };
974
- await f.send('push/event', f.params('1', 'asleep_fallback', { deferred: true, initial: true }));
975
- failing = 1; // the render-start commit fails; storage then recovers
976
- await f.framework.runUntilIdle();
977
- assert.equal(f.renders.length, 0);
978
- assert.equal(coalescer.pendingBatches(), 1, 'the batch is kept');
979
- assert(!f.lastRequest().includes('asleep_fallback'));
980
- await eventually(() => internals.pendingRequests.length > 0, 'recovery wake');
981
- await f.turn();
982
- assert(f.lastRequest().includes('asleep_fallback'), 'delivered without unrelated traffic');
983
- assert.equal(coalescer.pendingBatches(), 0);
984
- });
985
- test('R16b: a producer retry re-wakes a batch left asleep by a failed freeze', async (t) => {
986
- const f = await fixture();
987
- t.after(f.close);
988
- const coalescer = f.framework.pushCoalescer;
989
- coalescer.options.recoveryBackoffMs = 600_000; // park the timer; the retry must do it
990
- const internals = f.framework;
991
- const store = f.framework.store;
992
- const orig = store.sync.bind(store);
993
- let failing = 0;
994
- store.sync = () => { if (failing > 0) {
995
- failing--;
996
- throw new Error('EIO simulated');
997
- } orig(); };
998
- const params = f.params('1', 'asleep_fallback', { deferred: true, initial: true });
999
- const first = await f.send('push/event', params);
1000
- failing = 1;
1001
- await f.framework.runUntilIdle();
1002
- assert.equal(internals.pendingRequests.length, 0, 'asleep');
1003
- const retry = await f.send('push/event', params);
1004
- assert.deepEqual(retry.result, first.result);
1005
- assert.equal(internals.pendingRequests.length, 1, 'the retry re-woke it');
1006
- await f.turn();
1007
- assert(f.lastRequest().includes('asleep_fallback'));
1008
- });
1009
- // The timeout bounds a drain that never returns, such as a backoff regressed
1010
- // to a tight loop; npm test sets no timeout of its own.
1011
- /**
1012
- * R17's harness: a coalescing fixture whose store fails while `failing` is
1013
- * set, its recovery backoff at `baseMs`, and every traced recovery wake with
1014
- * the monotonic time it was traced at.
1015
- */
1016
- async function storageOutage(t, baseMs) {
1017
- const f = await fixture();
1018
- const coalescer = f.framework.pushCoalescer;
1019
- coalescer.options.recoveryBackoffMs = baseMs;
1020
- const internals = f.framework;
1021
- const store = f.framework.store;
1022
- const orig = store.sync.bind(store);
1023
- const outage = { failing: false };
1024
- store.sync = () => { if (outage.failing)
1025
- throw new Error('EIO persistent'); orig(); };
1026
- // One hook, in order: storage recovers before the fixture closes, so a
1027
- // failed assertion doesn't add the close's own EIO to its report.
1028
- t.after(async () => { outage.failing = false; await f.close(); });
1029
- const wakes = [];
1030
- f.framework.onTrace((e) => {
1031
- if (e.type === 'mcpl:coalescing' && e.kind === 'recovery-wake') {
1032
- wakes.push({ ...e, at: performance.now() });
1033
- }
1034
- });
1035
- f.alwaysRespond();
1036
- /**
1037
- * Drive until `count` recovery wakes have fired, ending with one still
1038
- * pending. Observe the recovery transitions themselves, not the drains:
1039
- * runUntilIdle drains until idle, so a recovery timer that fires while a
1040
- * drain is still running is consumed by that same drain, which freezes and
1041
- * fails again. One drain can therefore carry several attempts.
1042
- */
1043
- const driveTo = async (count) => {
1044
- while (wakes.length < count) {
1045
- const before = wakes.length;
1046
- await f.framework.runUntilIdle(); // each failed freeze schedules the next recovery
1047
- await eventually(() => internals.pendingRequests.length > 0, `recovery wake after ${wakes.length}`);
1048
- // Between attempts only the recovery timer re-wakes the batch, and it
1049
- // traces before it queues the request: every round adds a wake, so a
1050
- // lost trace fails here instead of looping forever.
1051
- assert(wakes.length > before, 'each re-wake is a traced recovery wake');
1052
- }
1053
- };
1054
- /** Each wake's timer was armed after the wake before it was traced, so their gap is at least its delay. */
1055
- const assertSpacing = (from = 0) => {
1056
- for (let i = from + 1; i < wakes.length; i++) {
1057
- // Less a millisecond: Node may fire a timer up to 1 ms early.
1058
- assert(wakes[i].at - wakes[i - 1].at >= wakes[i].delayMs - 1, `wake ${i + 1} came ${(wakes[i].at - wakes[i - 1].at).toFixed(1)} ms after the one before, under its ${wakes[i].delayMs} ms delay`);
1059
- }
1060
- };
1061
- return { f, coalescer, outage, wakes, driveTo, assertSpacing };
1062
- }
1063
- test('R17: a persistent storage outage backs off (1×, 2×, 4× …) and the series resets on recovery', { timeout: 30_000 }, async (t) => {
1064
- const { f, coalescer, outage, wakes, driveTo, assertSpacing } = await storageOutage(t, 40);
1065
- await f.send('push/event', f.params('1', 'outage_fallback', { deferred: true, initial: true }));
1066
- outage.failing = true;
1067
- await driveTo(3);
1068
- // Every wake is one more consecutive failure, and its delay doubles from the
1069
- // base, however the attempts fell across drains. The timers are spaced at
1070
- // least that far apart, not only labelled so.
1071
- assert.deepEqual(wakes.map((w) => w.attempt), wakes.map((_, i) => i + 1), 'consecutive failures count up');
1072
- assert.deepEqual(wakes.map((w) => w.delayMs), wakes.map((_, i) => Math.min(40 * 2 ** i, 40 * 60)), 'and the delay doubles');
1073
- assertSpacing();
1074
- assert.equal(coalescer.pendingBatches(), 1);
1075
- assert(!f.context().includes('outage_fallback'));
1076
- outage.failing = false; // storage recovers
1077
- await f.framework.runUntilIdle();
1078
- assert(f.lastRequest().includes('outage_fallback'), 'delivered once storage recovered');
1079
- assert.equal(coalescer.pendingBatches(), 0);
1080
- assert.equal(coalescer.recovery.size, 0, 'the failure series is forgotten on success');
1081
- // A second outage starts a new series: its first wake is attempt 1 again,
1082
- // at the base delay.
1083
- const second = wakes.length;
1084
- await f.send('push/event', f.params('2', 'second_outage', { deferred: true }));
1085
- outage.failing = true;
1086
- await driveTo(second + 1);
1087
- assert.deepEqual(wakes.slice(second, second + 1).map(({ attempt, delayMs }) => ({ attempt, delayMs })), [{ attempt: 1, delayMs: 40 }]);
1088
- outage.failing = false;
1089
- await f.framework.runUntilIdle();
1090
- assert(f.lastRequest().includes('second_outage'), 'delivered once storage recovered again');
1091
- });
1092
- test('R17b: the backoff stops doubling at 60× its base', { timeout: 30_000 }, async (t) => {
1093
- // A small base reaches the cap within a second: attempt 7 is the first
1094
- // whose doubling (64×) passes it.
1095
- const { f, wakes, outage, driveTo, assertSpacing } = await storageOutage(t, 3);
1096
- await f.send('push/event', f.params('1', 'capped_fallback', { deferred: true, initial: true }));
1097
- outage.failing = true;
1098
- await driveTo(8);
1099
- assert.deepEqual(wakes.slice(0, 8).map((w) => w.delayMs), [3, 6, 12, 24, 48, 96, 180, 180]);
1100
- assertSpacing();
1101
- });
1102
- // ---------------------------------------------------------------------------
1103
- // Review round 10 (#197): residents share one message slot
1104
- // ---------------------------------------------------------------------------
1105
- for (const reader of ['other', 'agent'])
1106
- test(`R18 (${reader} read it): a message read by any resident of the shared slot is history for all of them`, async (t) => {
1107
- const f = await fixture({ agents: [{ name: 'agent', model: 'test', systemPrompt: 'test' }, { name: 'other', model: 'test', systemPrompt: 'test' }], server: { shouldTriggerInference: () => true } });
1108
- t.after(f.close);
1109
- await f.send('push/event', f.params('1', 'read_by_one', { initial: true }));
1110
- const internals = f.framework;
1111
- internals.pendingRequests = internals.pendingRequests.filter((r) => r.agentName === reader);
1112
- f.alwaysRespond();
1113
- await f.framework.runUntilIdle();
1114
- assert(f.lastRequest().includes('read_by_one'), `${reader} read it`);
1115
- const edit = await f.send('push/event', f.params('2', 'later_edit'));
1116
- assert.equal(edit.result.coalesce.outcome, 'appended');
1117
- for (const name of ['agent', 'other']) {
1118
- const ctx = JSON.stringify(f.framework.getAgent(name).getContextManager().getAllMessages());
1119
- assert(ctx.includes('read_by_one') && ctx.includes('later_edit'), `${name} keeps the consumed original`);
1120
- }
1121
- const del = await f.send('push/event', f.params('3', 'deletion_notice', { retract: true }));
1122
- assert.equal(del.result.coalesce.outcome, 'noted');
1123
- assert(f.context().includes('read_by_one') && f.context().includes('deletion_notice') && !f.context().includes('later_edit'));
1124
- });
1125
- //# sourceMappingURL=mcpl-coalescing.test.js.map