@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,1394 +0,0 @@
1
- /**
2
- * Explicit prose routing (docs/explicit-prose-routing.md).
3
- *
4
- * The model names every destination in-band (`>>` prefixes); unprefixed prose
5
- * is NEVER delivered — it bounces to a clipboard for a prefixed resend. The
6
- * laria regression (2026-07-24): a DM reply generated while the host-inferred
7
- * locus pointed at a guild channel was misdelivered there. In explicit mode
8
- * that class is structurally impossible: the worst case is a bounce.
9
- */
10
- import { describe, it, beforeEach, afterEach } from 'node:test';
11
- import assert from 'node:assert/strict';
12
- import { tmpdir } from 'node:os';
13
- import { mkdtempSync, rmSync } from 'node:fs';
14
- import { join } from 'node:path';
15
- import { AgentFramework } from '../src/index.js';
16
- import { MockMembrane, createMockResponse } from './helpers/mock-membrane.js';
17
- import { ProseStreamRouter } from '../src/mcpl/prose-stream-router.js';
18
- class ToolboxModule {
19
- name = 'robot';
20
- framework = null;
21
- calls = [];
22
- async start(_ctx) { }
23
- async stop() { }
24
- getTools() {
25
- return [{
26
- name: 'move',
27
- description: 'Move the robot',
28
- inputSchema: { type: 'object', properties: { dir: { type: 'string' } } },
29
- }, {
30
- name: 'say',
31
- description: 'Publish speech into the world',
32
- inputSchema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
33
- }];
34
- }
35
- async handleToolCall(call) {
36
- this.calls.push(call);
37
- return { success: true, data: { ok: true, published: call.name.endsWith('--say') } };
38
- }
39
- async onProcess(event, _state) {
40
- if (event.type === 'external-message') {
41
- const text = String(event.content);
42
- return {
43
- addMessages: [{ participant: 'Antra', content: [{ type: 'text', text }] }],
44
- requestInference: text === 'go',
45
- };
46
- }
47
- return {};
48
- }
49
- }
50
- /** Registry stub: two known channels (#alpha guild, laria's DM) + capture. */
51
- function stubRegistry(framework, plan = []) {
52
- const routed = [];
53
- const known = {
54
- '#alpha': { channelId: 'chan-alpha', label: '#alpha' },
55
- '#cafe': { channelId: 'discord:g:cafe', label: '#cafe' },
56
- 'world:commons': { channelId: 'world:commons', label: 'world:commons' },
57
- 'alpha': { channelId: 'chan-alpha', label: '#alpha' },
58
- 'chan-alpha': { channelId: 'chan-alpha', label: '#alpha' },
59
- '@laria': { channelId: 'discord:dm:99', label: 'DM: laria' },
60
- 'discord:dm:99': { channelId: 'discord:dm:99', label: 'DM: laria' },
61
- };
62
- const explicit = {
63
- resolveProseTarget: (spec) => known[spec] ?? { error: `no channel matches "${spec}"` },
64
- routeSpeech: async (_agent, text, locus) => {
65
- routed.push({ text, locus: locus ?? null });
66
- const behavior = plan.shift() ?? 'delivered';
67
- if (behavior === 'throw')
68
- throw new Error('synthetic publication failure');
69
- return { delivered: behavior === 'delivered', channelId: locus ?? '' };
70
- },
71
- resolveLocus: () => 'world:commons',
72
- getDefaultPublishChannel: () => null,
73
- isChannelOpen: () => true,
74
- getDescriptor: () => undefined,
75
- getChannelTools: () => [],
76
- };
77
- framework.channelRegistry = new Proxy(explicit, {
78
- get: (target, prop) => (prop in target ? target[prop] : () => undefined),
79
- });
80
- return routed;
81
- }
82
- describe('explicit prose routing', () => {
83
- let tempDir;
84
- let membrane;
85
- let module;
86
- beforeEach(() => {
87
- tempDir = mkdtempSync(join(tmpdir(), 'epr-test-'));
88
- membrane = new MockMembrane();
89
- module = new ToolboxModule();
90
- });
91
- afterEach(() => {
92
- rmSync(tempDir, { recursive: true, force: true });
93
- });
94
- async function createFramework(mode = 'explicit') {
95
- const framework = await AgentFramework.create({
96
- storePath: join(tempDir, 'test.chronicle'),
97
- membrane: membrane.asMembrane(),
98
- agents: [{
99
- name: 'assistant',
100
- model: 'test-model',
101
- systemPrompt: 'You are a test agent.',
102
- proseRouting: mode,
103
- }],
104
- modules: [module],
105
- });
106
- module.framework = framework;
107
- return framework;
108
- }
109
- function trigger(framework) {
110
- framework.pushEvent({
111
- type: 'external-message',
112
- source: 'test',
113
- content: 'go',
114
- metadata: {},
115
- });
116
- }
117
- /** Feed a response for the Nth turn (1-based) the moment it starts —
118
- * MockMembrane hands ALL remaining responses to the first stream, so
119
- * later turns must receive theirs at inference:started time. */
120
- function feedTurn(framework, turn, blocks, stop) {
121
- let count = 0;
122
- const off = framework.onTrace((e) => {
123
- if (e.type !== 'inference:started')
124
- return;
125
- count++;
126
- if (count === turn) {
127
- membrane.pushResponse(createMockResponse(blocks, stop));
128
- off();
129
- }
130
- });
131
- }
132
- it('unprefixed prose NEVER delivers — bounces to clipboard, then a prefixed {{unsent}} resend delivers verbatim (laria regression)', async () => {
133
- const original = 'laria.\n\nYes, I remember. Two words through the keyhole.';
134
- membrane.pushResponse(createMockResponse([{ type: 'text', text: original }]));
135
- const framework = await createFramework();
136
- const routed = stubRegistry(framework);
137
- const bounceNotices = [];
138
- framework.onTrace((e) => {
139
- const ev = e;
140
- if (ev.type === 'message:added' && ev.source === 'prose-bounce')
141
- bounceNotices.push(1);
142
- });
143
- // Turn 2 = the bounce-wake: model resends with a destination.
144
- feedTurn(framework, 2, [{ type: 'text', text: '>>@laria {{unsent}}' }]);
145
- trigger(framework);
146
- await framework.runUntilIdle();
147
- assert.equal(bounceNotices.length, 1, 'exactly one bounce notice');
148
- assert.equal(routed.length, 1, 'only the prefixed resend delivered');
149
- assert.equal(routed[0].locus, 'discord:dm:99', 'delivered to the DM the model named');
150
- assert.equal(routed[0].text, original, 'clipboard substitution is verbatim');
151
- await framework.stop();
152
- });
153
- it('resolves label prefixes and applies the sticky turn target to later unprefixed segments', async () => {
154
- membrane.pushResponse(createMockResponse([
155
- { type: 'text', text: '>>#alpha first segment' },
156
- { type: 'tool_use', id: 'c1', name: 'robot--move', input: { dir: 'north' } },
157
- ], 'tool_use'));
158
- membrane.pushResponse(createMockResponse([
159
- { type: 'text', text: 'second segment, no prefix' },
160
- ]));
161
- const framework = await createFramework();
162
- const routed = stubRegistry(framework);
163
- trigger(framework);
164
- await framework.runUntilIdle();
165
- assert.deepEqual(routed, [
166
- { text: 'first segment', locus: 'chan-alpha' },
167
- { text: 'second segment, no prefix', locus: 'chan-alpha' },
168
- ], 'prefix stripped on wire; sticky target carries the second segment');
169
- await framework.stop();
170
- });
171
- it('>>skip_reply keeps the text in context — nothing delivered, no bounce', async () => {
172
- membrane.pushResponse(createMockResponse([
173
- { type: 'text', text: '>>skip_reply\nWorking through the plan here.' },
174
- ]));
175
- const framework = await createFramework();
176
- const routed = stubRegistry(framework);
177
- const bounceNotices = [];
178
- framework.onTrace((e) => {
179
- const ev = e;
180
- if (ev.type === 'message:added' && ev.source === 'prose-bounce')
181
- bounceNotices.push(1);
182
- });
183
- trigger(framework);
184
- await framework.runUntilIdle();
185
- assert.equal(routed.length, 0, 'skip_reply text is never delivered');
186
- assert.equal(bounceNotices.length, 0, 'skip_reply text never bounces');
187
- await framework.stop();
188
- });
189
- /** The registry stub answers skip_reply as the real registry does: the turn ends, and nothing is sent. */
190
- function answerSkipReply(framework) {
191
- const answered = { calls: 0 };
192
- const registry = framework.channelRegistry;
193
- registry.handleChannelToolCall = async (name) => {
194
- if (name === 'skip_reply')
195
- answered.calls++;
196
- return { success: true, endTurn: true, data: { skipped: true, note: 'Turn ended; nothing sent.' } };
197
- };
198
- return answered;
199
- }
200
- /** What the registry stub's streaming surface receives (MCPL Spec 14.3 chunk and complete). */
201
- function captureStream(framework) {
202
- const chunks = [];
203
- const completes = [];
204
- const registry = framework.channelRegistry;
205
- registry.sendOutgoingChunk = (channelId, _agent, _id, _index, delta) => {
206
- chunks.push({ channelId, delta });
207
- };
208
- registry.sendOutgoingComplete = (channelId, _agent, _id, text) => {
209
- completes.push({ channelId, text });
210
- };
211
- return { chunks, completes };
212
- }
213
- /** The texts of the agent's context, and the bounce notices among them. */
214
- function context(framework) {
215
- const messages = framework.getAgent('assistant').getContextManager().getAllMessages();
216
- return {
217
- texts: messages.flatMap((m) => m.content.filter((b) => b.type === 'text').map((b) => b.text ?? '')),
218
- bounces: messages.filter((m) => m.metadata?.kind === 'prose-bounce').length,
219
- };
220
- }
221
- const clipboardOf = (framework) => framework.proseClipboards.get('assistant');
222
- it('a round that calls skip_reply keeps its unprefixed prose private: nothing delivered, bounced, retained or woken for', async () => {
223
- membrane.pushResponse(createMockResponse([
224
- { type: 'text', text: 'Weighing whether this needs an answer from me.' },
225
- { type: 'tool_use', id: 'sr1', name: 'skip_reply', input: { reason: 'nothing to add' } },
226
- ], 'tool_use'));
227
- const framework = await createFramework();
228
- const routed = stubRegistry(framework);
229
- const skip = answerSkipReply(framework);
230
- // Were the text bounced, its wake would start a second turn: give that
231
- // turn a response, so the test fails on its count instead of hanging.
232
- feedTurn(framework, 2, [{ type: 'text', text: '>>skip_reply' }]);
233
- let turns = 0;
234
- framework.onTrace((e) => {
235
- if (e.type === 'inference:started')
236
- turns++;
237
- });
238
- trigger(framework);
239
- await framework.runUntilIdle();
240
- assert.equal(skip.calls, 1, 'the skip_reply round ran');
241
- assert.equal(routed.length, 0, 'nothing is delivered');
242
- const seen = context(framework);
243
- assert.equal(seen.bounces, 0, 'no bounce notice reaches the context');
244
- assert.ok(seen.texts.some((t) => t.includes('Weighing whether this needs an answer')), 'the text stays in the context');
245
- assert.equal(clipboardOf(framework), undefined, 'nothing is retained for {{unsent}}');
246
- assert.equal(turns, 1, 'no bounce wake starts another turn');
247
- await framework.stop();
248
- });
249
- it('a round that calls skip_reply keeps its unprefixed prose from a sticky destination', async () => {
250
- membrane.pushResponse(createMockResponse([
251
- { type: 'text', text: '>>#alpha first, deliberately' },
252
- { type: 'tool_use', id: 'c1', name: 'robot--move', input: { dir: 'north' } },
253
- ], 'tool_use'));
254
- membrane.pushResponse(createMockResponse([
255
- { type: 'text', text: 'Second thoughts I keep to myself.' },
256
- { type: 'tool_use', id: 'sr2', name: 'skip_reply', input: {} },
257
- ], 'tool_use'));
258
- const framework = await createFramework();
259
- const routed = stubRegistry(framework);
260
- const skip = answerSkipReply(framework);
261
- trigger(framework);
262
- await framework.runUntilIdle();
263
- assert.equal(skip.calls, 1, 'the skip_reply round ran');
264
- assert.deepEqual(routed, [{ text: 'first, deliberately', locus: 'chan-alpha' }], 'only the earlier, deliberately addressed segment goes out');
265
- assert.ok(context(framework).texts.some((t) => t.includes('Second thoughts I keep to myself.')), 'the text stays in the context');
266
- await framework.stop();
267
- });
268
- it('a round that calls skip_reply sends nothing, a prefixed segment included, and asks for no continuation', async () => {
269
- membrane.pushResponse(createMockResponse([
270
- { type: 'text', text: '>>#alpha ! addressed, then thought better of' },
271
- { type: 'tool_use', id: 'sr3', name: 'skip_reply', input: {} },
272
- ], 'tool_use'));
273
- const framework = await createFramework();
274
- const routed = stubRegistry(framework);
275
- const skip = answerSkipReply(framework);
276
- // A continuation would start a second turn: give it a response, so the
277
- // test fails on its count instead of hanging.
278
- feedTurn(framework, 2, [{ type: 'text', text: '>>skip_reply' }]);
279
- let turns = 0;
280
- framework.onTrace((e) => {
281
- if (e.type === 'inference:started')
282
- turns++;
283
- });
284
- trigger(framework);
285
- await framework.runUntilIdle();
286
- assert.equal(skip.calls, 1, 'the skip_reply round ran');
287
- assert.equal(routed.length, 0, 'skip_reply ends the turn without sending anything');
288
- assert.equal(context(framework).bounces, 0);
289
- assert.equal(turns, 1, "the round's ' !' starts no continuation after skip_reply ended the turn");
290
- await framework.stop();
291
- });
292
- async function within(promise, label, ms = 5000) {
293
- let timer;
294
- try {
295
- return await Promise.race([promise, new Promise((_, reject) => {
296
- timer = setTimeout(() => reject(new Error('Timed out waiting for ' + label)), ms);
297
- })]);
298
- }
299
- finally {
300
- clearTimeout(timer);
301
- }
302
- }
303
- function nextTrace(framework, type) {
304
- let resolve;
305
- const promise = new Promise((done) => { resolve = done; });
306
- const off = framework.onTrace((event) => {
307
- if (event.type === type && event.agentName === 'assistant') {
308
- off();
309
- resolve();
310
- }
311
- });
312
- return { promise, dispose: off };
313
- }
314
- async function withQueuedTurns(mode, lateText, nextResponses, inspect) {
315
- const arrows = mode === 'explicit' ? '>>' : '>>>';
316
- membrane.pushResponse(createMockResponse([
317
- { type: 'text', text: arrows + '#alpha first' },
318
- { type: 'tool_use', id: 'q1', name: 'robot--move', input: { dir: 'north' } },
319
- ], 'tool_use'));
320
- membrane.pushResponse(createMockResponse([
321
- { type: 'text', text: lateText },
322
- { type: 'tool_use', id: 'q2', name: 'robot--move', input: { dir: 'south' } },
323
- ], 'tool_use'));
324
- membrane.pushResponse(createMockResponse([
325
- { type: 'tool_use', id: 'q3', name: 'skip_reply', input: {} },
326
- ], 'tool_use'));
327
- const framework = await createFramework(mode);
328
- const routed = stubRegistry(framework);
329
- const skip = answerSkipReply(framework);
330
- const registry = framework.channelRegistry;
331
- const route = registry.routeSpeech;
332
- let release;
333
- const held = new Promise((resolve) => { release = resolve; });
334
- registry.routeSpeech = async (agent, text, locus) => {
335
- const outcome = await route(agent, text, locus);
336
- if (text.trim() === 'first')
337
- await held;
338
- return outcome;
339
- };
340
- let turns = 0;
341
- framework.onTrace((event) => {
342
- if (event.type !== 'inference:started')
343
- return;
344
- turns++;
345
- if (turns === 2)
346
- for (const response of nextResponses)
347
- membrane.pushResponse(response);
348
- // Serve a bounce wake or an unexpected continuation without hanging.
349
- if (turns === 3)
350
- membrane.pushResponse(createMockResponse([{ type: 'text', text: arrows + 'skip_reply' }]));
351
- });
352
- const internals = framework;
353
- const processRequests = internals.processInferenceRequests.bind(framework);
354
- let nextWakeQueued = false;
355
- let resolveBlocked;
356
- const blocked = new Promise((resolve) => { resolveBlocked = resolve; });
357
- internals.processInferenceRequests = async () => {
358
- await processRequests();
359
- if (nextWakeQueued && turns === 1 && internals.pendingRequests.some((r) => r.agentName === 'assistant')) {
360
- resolveBlocked();
361
- }
362
- };
363
- const ended = nextTrace(framework, 'inference:turn_ended');
364
- let completed;
365
- let started;
366
- let stopped = false;
367
- let driving;
368
- const drain = async () => {
369
- stopped = true;
370
- if (driving)
371
- await within(driving, 'the queued turns to drain', 10000);
372
- };
373
- try {
374
- trigger(framework);
375
- driving = (async () => {
376
- while (!stopped) {
377
- await framework.runUntilIdle();
378
- if (!stopped)
379
- await new Promise((resolve) => setImmediate(resolve));
380
- }
381
- })();
382
- void driving.catch(() => { });
383
- await within(ended.promise, 'the first turn to end');
384
- assert.equal(skip.calls, 1, 'skip_reply ended the first turn');
385
- const earlier = internals.turnRouting.get('assistant');
386
- completed = nextTrace(framework, 'inference:completed');
387
- started = nextTrace(framework, 'inference:started');
388
- nextWakeQueued = true;
389
- trigger(framework);
390
- if (mode === 'hybrid') {
391
- const disposition = await within(Promise.race([
392
- blocked.then(() => 'blocked'), started.promise.then(() => 'started'),
393
- ]), 'the next wake to reach scheduling');
394
- assert.equal(disposition, 'blocked', 'hybrid retains the earlier turn token until its receipt tail drains');
395
- assert.equal(internals.turnRouting.get('assistant'), earlier, 'the earlier turn still owns routing');
396
- assert.deepEqual(routed.map((r) => r.text.trim()), ['first']);
397
- release();
398
- await within(completed.promise, 'the next inference after release');
399
- assert.equal(turns, 2, 'hybrid starts the next turn after the earlier teardown');
400
- }
401
- else {
402
- await within(completed.promise, 'the next inference to complete');
403
- assert.equal(turns, 2, 'explicit starts the next turn without waiting');
404
- assert.deepEqual(routed.map((r) => r.text.trim()), ['first'], "only the first post has entered routing while its confirmation is held");
405
- }
406
- await inspect({ framework, routed, earlier, next: internals.turnRouting.get('assistant'),
407
- turns: () => turns, release, drain });
408
- }
409
- finally {
410
- ended.dispose();
411
- completed?.dispose();
412
- started?.dispose();
413
- internals.processInferenceRequests = processRequests;
414
- release();
415
- try {
416
- await drain();
417
- }
418
- finally {
419
- await framework.stop();
420
- }
421
- }
422
- }
423
- async function withHeldHybridCallback(lateText, inspect) {
424
- const framework = await createFramework('hybrid');
425
- const routed = stubRegistry(framework);
426
- const gateway = framework;
427
- const agent = framework.getAgent('assistant');
428
- const earlier = { destination: null, hybridHold: false, logical: { continuation: false, deliveries: [], suppressed: 0 } };
429
- const next = { ...earlier, logical: { continuation: false, deliveries: [], suppressed: 0 } };
430
- gateway.turnRouting.set('assistant', earlier);
431
- const registry = framework.channelRegistry;
432
- const route = registry.routeSpeech;
433
- let release;
434
- let resolveEntered;
435
- const held = new Promise((resolve) => { release = resolve; });
436
- const entered = new Promise((resolve) => { resolveEntered = resolve; });
437
- registry.routeSpeech = async (name, text, locus) => {
438
- const outcome = await route(name, text, locus);
439
- resolveEntered();
440
- await held;
441
- return outcome;
442
- };
443
- const first = gateway.deliverHybridProse(agent, '>>>#alpha first', 'world:commons', true, earlier);
444
- const pending = lateText === null ? first
445
- : first.then(() => gateway.deliverHybridProse(agent, lateText, 'world:commons', true, earlier));
446
- void pending.catch(() => { });
447
- try {
448
- await within(entered, 'the earlier callback to enter routing');
449
- // Exercise callback ownership directly. Normal hybrid scheduling keeps
450
- // its turn token until the receipt tail drains, so fresh turns serialize.
451
- gateway.turnRouting.set('assistant', next);
452
- await inspect({ framework, routed, gateway, earlier, next, agent, pending, release });
453
- }
454
- finally {
455
- release();
456
- try {
457
- await within(pending, 'the held callback to drain');
458
- }
459
- finally {
460
- await framework.stop();
461
- }
462
- }
463
- }
464
- for (const mode of ['explicit', 'hybrid']) {
465
- const title = mode === 'explicit'
466
- ? "explicit: prose a turn leaves queued follows that turn's destination, and the next turn's posts wait behind it"
467
- : "hybrid: the next turn waits for the ended turn's receipt tail, and posts keep their destinations and order";
468
- it(title, async () => {
469
- const arrows = mode === 'explicit' ? '>>' : '>>>';
470
- await withQueuedTurns(mode, 'and then this', [
471
- createMockResponse([{ type: 'text', text: arrows + '#cafe next' }]),
472
- ], async ({ framework, routed, release, drain }) => {
473
- release();
474
- await drain();
475
- assert.deepEqual(routed.map((r) => ({ text: r.text.trim(), locus: r.locus })), [
476
- { text: 'first', locus: 'chan-alpha' },
477
- { text: 'and then this', locus: 'chan-alpha' },
478
- { text: 'next', locus: 'discord:g:cafe' },
479
- ], "each post follows its own turn's destination, in delivery order");
480
- assert.equal(context(framework).bounces, 0, 'nothing bounces into the next turn');
481
- });
482
- });
483
- }
484
- it("a queued round's late continuation starts once its tool-ended turn's deliveries have run", async () => {
485
- await withQueuedTurns('explicit', '>>#alpha ! late', [
486
- createMockResponse([{ type: 'text', text: '>>#cafe next' }]),
487
- ], async ({ earlier, next, turns, release, drain }) => {
488
- assert.equal(earlier.logical.continuation, false, 'the continuation has not run before release');
489
- release();
490
- for (const end = Date.now() + 5000; turns() < 3 && Date.now() < end;) {
491
- await new Promise((resolve) => setTimeout(resolve, 5));
492
- }
493
- await drain();
494
- assert.equal(turns(), 3, 'the continuation starts its own turn, once');
495
- assert.equal(earlier.logical.continuation, false, "the earlier turn's request was taken from its own object");
496
- assert.equal(next.logical.continuation, false, 'the next turn never receives that continuation');
497
- });
498
- });
499
- for (const mode of ['explicit', 'hybrid']) {
500
- it(`${mode}: a ' !' an earlier round asked for starts its continuation when skip_reply ends the turn`, async () => {
501
- const arrows = mode === 'explicit' ? '>>' : '>>>';
502
- membrane.pushResponse(createMockResponse([
503
- { type: 'text', text: arrows + '#alpha ! keep going' },
504
- { type: 'tool_use', id: 'k1', name: 'robot--move', input: { dir: 'north' } },
505
- ], 'tool_use'));
506
- membrane.pushResponse(createMockResponse([
507
- { type: 'tool_use', id: 'k2', name: 'skip_reply', input: {} },
508
- ], 'tool_use'));
509
- const framework = await createFramework(mode);
510
- const routed = stubRegistry(framework);
511
- const skip = answerSkipReply(framework);
512
- let turns = 0;
513
- framework.onTrace((e) => {
514
- if (e.type === 'inference:started')
515
- turns++;
516
- });
517
- feedTurn(framework, 2, [{ type: 'text', text: arrows + 'skip_reply' }]);
518
- const reasons = [];
519
- const internals = framework;
520
- const process = internals.processInferenceRequests.bind(framework);
521
- internals.processInferenceRequests = async () => {
522
- for (const request of internals.pendingRequests)
523
- reasons.push(request.reason);
524
- await process();
525
- };
526
- trigger(framework);
527
- await framework.runUntilIdle();
528
- for (const end = Date.now() + 5000; turns < 2 && Date.now() < end;) {
529
- await new Promise((resolve) => setTimeout(resolve, 5));
530
- await framework.runUntilIdle();
531
- }
532
- assert.equal(skip.calls, 1, 'skip_reply ended the first turn');
533
- assert.deepEqual(routed.map((r) => r.text.trim()), ['keep going']);
534
- assert.ok(reasons.includes('prose-continuation'), 'the continuation was requested');
535
- assert.equal(turns, 2, 'and it started one more turn');
536
- await framework.stop();
537
- });
538
- }
539
- for (const mode of ['explicit', 'hybrid']) {
540
- it(`${mode}: a tool-ended turn's ' !' queued behind a held post starts its continuation once that post settles`, async () => {
541
- const arrows = mode === 'explicit' ? '>>' : '>>>';
542
- membrane.pushResponse(createMockResponse([
543
- { type: 'text', text: `${arrows}#alpha first\n${arrows}#alpha ! second` },
544
- { type: 'tool_use', id: 'w1', name: 'robot--move', input: { dir: 'north' } },
545
- ], 'tool_use'));
546
- membrane.pushResponse(createMockResponse([
547
- { type: 'tool_use', id: 'w2', name: 'skip_reply', input: {} },
548
- ], 'tool_use'));
549
- const framework = await createFramework(mode);
550
- const routed = stubRegistry(framework);
551
- answerSkipReply(framework);
552
- let turns = 0;
553
- framework.onTrace((e) => {
554
- if (e.type === 'inference:started')
555
- turns++;
556
- });
557
- feedTurn(framework, 2, [{ type: 'text', text: arrows + 'skip_reply' }]);
558
- const registry = framework.channelRegistry;
559
- const route = registry.routeSpeech;
560
- let release;
561
- const held = new Promise((resolve) => { release = resolve; });
562
- let entered;
563
- const reached = new Promise((resolve) => { entered = resolve; });
564
- registry.routeSpeech = async (agent, text, locus) => {
565
- const outcome = await route(agent, text, locus);
566
- if (text.trim() === 'first') {
567
- entered();
568
- await held;
569
- }
570
- return outcome;
571
- };
572
- const internals = framework;
573
- const ended = nextTrace(framework, 'inference:turn_ended');
574
- trigger(framework);
575
- const running = framework.runUntilIdle();
576
- try {
577
- await within(reached, "the first envelope's post to start");
578
- await within(ended.promise, 'skip_reply to end the turn');
579
- assert.equal(turns, 1);
580
- assert.ok(!internals.pendingRequests.some((r) => r.reason === 'prose-continuation'), "no continuation while the ' !' envelope waits behind the held post");
581
- release();
582
- await within(running, 'the turn to settle');
583
- for (const end = Date.now() + 5000; turns < 2 && Date.now() < end;) {
584
- await new Promise((resolve) => setTimeout(resolve, 5));
585
- await framework.runUntilIdle();
586
- }
587
- assert.deepEqual(routed.map((r) => r.text.trim()), ['first', 'second']);
588
- assert.equal(turns, 2, 'the continuation starts once the queued envelope has run');
589
- }
590
- finally {
591
- ended.dispose();
592
- release();
593
- await framework.stop();
594
- }
595
- });
596
- }
597
- for (const when of ['already set', 'set late']) {
598
- it(`hybrid: a ' !' an earlier stream asked for (${when}) still starts its continuation after a re-inference on unstreamed tool results`, async () => {
599
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'carrying on' }]));
600
- const framework = await createFramework('hybrid');
601
- const routed = stubRegistry(framework);
602
- let turns = 0;
603
- framework.onTrace((e) => {
604
- if (e.type === 'inference:started')
605
- turns++;
606
- });
607
- feedTurn(framework, 2, [{ type: 'text', text: '>>>skip_reply' }]);
608
- const internals = framework;
609
- let release;
610
- const held = new Promise((resolve) => { release = resolve; });
611
- try {
612
- // The turn's earlier stream asked for a continuation, or will once
613
- // its queued delivery runs; the turn goes on in a re-inference, which
614
- // starts a new stream with a fresh routing state while the earlier
615
- // stream's logical turn is still open.
616
- const earlier = { destination: null, hybridHold: false, logical: { continuation: when === 'already set', deliveries: [], suppressed: 0 } };
617
- internals.turnRouting.set('assistant', earlier);
618
- internals.openLogical.set('assistant', earlier.logical);
619
- if (when === 'set late') {
620
- internals.proseSpeechChains.set('assistant', held.then(() => { earlier.logical.continuation = true; }));
621
- }
622
- internals.pendingRequests.push({ agentName: 'assistant', reason: 'tool_results_ready', source: 'framework', timestamp: Date.now() });
623
- const running = framework.runUntilIdle();
624
- if (when === 'set late') {
625
- for (const end = Date.now() + 5000; turns < 1 && Date.now() < end;) {
626
- await new Promise((resolve) => setTimeout(resolve, 5));
627
- }
628
- assert.equal(turns, 1, 'the re-inference has started');
629
- assert.equal(earlier.logical.continuation, false, 'the earlier delivery has not run as the re-inference starts');
630
- release();
631
- }
632
- await within(running, 'the re-inference to settle');
633
- for (const end = Date.now() + 5000; turns < 2 && Date.now() < end;) {
634
- await new Promise((resolve) => setTimeout(resolve, 5));
635
- await framework.runUntilIdle();
636
- }
637
- assert.deepEqual(routed.map((r) => r.text.trim()), ['carrying on']);
638
- assert.equal(turns, 2, 'the re-inference completes, then the continuation it carried starts');
639
- assert.equal(earlier.logical.continuation, false, "the check took the earlier stream's flag");
640
- }
641
- finally {
642
- release();
643
- await framework.stop();
644
- }
645
- });
646
- }
647
- it("hybrid: a re-inference on unstreamed tool results receipts the earlier stream's late delivery with its own", async () => {
648
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'carrying on' }]));
649
- const framework = await createFramework('hybrid');
650
- const routed = stubRegistry(framework);
651
- let turns = 0;
652
- framework.onTrace((e) => {
653
- if (e.type === 'inference:started')
654
- turns++;
655
- });
656
- const internals = framework;
657
- let release;
658
- const held = new Promise((resolve) => { release = resolve; });
659
- try {
660
- // The earlier stream left its tool results pending, its logical turn
661
- // still open; its delivery lands only after the re-inference has
662
- // begun, into the logical turn the two share.
663
- const earlier = { destination: null, hybridHold: false, logical: { continuation: false, deliveries: [], suppressed: 0 } };
664
- internals.turnRouting.set('assistant', earlier);
665
- internals.openLogical.set('assistant', earlier.logical);
666
- internals.proseSpeechChains.set('assistant', held.then(() => { earlier.logical.deliveries.push('chan-alpha'); }));
667
- internals.pendingRequests.push({ agentName: 'assistant', reason: 'tool_results_ready', source: 'framework', timestamp: Date.now() });
668
- const running = framework.runUntilIdle();
669
- for (const end = Date.now() + 5000; turns < 1 && Date.now() < end;) {
670
- await new Promise((resolve) => setTimeout(resolve, 5));
671
- }
672
- assert.equal(turns, 1, 'the re-inference has started');
673
- assert.deepEqual(earlier.logical.deliveries, [], 'the earlier delivery has not landed yet');
674
- release();
675
- await within(running, 'the re-inference to settle');
676
- assert.deepEqual(routed.map((r) => r.text.trim()), ['carrying on']);
677
- const receipt = context(framework).texts.filter((t) => t.startsWith('[delivered]'));
678
- assert.equal(receipt.length, 1, `one receipt for the logical turn: ${receipt.join(' | ')}`);
679
- assert.match(receipt[0], /chan-alpha/, "it reports the earlier stream's late delivery");
680
- assert.match(receipt[0], /world:commons/, "and the re-inference's own");
681
- }
682
- finally {
683
- release();
684
- await framework.stop();
685
- }
686
- });
687
- for (const mode of ['explicit', 'hybrid']) {
688
- const p = mode === 'explicit' ? '>>' : '>>>';
689
- it(`${mode}: an error retry keeps a ' !' whose envelope is delivered after the retry starts`, async () => {
690
- membrane.pushResponse(createMockResponse([
691
- { type: 'text', text: `${p}#alpha first\n${p}#alpha ! second` },
692
- { type: 'tool_use', id: 'retry-held', name: 'robot--move', input: { dir: 'north' } },
693
- ], 'tool_use'));
694
- membrane.pushResponse(createMockResponse([]));
695
- const framework = await createFramework(mode);
696
- const routed = stubRegistry(framework);
697
- const internals = framework;
698
- let release;
699
- const held = new Promise((resolve) => { release = resolve; });
700
- let reached;
701
- const entered = new Promise((resolve) => { reached = resolve; });
702
- const route = internals.channelRegistry.routeSpeech;
703
- internals.channelRegistry.routeSpeech = async (...args) => {
704
- const result = await route(...args);
705
- if (String(args[1]).trim() === 'first') {
706
- reached();
707
- await held;
708
- }
709
- return result;
710
- };
711
- let firstChain;
712
- let restarted;
713
- const retryStarted = new Promise((resolve) => { restarted = resolve; });
714
- let starts = 0;
715
- framework.onTrace((event) => {
716
- if (event.type === 'inference:failed')
717
- firstChain = internals.proseSpeechChains.get('assistant');
718
- if (event.type === 'inference:started') {
719
- starts++;
720
- if (starts === 2) {
721
- membrane.pushResponse(createMockResponse([{ type: 'text', text: `${p}skip_reply retry done` }]));
722
- restarted();
723
- }
724
- if (starts === 3)
725
- membrane.pushResponse(createMockResponse([{ type: 'text', text: `${p}skip_reply continuation done` }]));
726
- }
727
- });
728
- // The first stream ends in a retryable error instead of completing; the
729
- // retry's own end waits for the first stream's queued deliveries, so the
730
- // held ' !' envelope runs after the retry has started.
731
- const makeStream = membrane.streamYielding.bind(membrane);
732
- let streams = 0;
733
- membrane.streamYielding = (request, options) => {
734
- const stream = makeStream(request, options);
735
- const index = ++streams;
736
- const iterate = stream[Symbol.asyncIterator].bind(stream);
737
- stream[Symbol.asyncIterator] = async function* () {
738
- for await (const event of { [Symbol.asyncIterator]: iterate }) {
739
- if (event.type === 'complete' && index === 1) {
740
- yield { type: 'error', error: new Error('synthetic transient stream error') };
741
- return;
742
- }
743
- if (event.type === 'complete' && index === 2) {
744
- assert.ok(firstChain, 'the first stream queued a delivery chain');
745
- await firstChain;
746
- }
747
- yield event;
748
- }
749
- };
750
- return stream;
751
- };
752
- let running;
753
- try {
754
- trigger(framework);
755
- running = framework.runUntilIdle();
756
- void running.catch(() => { });
757
- await within(entered, 'the held first post');
758
- await within(retryStarted, 'the error retry to start', 10000);
759
- assert.equal(starts, 2);
760
- release();
761
- await within(running, 'the retry and continuation to finish', 10000);
762
- assert.deepEqual(routed.map((r) => r.text.trim()), ['first', 'second']);
763
- assert.equal(membrane.calls.length, 3, "the late ' !' survives the error retry and starts a continuation");
764
- }
765
- finally {
766
- release();
767
- if (running)
768
- await running;
769
- await framework.stop();
770
- }
771
- });
772
- }
773
- it("the non-streaming fallback leaves the scheduling turn's logical turn open for the re-inference", async () => {
774
- // Adapted from a reviewer's producer test: the agent's direct inference
775
- // API leaves tools pending with no yielding stream, and the tool result
776
- // goes through the framework's real event handling.
777
- const framework = await createFramework('hybrid');
778
- stubRegistry(framework);
779
- const agent = framework.getAgent('assistant');
780
- const internals = framework;
781
- const origin = internals.routingOf('assistant').logical;
782
- origin.continuation = true;
783
- origin.deliveries.push('chan-alpha');
784
- // The turn that ran the tools is open, as its fresh start left it.
785
- internals.openLogical.set('assistant', origin);
786
- const response = createMockResponse([
787
- { type: 'tool_use', id: 'unstreamed-one', name: 'robot--move', input: { dir: 'north' } },
788
- ], 'tool_use');
789
- membrane.stream = async () => response;
790
- try {
791
- agent.getContextManager().addMessage('user', [{ type: 'text', text: 'Take the direct inference path.' }]);
792
- await agent.runInference(framework.getAllTools());
793
- assert.equal(agent.state.status, 'waiting_for_tools');
794
- assert.equal(agent.state.stream, undefined, 'the direct API has no yielding stream');
795
- agent.addAssistantResponse(response.content);
796
- await internals.handleProcessEvent({
797
- type: 'tool-result', source: 'robot', moduleName: 'robot', agentName: 'assistant', callId: 'unstreamed-one',
798
- result: { success: true, data: { ok: true } },
799
- });
800
- assert.ok(internals.pendingRequests.some((r) => r.reason === 'tool_results_ready'), 'the fallback scheduled the re-inference');
801
- assert.equal(internals.openLogical.get('assistant'), origin, 'without settling the turn it continues');
802
- }
803
- finally {
804
- await framework.stop();
805
- }
806
- });
807
- it("a settle closes the open logical turn only when it is the settling stream's own, and stop() clears open turns", async () => {
808
- const framework = await createFramework('hybrid');
809
- const internals = framework;
810
- const open = { continuation: true, deliveries: [], suppressed: 0 };
811
- const stale = { continuation: false, deliveries: [], suppressed: 0 };
812
- internals.openLogical.set('assistant', open);
813
- // A settle from a stream a newer one replaced passes its own logical
814
- // turn, or none; neither closes the open one.
815
- internals.settleAgent('assistant', { stopReason: 'completed', speech: '' }, stale);
816
- internals.settleAgent('assistant', { stopReason: 'completed', speech: '' }, undefined);
817
- assert.equal(internals.openLogical.get('assistant'), open, "a stale settle leaves the newer turn open");
818
- internals.settleAgent('assistant', { stopReason: 'completed', speech: '' }, open);
819
- assert.equal(internals.openLogical.has('assistant'), false, "the turn's own settle closes it");
820
- internals.openLogical.set('assistant', open);
821
- await framework.stop();
822
- assert.equal(internals.openLogical.size, 0, 'stop() clears every open logical turn');
823
- });
824
- it('a silent start leaves an open logical turn to the next start that routes prose', async () => {
825
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'quiet' }]));
826
- const framework = await createFramework('hybrid');
827
- stubRegistry(framework);
828
- let turns = 0;
829
- framework.onTrace((e) => {
830
- if (e.type === 'inference:started')
831
- turns++;
832
- });
833
- const internals = framework;
834
- try {
835
- const open = { continuation: true, deliveries: [], suppressed: 0 };
836
- internals.openLogical.set('assistant', open);
837
- internals.pendingRequests.push({ agentName: 'assistant', reason: 'heartbeat', source: 'framework', timestamp: Date.now(), suppressProse: true });
838
- // Bounded: a silent start that took the open turn's ' !' would start a
839
- // continuation with no response queued, and this would wait for good.
840
- await within(framework.runUntilIdle(), 'the silent turn to settle', 3000);
841
- assert.equal(turns, 1);
842
- assert.notEqual(internals.turnRouting.get('assistant')?.logical, open, 'the silent start kept a logical turn of its own');
843
- assert.equal(internals.openLogical.get('assistant'), open, 'and neither took nor closed the open one');
844
- assert.equal(open.continuation, true, "the open turn's ' !' is untouched");
845
- // The next start that routes prose continues it, and its ' !' runs.
846
- // (feedTurn counts starts from here: the prose turn is 1, the
847
- // continuation 2.)
848
- membrane.pushResponse(createMockResponse([{ type: 'text', text: '>>>skip_reply' }]));
849
- feedTurn(framework, 2, [{ type: 'text', text: '>>>skip_reply' }]);
850
- trigger(framework);
851
- await framework.runUntilIdle();
852
- for (const end = Date.now() + 5000; turns < 3 && Date.now() < end;) {
853
- await new Promise((resolve) => setTimeout(resolve, 5));
854
- await framework.runUntilIdle();
855
- }
856
- assert.equal(turns, 3, "the next prose turn continued the open turn and took its ' !'");
857
- }
858
- finally {
859
- await framework.stop();
860
- }
861
- });
862
- for (const mode of ['explicit', 'hybrid']) {
863
- it(`${mode}: the wake that consumes unstreamed tool results continues the turn that scheduled them`, async () => {
864
- const p = mode === 'explicit' ? '>>' : '>>>';
865
- membrane.pushResponse(createMockResponse([{ type: 'text', text: `${p}skip_reply carrying on` }]));
866
- const framework = await createFramework(mode);
867
- stubRegistry(framework);
868
- let turns = 0;
869
- framework.onTrace((e) => {
870
- if (e.type === 'inference:started')
871
- turns++;
872
- });
873
- feedTurn(framework, 2, [{ type: 'text', text: `${p}skip_reply` }]);
874
- const internals = framework;
875
- try {
876
- // The scheduling turn asked for a continuation and left its tool
877
- // results pending; the scheduler then starts the agent with another
878
- // wake, which consumes them.
879
- const scheduling = { destination: null, hybridHold: false, logical: { continuation: true, deliveries: [], suppressed: 0 } };
880
- internals.openLogical.set('assistant', scheduling.logical);
881
- internals.turnRouting.set('assistant', { destination: null, hybridHold: false, logical: { continuation: false, deliveries: [], suppressed: 0 } });
882
- trigger(framework);
883
- await framework.runUntilIdle();
884
- for (const end = Date.now() + 5000; turns < 2 && Date.now() < end;) {
885
- await new Promise((resolve) => setTimeout(resolve, 5));
886
- await framework.runUntilIdle();
887
- }
888
- assert.equal(turns, 2, "the consuming turn took the scheduling turn's ' !'");
889
- assert.equal(scheduling.logical.continuation, false);
890
- assert.equal(internals.openLogical.has('assistant'), false, 'and the turns it ran have settled');
891
- }
892
- finally {
893
- await framework.stop();
894
- }
895
- });
896
- }
897
- for (const boundary of ['stop', 'dispose']) {
898
- it(`a tool-ended explicit turn's late ' !' takes no new turn after ${boundary === 'stop' ? 'stop()' : 'its agent is disposed'}`, async () => {
899
- membrane.pushResponse(createMockResponse([
900
- { type: 'text', text: '>>#alpha ! first' },
901
- { type: 'tool_use', id: 'p1', name: 'robot--move', input: { dir: 'north' } },
902
- ], 'tool_use'));
903
- membrane.pushResponse(createMockResponse([
904
- { type: 'tool_use', id: 'p2', name: 'skip_reply', input: {} },
905
- ], 'tool_use'));
906
- const framework = await createFramework();
907
- stubRegistry(framework);
908
- answerSkipReply(framework);
909
- const internals = framework;
910
- let release;
911
- const held = new Promise((resolve) => { release = resolve; });
912
- let entered;
913
- const reached = new Promise((resolve) => { entered = resolve; });
914
- internals.channelRegistry.routeSpeech = async () => {
915
- entered();
916
- await held;
917
- return { delivered: true, channelId: 'chan-alpha' };
918
- };
919
- try {
920
- trigger(framework);
921
- await within(framework.runUntilIdle(), 'the tool-ended turn to settle');
922
- await within(reached, 'the queued delivery to reach routing');
923
- const chain = internals.proseSpeechChains.get('assistant');
924
- const routing = internals.turnRouting.get('assistant');
925
- assert.equal(routing.logical.continuation, true, "the held delivery's ' !' is on its turn's object");
926
- assert.equal(internals.pendingRequests.length, 0);
927
- if (boundary === 'stop')
928
- await framework.stop();
929
- else
930
- internals.disposeConversationAgent('assistant');
931
- release();
932
- await within(chain, 'the queued delivery');
933
- await new Promise((resolve) => setImmediate(resolve));
934
- assert.equal(internals.pendingRequests.filter((r) => r.reason === 'prose-continuation').length, 0, 'the late check adds no request');
935
- assert.equal(routing.logical.continuation, false, 'it took the flag and dropped it');
936
- }
937
- finally {
938
- release();
939
- await framework.stop();
940
- }
941
- });
942
- }
943
- it("hybrid: a held callback's late malformed envelope holds only its supplied turn", async () => {
944
- await withHeldHybridCallback('>>>', async ({ earlier, next, routed, gateway, agent, pending, release }) => {
945
- assert.equal(earlier.hybridHold, false, 'the malformed envelope has not run before release');
946
- release();
947
- await within(pending, 'the late malformed envelope');
948
- assert.equal(earlier.hybridHold, true, 'the late hold belongs to the supplied earlier turn');
949
- assert.equal(next.hybridHold, false, 'the newer object stays unheld');
950
- await gateway.deliverHybridProse(agent, 'next', 'world:commons', true, next);
951
- assert.deepEqual(routed.map((r) => ({ text: r.text.trim(), locus: r.locus })), [
952
- { text: 'first', locus: 'chan-alpha' },
953
- { text: 'next', locus: 'world:commons' },
954
- ], 'the newer callback still publishes to its own locus');
955
- });
956
- });
957
- it("hybrid callbacks and receipts keep each turn's counts while a post is held", async () => {
958
- await withHeldHybridCallback(null, async ({ framework, earlier, next, gateway, agent, pending, release }) => {
959
- await gateway.deliverHybridProse(agent, 'quiet without a destination', 'world:commons', false, next);
960
- release();
961
- await within(pending, 'the earlier delivery confirmation');
962
- gateway.appendProseDeliveryReceipt(agent, earlier);
963
- gateway.appendProseDeliveryReceipt(agent, next);
964
- const receipts = context(framework).texts.filter((text) => text.startsWith('[delivered]'));
965
- assert.equal(receipts.length, 2, 'each supplied turn gets its own receipt');
966
- const delivered = receipts.find((text) => text.includes('chan-alpha'));
967
- const suppressed = receipts.find((text) => text.includes('suppressed'));
968
- assert.ok(delivered, "the earlier turn's delivery is acknowledged");
969
- assert.doesNotMatch(delivered, /suppressed/, "the earlier receipt excludes the newer suppression");
970
- assert.ok(suppressed, "the newer turn's suppression is acknowledged");
971
- assert.match(suppressed, /1 plain-speech segment.*suppressed/);
972
- assert.doesNotMatch(suppressed, /chan-alpha/, "the newer receipt excludes the earlier delivery");
973
- });
974
- });
975
- it("a private round's ' !' leaves no continuation request behind", async () => {
976
- const framework = await createFramework();
977
- const routed = stubRegistry(framework);
978
- const internals = framework;
979
- const agent = framework.getAgent('assistant');
980
- // When skip_reply ends an explicit turn, the speech chain isn't awaited,
981
- // so the round's delivery can run once the next turn has begun.
982
- await internals.deliverProse(agent, '>>#alpha ! kept private', { keepPrivate: true });
983
- assert.equal(routed.length, 0, 'nothing is delivered');
984
- assert.equal(internals.turnRouting.get('assistant')?.logical.continuation ?? false, false, 'and no continuation is asked for');
985
- // The same envelope outside a private round asks for one.
986
- await internals.deliverProse(agent, '>>#alpha ! sent');
987
- assert.equal(routed.length, 1);
988
- assert.equal(internals.turnRouting.get('assistant')?.logical.continuation, true);
989
- await framework.stop();
990
- });
991
- it('a skip_reply round that sets the retained text aside with >>skip_reply {{unsent}} clears it', async () => {
992
- // Turn 1 bounces its text, which retains it and wakes turn 2.
993
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'An answer with nowhere to go.' }]));
994
- const framework = await createFramework();
995
- const routed = stubRegistry(framework);
996
- const skip = answerSkipReply(framework);
997
- feedTurn(framework, 2, [
998
- { type: 'text', text: '>>skip_reply {{unsent}}' },
999
- { type: 'tool_use', id: 'sr4', name: 'skip_reply', input: {} },
1000
- ], 'tool_use');
1001
- trigger(framework);
1002
- await framework.runUntilIdle();
1003
- assert.equal(skip.calls, 1, 'the bounce wake ran the skip_reply round');
1004
- assert.equal(routed.length, 0);
1005
- assert.equal(clipboardOf(framework), undefined, 'the retained text is set aside, as >>skip_reply {{unsent}} says');
1006
- await framework.stop();
1007
- });
1008
- it('explicit mode streams nothing before a destination: unprefixed text never reaches the trigger channel', async () => {
1009
- membrane.pushResponse(createMockResponse([
1010
- { type: 'text', text: 'Unprefixed musing.\n>>#alpha streamed after the prefix' },
1011
- ]));
1012
- const framework = await createFramework();
1013
- stubRegistry(framework);
1014
- const stream = captureStream(framework);
1015
- // The unprefixed line bounces, and its wake starts a second turn.
1016
- feedTurn(framework, 2, [{ type: 'text', text: '>>skip_reply' }]);
1017
- // A turn the trigger channel woke: chan-alpha is where its typing shows.
1018
- const internals = framework;
1019
- internals.pendingRequests.push({
1020
- agentName: 'assistant', reason: 'mcpl:channel-incoming', source: 'discord', timestamp: Date.now(),
1021
- channelId: 'chan-alpha', addressed: true,
1022
- });
1023
- await internals.processInferenceRequests();
1024
- await framework.runUntilIdle();
1025
- const streamed = stream.chunks.map((c) => c.delta).join('');
1026
- assert.ok(streamed.includes('streamed after the prefix'), 'prefixed text streams to its destination');
1027
- assert.equal(streamed.includes('Unprefixed musing.'), false, 'unprefixed text is never streamed');
1028
- assert.equal(stream.completes.some((c) => c.text.includes('Unprefixed musing.')), false, 'nor finalized by the stream complete');
1029
- await framework.stop();
1030
- });
1031
- it('the stream reads leading whitespace and a token\'s end as delivery does', async () => {
1032
- const framework = await createFramework();
1033
- const routed = stubRegistry(framework);
1034
- const gateway = framework;
1035
- const agent = framework.getAgent('assistant');
1036
- const cases = [' >>#alpha hello', '\u00a0>>#alpha hello', '\u2003>>#alpha hello', '\r>>#alpha hello', '\t>>#alpha\r\nhello'];
1037
- try {
1038
- for (const text of cases) {
1039
- routed.length = 0;
1040
- await gateway.deliverProse(agent, text);
1041
- const router = new ProseStreamRouter({
1042
- mode: 'explicit', initialTarget: null, resolve: (spec) => (spec === '#alpha' ? 'chan-alpha' : null),
1043
- });
1044
- const streamed = [...router.feed(text), ...router.finish()];
1045
- assert.deepEqual(routed.map((r) => r.locus), ['chan-alpha'], `delivery sends ${JSON.stringify(text)} to #alpha`);
1046
- assert.ok(streamed.length > 0 && streamed.every((d) => d.channelId === 'chan-alpha'), `the stream sends ${JSON.stringify(text)} there too`);
1047
- }
1048
- }
1049
- finally {
1050
- await framework.stop();
1051
- }
1052
- });
1053
- it("a round's destination, decided at its boundary, is the sticky destination its delivery leaves", async () => {
1054
- const framework = await createFramework();
1055
- stubRegistry(framework);
1056
- const internals = framework;
1057
- const agent = framework.getAgent('assistant');
1058
- const rounds = [
1059
- ['>>#alpha first\n>>#alpha'],
1060
- [' >>#alpha indented'],
1061
- ['>>#nowhere bounced'],
1062
- ['>>skip_reply kept\nand unprefixed'],
1063
- ['unprefixed', '>>#alpha second segment'],
1064
- ];
1065
- try {
1066
- for (const segments of rounds) {
1067
- for (const start of [null, 'chan-alpha']) {
1068
- internals.turnRouting.set('assistant', { pin: start ?? undefined, destination: null, hybridHold: false,
1069
- logical: { continuation: false, deliveries: [], suppressed: 0 } });
1070
- for (const segment of segments)
1071
- await internals.deliverProse(agent, segment);
1072
- const admitted = segments.flatMap((segment) => internals.admitProse(segment));
1073
- assert.equal(internals.roundDestination(start, admitted), internals.turnRouting.get('assistant')?.pin ?? null, `${JSON.stringify(segments)} from ${start}`);
1074
- }
1075
- }
1076
- }
1077
- finally {
1078
- await framework.stop();
1079
- }
1080
- });
1081
- it('hybrid: a `>>>` envelope with no target streams nothing, as delivery bounces it', async () => {
1082
- const framework = await createFramework('hybrid');
1083
- const routed = stubRegistry(framework);
1084
- const gateway = framework;
1085
- const agent = framework.getAgent('assistant');
1086
- // The registry's resolver trims its argument, as the router's must here.
1087
- const resolve = (spec) => (spec.trim() === '#alpha' ? 'chan-alpha' : null);
1088
- const cases = ['>>>\r\nsecret', '>>>\nsecret', '>>>\u00a0#alpha secret', '>>> \u00a0#alpha secret'];
1089
- try {
1090
- for (const text of cases) {
1091
- routed.length = 0;
1092
- const queued = { destination: null, hybridHold: false,
1093
- logical: { continuation: false, deliveries: [], suppressed: 0 } };
1094
- const live = { ...queued, logical: { continuation: false, deliveries: [], suppressed: 0 } };
1095
- gateway.turnRouting.set('assistant', live);
1096
- await gateway.deliverHybridProse(agent, text, 'chan-locus', true, queued);
1097
- assert.equal(queued.hybridHold, true, 'the supplied turn receives the hold');
1098
- assert.equal(live.hybridHold, false, 'the live turn stays unheld');
1099
- const router = new ProseStreamRouter({ mode: 'hybrid', initialTarget: 'chan-locus', resolve });
1100
- const streamed = [...router.feed(text), ...router.finish()];
1101
- assert.equal(routed.length, 0, `delivery bounces ${JSON.stringify(text)}`);
1102
- assert.deepEqual(streamed, [], `and the stream sends none of ${JSON.stringify(text)}`);
1103
- }
1104
- }
1105
- finally {
1106
- await framework.stop();
1107
- }
1108
- });
1109
- it('hybrid: a segment that is only `>>>`, or `>>>` and a gap, holds back the next segment, as delivery does', async () => {
1110
- const framework = await createFramework('hybrid');
1111
- const routed = stubRegistry(framework);
1112
- const gateway = framework;
1113
- const agent = framework.getAgent('assistant');
1114
- const resolve = (spec) => (spec.trim() === '#alpha' ? 'chan-alpha' : null);
1115
- try {
1116
- for (const prefix of ['>>>', '>>> ', '>>>\t']) {
1117
- routed.length = 0;
1118
- const queued = { destination: null, hybridHold: false,
1119
- logical: { continuation: false, deliveries: [], suppressed: 0 } };
1120
- const live = { ...queued, logical: { continuation: false, deliveries: [], suppressed: 0 } };
1121
- gateway.turnRouting.set('assistant', live);
1122
- await gateway.deliverHybridProse(agent, '>>>#alpha first', 'chan-locus', true, queued);
1123
- await gateway.deliverHybridProse(agent, prefix, 'chan-locus', true, queued);
1124
- await gateway.deliverHybridProse(agent, 'secret', 'chan-locus', true, queued);
1125
- assert.deepEqual(queued.logical.deliveries, ['chan-alpha'], 'the supplied turn records its delivery');
1126
- assert.equal(queued.logical.suppressed, 1, 'the supplied turn records its suppression');
1127
- assert.deepEqual(live.logical.deliveries, [], 'the live turn receives no delivery');
1128
- assert.equal(live.logical.suppressed, 0, 'the live turn receives no suppression');
1129
- const router = new ProseStreamRouter({ mode: 'hybrid', initialTarget: 'chan-locus', resolve });
1130
- const streamed = [
1131
- ...router.feed('>>>#alpha first'), ...router.endSegment(),
1132
- ...router.feed(prefix), ...router.endSegment(),
1133
- ...router.feed('secret'), ...router.finish(),
1134
- ];
1135
- assert.deepEqual(routed.map((r) => r.text), ['first'], `delivery sends only first, after ${JSON.stringify(prefix)}`);
1136
- assert.equal(streamed.filter((d) => d.delta.includes('secret')).length, 0, `and the stream holds secret back after ${JSON.stringify(prefix)}`);
1137
- }
1138
- }
1139
- finally {
1140
- await framework.stop();
1141
- }
1142
- });
1143
- it('admission turns a resolver that throws into an envelope that bounces', async () => {
1144
- const framework = await createFramework();
1145
- const routed = stubRegistry(framework);
1146
- const internals = framework;
1147
- const resolve = internals.channelRegistry.resolveProseTarget.bind(internals.channelRegistry);
1148
- internals.channelRegistry.resolveProseTarget = (spec) => {
1149
- if (spec === '#boom')
1150
- throw new Error('directory unavailable');
1151
- return resolve(spec);
1152
- };
1153
- try {
1154
- const admitted = internals.admitProse('>>#boom one\n>>#alpha two');
1155
- assert.match(admitted[0].resolved?.error ?? '', /directory unavailable/);
1156
- await internals.deliverAdmittedProse(framework.getAgent('assistant'), admitted, { routing: internals.routingOf('assistant') });
1157
- assert.deepEqual(routed.map((r) => r.text), ['two'], 'the other envelope still goes out');
1158
- }
1159
- finally {
1160
- await framework.stop();
1161
- }
1162
- });
1163
- it("' !' continuation starts another turn immediately after prose ends the current one", async () => {
1164
- membrane.pushResponse(createMockResponse([
1165
- { type: 'text', text: '>>#alpha ! step one done, continuing' },
1166
- ]));
1167
- const framework = await createFramework();
1168
- const routed = stubRegistry(framework);
1169
- feedTurn(framework, 2, [{ type: 'text', text: '>>#alpha step two done' }]);
1170
- trigger(framework);
1171
- await framework.runUntilIdle();
1172
- assert.equal(membrane.calls.length, 2, 'a second inference ran without any external event');
1173
- assert.deepEqual(routed.map((r) => r.text), ['step one done, continuing', 'step two done']);
1174
- assert.deepEqual(routed.map((r) => r.locus), ['chan-alpha', 'chan-alpha']);
1175
- await framework.stop();
1176
- });
1177
- it('appends the mode primer exactly once across turns', async () => {
1178
- membrane.pushResponse(createMockResponse([
1179
- { type: 'text', text: '>>#alpha hello' },
1180
- ]));
1181
- const framework = await createFramework();
1182
- stubRegistry(framework);
1183
- const primers = [];
1184
- framework.onTrace((e) => {
1185
- const ev = e;
1186
- if (ev.type === 'message:added' && ev.source === 'prose-routing-primer')
1187
- primers.push(1);
1188
- });
1189
- trigger(framework);
1190
- await framework.runUntilIdle();
1191
- assert.equal(primers.length, 1, 'primer on first explicit turn');
1192
- membrane.pushResponse(createMockResponse([
1193
- { type: 'text', text: '>>#alpha again' },
1194
- ]));
1195
- trigger(framework);
1196
- await framework.runUntilIdle();
1197
- assert.equal(primers.length, 1, 'no re-priming on later turns');
1198
- await framework.stop();
1199
- });
1200
- it('prose_help tool returns the routing reference on demand', async () => {
1201
- membrane.pushResponse(createMockResponse([
1202
- { type: 'tool_use', id: 'c1', name: 'prose_help', input: {} },
1203
- ], 'tool_use'));
1204
- membrane.pushResponse(createMockResponse([
1205
- { type: 'text', text: '>>#alpha got it' },
1206
- ]));
1207
- const framework = await createFramework();
1208
- const routed = stubRegistry(framework);
1209
- trigger(framework);
1210
- await framework.runUntilIdle();
1211
- const stream = membrane.lastStream;
1212
- assert.equal(stream.receivedToolResults.length, 1);
1213
- const result = JSON.stringify(stream.receivedToolResults[0]);
1214
- assert.ok(result.includes('destination'), 'help text served as tool result');
1215
- assert.deepEqual(routed, [{ text: 'got it', locus: 'chan-alpha' }]);
1216
- await framework.stop();
1217
- });
1218
- it('multi-envelope prose: each >> line routes independently (Tilde scenario)', async () => {
1219
- membrane.pushResponse(createMockResponse([
1220
- { type: 'text', text: '>>#alpha part one for the channel\n>>@laria part two, just for you\nwith a second line\n>> quoted arrow stays in body' },
1221
- ]));
1222
- const framework = await createFramework();
1223
- const routed = stubRegistry(framework);
1224
- trigger(framework);
1225
- await framework.runUntilIdle();
1226
- assert.deepEqual(routed, [
1227
- { text: 'part one for the channel', locus: 'chan-alpha' },
1228
- { text: 'part two, just for you\nwith a second line\n>> quoted arrow stays in body', locus: 'discord:dm:99' },
1229
- ], 'two envelopes, two destinations; quoted arrow not split');
1230
- await framework.stop();
1231
- });
1232
- it('hybrid preserves authored >>> envelope, publishes only body cross-surface, and records canonical receipt', async () => {
1233
- const authored = '>>>#cafe\nA message meant for the café.';
1234
- membrane.pushResponse(createMockResponse([{ type: 'text', text: authored }]));
1235
- const framework = await createFramework('hybrid');
1236
- const routed = stubRegistry(framework);
1237
- trigger(framework);
1238
- await framework.runUntilIdle();
1239
- assert.deepEqual(routed, [{ text: 'A message meant for the café.', locus: 'discord:g:cafe' }]);
1240
- const cm = framework.getAgent('assistant').getContextManager();
1241
- const texts = cm.getAllMessages().flatMap(m => m.content)
1242
- .filter(b => b.type === 'text').map(b => b.text);
1243
- assert.ok(texts.includes(authored), 'source/author view retains exact envelope');
1244
- assert.ok(texts.includes('[delivered] plain speech → discord:g:cafe'), 'canonical destination receipt enters context');
1245
- await framework.stop();
1246
- });
1247
- it('hybrid leaves unprefixed prose on the frozen locus', async () => {
1248
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'Ordinary field speech.' }]));
1249
- const framework = await createFramework('hybrid');
1250
- const routed = stubRegistry(framework);
1251
- trigger(framework);
1252
- await framework.runUntilIdle();
1253
- assert.deepEqual(routed, [{ text: 'Ordinary field speech.', locus: 'world:commons' }]);
1254
- await framework.stop();
1255
- });
1256
- it('hybrid accepts permissive whitespace but missing targets bounce and publish nowhere', async () => {
1257
- membrane.pushResponse(createMockResponse([{ type: 'text', text: ' >>> #missing\nDo not guess.' }]));
1258
- const framework = await createFramework('hybrid');
1259
- const routed = stubRegistry(framework);
1260
- feedTurn(framework, 2, [{ type: 'text', text: '>>>skip_reply {{unsent}}' }]);
1261
- trigger(framework);
1262
- await framework.runUntilIdle();
1263
- assert.deepEqual(routed, []);
1264
- const cm = framework.getAgent('assistant').getContextManager();
1265
- const texts = cm.getAllMessages().flatMap(m => m.content)
1266
- .filter(b => b.type === 'text').map(b => b.text);
1267
- assert.ok(texts.some(t => t.includes('[prose-routing]') && t.includes('not delivered') && t.includes('>>>#channel {{unsent}}')));
1268
- await framework.stop();
1269
- });
1270
- it('hybrid keeps an explicit target sticky across tool-round segments in the same turn', async () => {
1271
- membrane.pushResponse(createMockResponse([
1272
- { type: 'text', text: '>>>#cafe first segment' },
1273
- { type: 'tool_use', id: 'hc1', name: 'robot--move', input: { dir: 'north' } },
1274
- ], 'tool_use'));
1275
- membrane.pushResponse(createMockResponse([
1276
- { type: 'text', text: 'second segment, no repeated envelope' },
1277
- ]));
1278
- const framework = await createFramework('hybrid');
1279
- const routed = stubRegistry(framework);
1280
- trigger(framework);
1281
- await framework.runUntilIdle();
1282
- assert.deepEqual(routed, [
1283
- { text: 'first segment', locus: 'discord:g:cafe' },
1284
- { text: 'second segment, no repeated envelope', locus: 'discord:g:cafe' },
1285
- ]);
1286
- await framework.stop();
1287
- });
1288
- it('hybrid target-only envelope establishes the sticky destination for the next tool-round segment', async () => {
1289
- membrane.pushResponse(createMockResponse([
1290
- { type: 'text', text: '>>>#cafe' },
1291
- { type: 'tool_use', id: 'hc2', name: 'robot--move', input: { dir: 'north' } },
1292
- ], 'tool_use'));
1293
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'body follows' }]));
1294
- const framework = await createFramework('hybrid');
1295
- const routed = stubRegistry(framework);
1296
- trigger(framework);
1297
- await framework.runUntilIdle();
1298
- assert.deepEqual(routed, [{ text: 'body follows', locus: 'discord:g:cafe' }]);
1299
- await framework.stop();
1300
- });
1301
- it('hybrid >>>skip_reply clears a sticky target until a fresh valid envelope', async () => {
1302
- membrane.pushResponse(createMockResponse([{ type: 'text', text: '>>>#cafe first' }, { type: 'tool_use', id: 'sk1', name: 'robot--move', input: { dir: 'north' } }], 'tool_use'));
1303
- membrane.pushResponse(createMockResponse([{ type: 'text', text: '>>>skip_reply hidden' }, { type: 'tool_use', id: 'sk2', name: 'robot--move', input: { dir: 'north' } }], 'tool_use'));
1304
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'plain text must remain suppressed' }, { type: 'tool_use', id: 'sk3', name: 'robot--move', input: { dir: 'north' } }], 'tool_use'));
1305
- membrane.pushResponse(createMockResponse([{ type: 'text', text: '>>>world:commons recovered' }]));
1306
- const framework = await createFramework('hybrid');
1307
- const routed = stubRegistry(framework);
1308
- trigger(framework);
1309
- await framework.runUntilIdle();
1310
- assert.deepEqual(routed, [{ text: 'first', locus: 'discord:g:cafe' }, { text: 'recovered', locus: 'world:commons' }]);
1311
- await framework.stop();
1312
- });
1313
- for (const failure of ['false', 'throw']) {
1314
- it(`hybrid ${failure} delivery clears sticky authority, suppresses later prose, and a fresh target resumes`, async () => {
1315
- membrane.pushResponse(createMockResponse([{ type: 'text', text: '>>>#cafe first' }, { type: 'tool_use', id: 'fl1', name: 'robot--move', input: { dir: 'north' } }], 'tool_use'));
1316
- membrane.pushResponse(createMockResponse([{ type: 'text', text: '>>>#alpha fails' }, { type: 'tool_use', id: 'fl2', name: 'robot--move', input: { dir: 'north' } }], 'tool_use'));
1317
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'plain text must remain suppressed' }, { type: 'tool_use', id: 'fl3', name: 'robot--move', input: { dir: 'north' } }], 'tool_use'));
1318
- membrane.pushResponse(createMockResponse([{ type: 'text', text: '>>>world:commons recovered' }]));
1319
- const framework = await createFramework('hybrid');
1320
- const routed = stubRegistry(framework, ['delivered', failure, 'delivered']);
1321
- feedTurn(framework, 2, [{ type: 'text', text: '>>>skip_reply {{unsent}}' }]);
1322
- trigger(framework);
1323
- await framework.runUntilIdle();
1324
- assert.equal(routed.some(r => r.text.includes('plain text must remain suppressed')), false);
1325
- assert.deepEqual(routed.filter(r => r.text !== 'fails'), [{ text: 'first', locus: 'discord:g:cafe' }, { text: 'recovered', locus: 'world:commons' }]);
1326
- const cm = framework.getAgent('assistant').getContextManager();
1327
- const texts = cm.getAllMessages().flatMap(m => m.content).filter(b => b.type === 'text').map(b => b.text);
1328
- assert.ok(texts.some(t => t.includes('[prose-routing]') && t.includes('>>>#channel {{unsent}}')));
1329
- await framework.stop();
1330
- });
1331
- }
1332
- it('hybrid same-round successful say tool outranks a contradictory prose envelope', async () => {
1333
- const authored = '>>>#cafe wrong-locus duplicate';
1334
- membrane.pushResponse(createMockResponse([
1335
- { type: 'text', text: authored },
1336
- { type: 'tool_use', id: 'say1', name: 'robot--say', input: { text: 'Hello Sill in the world' } },
1337
- ], 'tool_use'));
1338
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'tool completed' }]));
1339
- const framework = await createFramework('hybrid');
1340
- const routed = stubRegistry(framework);
1341
- trigger(framework);
1342
- await framework.runUntilIdle();
1343
- assert.equal(module.calls.filter(c => c.name === 'say' || c.name.endsWith('--say')).length, 1);
1344
- assert.deepEqual(routed, []);
1345
- const cm = framework.getAgent('assistant').getContextManager();
1346
- const texts = cm.getAllMessages().flatMap(m => m.content).filter(b => b.type === 'text').map(b => b.text);
1347
- assert.ok(texts.includes(authored));
1348
- assert.ok(texts.some(t => t.startsWith('[delivered] nothing') && t.includes('suppressed')));
1349
- await framework.stop();
1350
- });
1351
- it('disabled mode suppresses text-only prose without a bounce, primer, or channel delivery', async () => {
1352
- const authored = 'Internal continuity summary that must never publish.';
1353
- membrane.pushResponse(createMockResponse([{ type: 'text', text: authored }]));
1354
- const framework = await createFramework('disabled');
1355
- const routed = stubRegistry(framework);
1356
- const sources = [];
1357
- framework.onTrace((e) => {
1358
- const ev = e;
1359
- if (ev.type === 'message:added' && ev.source)
1360
- sources.push(ev.source);
1361
- });
1362
- trigger(framework);
1363
- await framework.runUntilIdle();
1364
- assert.deepEqual(routed, [], 'disabled prose never reaches a channel');
1365
- assert.equal(sources.includes('prose-bounce'), false, 'disabled prose does not bounce');
1366
- assert.equal(sources.includes('prose-routing-primer'), false, 'disabled mode injects no model-visible primer');
1367
- const texts = framework.getAgent('assistant').getContextManager().getAllMessages()
1368
- .flatMap(m => m.content).filter(b => b.type === 'text').map(b => b.text);
1369
- assert.ok(texts.includes(authored), 'authored prose remains in Chronicle');
1370
- assert.ok(texts.some(t => t.includes('[delivered] nothing') && t.includes('proseRouting=disabled')), 'private suppression receipt explains the unsent prose');
1371
- await framework.stop();
1372
- });
1373
- it('disabled mode ignores destination prefixes but explicit publish tools still execute', async () => {
1374
- membrane.pushResponse(createMockResponse([
1375
- { type: 'text', text: '>>#alpha this prefix must not bypass disabled mode' },
1376
- { type: 'tool_use', id: 'd1', name: 'robot--say', input: { text: 'published explicitly' } },
1377
- ], 'tool_use'));
1378
- membrane.pushResponse(createMockResponse([
1379
- { type: 'text', text: 'trailing prose must also remain private' },
1380
- ]));
1381
- const framework = await createFramework('disabled');
1382
- const routed = stubRegistry(framework);
1383
- trigger(framework);
1384
- await framework.runUntilIdle();
1385
- assert.deepEqual(routed, [], 'neither prefixed nor trailing prose routed');
1386
- assert.equal(module.calls.length, 1, 'explicit tool call executed');
1387
- assert.equal(module.calls[0].name, 'say');
1388
- const texts = framework.getAgent('assistant').getContextManager().getAllMessages()
1389
- .flatMap(m => m.content).filter(b => b.type === 'text').map(b => b.text);
1390
- assert.ok(texts.some(t => t.includes('[delivered] nothing') && t.includes('2 plain-speech segment(s) suppressed')), 'one receipt accounts for mid-turn and trailing prose');
1391
- await framework.stop();
1392
- });
1393
- });
1394
- //# sourceMappingURL=explicit-prose-routing.test.js.map