@animalabs/agent-framework 0.22.1 → 0.22.3

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