@animalabs/agent-framework 0.23.2 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/README.md +212 -42
  2. package/dist/src/agent.d.ts +60 -9
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +92 -12
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/server.d.ts.map +1 -1
  7. package/dist/src/api/server.js +14 -12
  8. package/dist/src/api/server.js.map +1 -1
  9. package/dist/src/code-execution/engine.d.ts +177 -0
  10. package/dist/src/code-execution/engine.d.ts.map +1 -0
  11. package/dist/src/code-execution/engine.js +494 -0
  12. package/dist/src/code-execution/engine.js.map +1 -0
  13. package/dist/src/code-execution/index.d.ts +5 -10
  14. package/dist/src/code-execution/index.d.ts.map +1 -1
  15. package/dist/src/code-execution/index.js +5 -9
  16. package/dist/src/code-execution/index.js.map +1 -1
  17. package/dist/src/code-execution/isolate/calls.d.ts +114 -0
  18. package/dist/src/code-execution/isolate/calls.d.ts.map +1 -0
  19. package/dist/src/code-execution/isolate/calls.js +145 -0
  20. package/dist/src/code-execution/isolate/calls.js.map +1 -0
  21. package/dist/src/code-execution/isolate/child-entry.d.ts +2 -0
  22. package/dist/src/code-execution/isolate/child-entry.d.ts.map +1 -0
  23. package/dist/src/code-execution/isolate/child-entry.js +5 -0
  24. package/dist/src/code-execution/isolate/child-entry.js.map +1 -0
  25. package/dist/src/code-execution/isolate/child-main.d.ts +26 -0
  26. package/dist/src/code-execution/isolate/child-main.d.ts.map +1 -0
  27. package/dist/src/code-execution/isolate/child-main.js +132 -0
  28. package/dist/src/code-execution/isolate/child-main.js.map +1 -0
  29. package/dist/src/code-execution/isolate/child.d.ts +44 -0
  30. package/dist/src/code-execution/isolate/child.d.ts.map +1 -0
  31. package/dist/src/code-execution/isolate/child.js +315 -0
  32. package/dist/src/code-execution/isolate/child.js.map +1 -0
  33. package/dist/src/code-execution/isolate/frames.d.ts +80 -0
  34. package/dist/src/code-execution/isolate/frames.d.ts.map +1 -0
  35. package/dist/src/code-execution/isolate/frames.js +210 -0
  36. package/dist/src/code-execution/isolate/frames.js.map +1 -0
  37. package/dist/src/code-execution/isolate/output.d.ts +68 -0
  38. package/dist/src/code-execution/isolate/output.d.ts.map +1 -0
  39. package/dist/src/code-execution/isolate/output.js +143 -0
  40. package/dist/src/code-execution/isolate/output.js.map +1 -0
  41. package/dist/src/code-execution/isolate/prelude.d.ts +109 -0
  42. package/dist/src/code-execution/isolate/prelude.d.ts.map +1 -0
  43. package/dist/src/code-execution/isolate/prelude.js +401 -0
  44. package/dist/src/code-execution/isolate/prelude.js.map +1 -0
  45. package/dist/src/code-execution/isolate/protocol.d.ts +151 -0
  46. package/dist/src/code-execution/isolate/protocol.d.ts.map +1 -0
  47. package/dist/src/code-execution/isolate/protocol.js +150 -0
  48. package/dist/src/code-execution/isolate/protocol.js.map +1 -0
  49. package/dist/src/code-execution/isolate/render.d.ts +57 -0
  50. package/dist/src/code-execution/isolate/render.d.ts.map +1 -0
  51. package/dist/src/code-execution/isolate/render.js +214 -0
  52. package/dist/src/code-execution/isolate/render.js.map +1 -0
  53. package/dist/src/code-execution/isolate/runner.d.ts +250 -0
  54. package/dist/src/code-execution/isolate/runner.d.ts.map +1 -0
  55. package/dist/src/code-execution/isolate/runner.js +854 -0
  56. package/dist/src/code-execution/isolate/runner.js.map +1 -0
  57. package/dist/src/code-execution/isolate/scripts.d.ts +175 -0
  58. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -0
  59. package/dist/src/code-execution/isolate/scripts.js +357 -0
  60. package/dist/src/code-execution/isolate/scripts.js.map +1 -0
  61. package/dist/src/code-execution/quickjs.wasm +0 -0
  62. package/dist/src/code-execution/tool-definition.d.ts +93 -43
  63. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  64. package/dist/src/code-execution/tool-definition.js +176 -142
  65. package/dist/src/code-execution/tool-definition.js.map +1 -1
  66. package/dist/src/deadline-timer.d.ts +3 -0
  67. package/dist/src/deadline-timer.d.ts.map +1 -1
  68. package/dist/src/deadline-timer.js +1 -1
  69. package/dist/src/deadline-timer.js.map +1 -1
  70. package/dist/src/framework.d.ts +1027 -440
  71. package/dist/src/framework.d.ts.map +1 -1
  72. package/dist/src/framework.js +4093 -2238
  73. package/dist/src/framework.js.map +1 -1
  74. package/dist/src/gate/event-gate.d.ts +28 -1
  75. package/dist/src/gate/event-gate.d.ts.map +1 -1
  76. package/dist/src/gate/event-gate.js +88 -10
  77. package/dist/src/gate/event-gate.js.map +1 -1
  78. package/dist/src/held-writes.d.ts +16 -3
  79. package/dist/src/held-writes.d.ts.map +1 -1
  80. package/dist/src/held-writes.js +16 -3
  81. package/dist/src/held-writes.js.map +1 -1
  82. package/dist/src/index.d.ts +6 -6
  83. package/dist/src/index.d.ts.map +1 -1
  84. package/dist/src/index.js +9 -8
  85. package/dist/src/index.js.map +1 -1
  86. package/dist/src/mcpl/channel-registry.d.ts +471 -75
  87. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  88. package/dist/src/mcpl/channel-registry.js +1673 -337
  89. package/dist/src/mcpl/channel-registry.js.map +1 -1
  90. package/dist/src/mcpl/conversation-router.d.ts +12 -7
  91. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  92. package/dist/src/mcpl/conversation-router.js +12 -7
  93. package/dist/src/mcpl/conversation-router.js.map +1 -1
  94. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  95. package/dist/src/mcpl/convert-block.js +9 -5
  96. package/dist/src/mcpl/convert-block.js.map +1 -1
  97. package/dist/src/mcpl/inbound-source.d.ts +50 -0
  98. package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
  99. package/dist/src/mcpl/inbound-source.js +130 -0
  100. package/dist/src/mcpl/inbound-source.js.map +1 -1
  101. package/dist/src/mcpl/index.d.ts +2 -1
  102. package/dist/src/mcpl/index.d.ts.map +1 -1
  103. package/dist/src/mcpl/index.js +3 -2
  104. package/dist/src/mcpl/index.js.map +1 -1
  105. package/dist/src/mcpl/log-value.d.ts +6 -0
  106. package/dist/src/mcpl/log-value.d.ts.map +1 -1
  107. package/dist/src/mcpl/log-value.js +8 -0
  108. package/dist/src/mcpl/log-value.js.map +1 -1
  109. package/dist/src/mcpl/modern-connection.d.ts +15 -6
  110. package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
  111. package/dist/src/mcpl/modern-connection.js +168 -53
  112. package/dist/src/mcpl/modern-connection.js.map +1 -1
  113. package/dist/src/mcpl/prose-grammar.d.ts +4 -5
  114. package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
  115. package/dist/src/mcpl/prose-grammar.js +4 -5
  116. package/dist/src/mcpl/prose-grammar.js.map +1 -1
  117. package/dist/src/mcpl/protocol-family.d.ts +97 -10
  118. package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
  119. package/dist/src/mcpl/protocol-family.js +173 -16
  120. package/dist/src/mcpl/protocol-family.js.map +1 -1
  121. package/dist/src/mcpl/push-coalescer.d.ts +132 -14
  122. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  123. package/dist/src/mcpl/push-coalescer.js +227 -50
  124. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  125. package/dist/src/mcpl/push-handler.d.ts +6 -1
  126. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  127. package/dist/src/mcpl/push-handler.js +4 -1
  128. package/dist/src/mcpl/push-handler.js.map +1 -1
  129. package/dist/src/mcpl/reference-fetcher.d.ts +2 -2
  130. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  131. package/dist/src/mcpl/reference-fetcher.js +4 -4
  132. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  133. package/dist/src/mcpl/server-connection.d.ts +50 -21
  134. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  135. package/dist/src/mcpl/server-connection.js +158 -74
  136. package/dist/src/mcpl/server-connection.js.map +1 -1
  137. package/dist/src/mcpl/tags.d.ts +24 -0
  138. package/dist/src/mcpl/tags.d.ts.map +1 -1
  139. package/dist/src/mcpl/tags.js +33 -0
  140. package/dist/src/mcpl/tags.js.map +1 -1
  141. package/dist/src/mcpl/tool-result-normalize.d.ts +2 -1
  142. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
  143. package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
  144. package/dist/src/mcpl/transport.d.ts +55 -7
  145. package/dist/src/mcpl/transport.d.ts.map +1 -1
  146. package/dist/src/mcpl/transport.js +163 -34
  147. package/dist/src/mcpl/transport.js.map +1 -1
  148. package/dist/src/mcpl/types.d.ts +22 -6
  149. package/dist/src/mcpl/types.d.ts.map +1 -1
  150. package/dist/src/mcpl/types.js.map +1 -1
  151. package/dist/src/module-registry.d.ts +4 -2
  152. package/dist/src/module-registry.d.ts.map +1 -1
  153. package/dist/src/module-registry.js +100 -13
  154. package/dist/src/module-registry.js.map +1 -1
  155. package/dist/src/modules/discord/index.d.ts.map +1 -1
  156. package/dist/src/modules/discord/index.js +8 -13
  157. package/dist/src/modules/discord/index.js.map +1 -1
  158. package/dist/src/modules/history/index.d.ts.map +1 -1
  159. package/dist/src/modules/history/index.js +27 -4
  160. package/dist/src/modules/history/index.js.map +1 -1
  161. package/dist/src/modules/history/semantic.d.ts +15 -3
  162. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  163. package/dist/src/modules/history/semantic.js +27 -5
  164. package/dist/src/modules/history/semantic.js.map +1 -1
  165. package/dist/src/modules/index.d.ts +1 -1
  166. package/dist/src/modules/index.d.ts.map +1 -1
  167. package/dist/src/modules/index.js.map +1 -1
  168. package/dist/src/modules/unknown-tool.d.ts +4 -1
  169. package/dist/src/modules/unknown-tool.d.ts.map +1 -1
  170. package/dist/src/modules/unknown-tool.js +4 -1
  171. package/dist/src/modules/unknown-tool.js.map +1 -1
  172. package/dist/src/modules/workspace/branch-intent.d.ts +3 -1
  173. package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
  174. package/dist/src/modules/workspace/branch-intent.js +3 -1
  175. package/dist/src/modules/workspace/branch-intent.js.map +1 -1
  176. package/dist/src/modules/workspace/edit-plan.d.ts +265 -0
  177. package/dist/src/modules/workspace/edit-plan.d.ts.map +1 -0
  178. package/dist/src/modules/workspace/edit-plan.js +794 -0
  179. package/dist/src/modules/workspace/edit-plan.js.map +1 -0
  180. package/dist/src/modules/workspace/edit-text.d.ts +117 -0
  181. package/dist/src/modules/workspace/edit-text.d.ts.map +1 -0
  182. package/dist/src/modules/workspace/edit-text.js +584 -0
  183. package/dist/src/modules/workspace/edit-text.js.map +1 -0
  184. package/dist/src/modules/workspace/effects.d.ts +5 -4
  185. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  186. package/dist/src/modules/workspace/effects.js +78 -25
  187. package/dist/src/modules/workspace/effects.js.map +1 -1
  188. package/dist/src/modules/workspace/index.d.ts +484 -61
  189. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  190. package/dist/src/modules/workspace/index.js +2173 -645
  191. package/dist/src/modules/workspace/index.js.map +1 -1
  192. package/dist/src/modules/workspace/observe.d.ts +213 -38
  193. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  194. package/dist/src/modules/workspace/observe.js +416 -36
  195. package/dist/src/modules/workspace/observe.js.map +1 -1
  196. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  197. package/dist/src/modules/workspace/reconcile.js +15 -8
  198. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  199. package/dist/src/modules/workspace/text.d.ts +32 -0
  200. package/dist/src/modules/workspace/text.d.ts.map +1 -0
  201. package/dist/src/modules/workspace/text.js +67 -0
  202. package/dist/src/modules/workspace/text.js.map +1 -0
  203. package/dist/src/modules/workspace/types.d.ts +210 -6
  204. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  205. package/dist/src/modules/workspace/types.js.map +1 -1
  206. package/dist/src/modules/workspace/watcher.d.ts +3 -2
  207. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  208. package/dist/src/modules/workspace/watcher.js.map +1 -1
  209. package/dist/src/operator-log.d.ts +52 -24
  210. package/dist/src/operator-log.d.ts.map +1 -1
  211. package/dist/src/operator-log.js.map +1 -1
  212. package/dist/src/recovery/discord-awareness-outbox.d.ts +496 -72
  213. package/dist/src/recovery/discord-awareness-outbox.d.ts.map +1 -1
  214. package/dist/src/recovery/discord-awareness-outbox.js +1196 -289
  215. package/dist/src/recovery/discord-awareness-outbox.js.map +1 -1
  216. package/dist/src/recovery/offline-branch.d.ts +26 -1
  217. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  218. package/dist/src/recovery/offline-branch.js +164 -39
  219. package/dist/src/recovery/offline-branch.js.map +1 -1
  220. package/dist/src/recovery/recover-cli.js +23 -4
  221. package/dist/src/recovery/recover-cli.js.map +1 -1
  222. package/dist/src/send-tools.d.ts +11 -0
  223. package/dist/src/send-tools.d.ts.map +1 -0
  224. package/dist/src/send-tools.js +11 -0
  225. package/dist/src/send-tools.js.map +1 -0
  226. package/dist/src/tool-result-guard.d.ts +4 -3
  227. package/dist/src/tool-result-guard.d.ts.map +1 -1
  228. package/dist/src/tool-result-guard.js +5 -4
  229. package/dist/src/tool-result-guard.js.map +1 -1
  230. package/dist/src/tool-result-history.d.ts +1 -1
  231. package/dist/src/tool-result-history.js +1 -1
  232. package/dist/src/tune-out/coordinator.d.ts +21 -0
  233. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  234. package/dist/src/tune-out/coordinator.js +139 -37
  235. package/dist/src/tune-out/coordinator.js.map +1 -1
  236. package/dist/src/types/agent.d.ts +4 -0
  237. package/dist/src/types/agent.d.ts.map +1 -1
  238. package/dist/src/types/events.d.ts +62 -9
  239. package/dist/src/types/events.d.ts.map +1 -1
  240. package/dist/src/types/framework.d.ts +62 -58
  241. package/dist/src/types/framework.d.ts.map +1 -1
  242. package/dist/src/types/module.d.ts +40 -8
  243. package/dist/src/types/module.d.ts.map +1 -1
  244. package/dist/src/types/trace.d.ts +111 -10
  245. package/dist/src/types/trace.d.ts.map +1 -1
  246. package/dist/src/usage/types.d.ts +16 -0
  247. package/dist/src/usage/types.d.ts.map +1 -1
  248. package/dist/src/usage/usage-tracker.d.ts +14 -1
  249. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  250. package/dist/src/usage/usage-tracker.js +81 -41
  251. package/dist/src/usage/usage-tracker.js.map +1 -1
  252. package/package.json +6 -5
  253. package/src/agent.ts +93 -12
  254. package/src/api/server.ts +14 -12
  255. package/src/code-execution/engine.ts +595 -0
  256. package/src/code-execution/index.ts +5 -17
  257. package/src/code-execution/isolate/calls.ts +199 -0
  258. package/src/code-execution/isolate/child-entry.ts +5 -0
  259. package/src/code-execution/isolate/child-main.ts +132 -0
  260. package/src/code-execution/isolate/child.ts +327 -0
  261. package/src/code-execution/isolate/frames.ts +209 -0
  262. package/src/code-execution/isolate/output.ts +161 -0
  263. package/src/code-execution/isolate/prelude.ts +409 -0
  264. package/src/code-execution/isolate/protocol.ts +240 -0
  265. package/src/code-execution/isolate/render.ts +230 -0
  266. package/src/code-execution/isolate/runner.ts +911 -0
  267. package/src/code-execution/isolate/scripts.ts +479 -0
  268. package/src/code-execution/quickjs.wasm +0 -0
  269. package/src/code-execution/quickjs.wasm.LICENSES +149 -0
  270. package/src/code-execution/tool-definition.ts +236 -167
  271. package/src/deadline-timer.ts +1 -1
  272. package/src/framework.ts +4468 -2418
  273. package/src/gate/event-gate.ts +78 -11
  274. package/src/held-writes.ts +17 -3
  275. package/src/index.ts +30 -22
  276. package/src/mcpl/channel-registry.ts +1825 -368
  277. package/src/mcpl/conversation-router.ts +12 -7
  278. package/src/mcpl/convert-block.ts +9 -5
  279. package/src/mcpl/inbound-source.ts +131 -0
  280. package/src/mcpl/index.ts +4 -2
  281. package/src/mcpl/log-value.ts +9 -0
  282. package/src/mcpl/modern-connection.ts +162 -53
  283. package/src/mcpl/prose-grammar.ts +4 -5
  284. package/src/mcpl/protocol-family.ts +199 -21
  285. package/src/mcpl/push-coalescer.ts +306 -62
  286. package/src/mcpl/push-handler.ts +10 -2
  287. package/src/mcpl/reference-fetcher.ts +4 -4
  288. package/src/mcpl/server-connection.ts +180 -79
  289. package/src/mcpl/tags.ts +36 -0
  290. package/src/mcpl/tool-result-normalize.ts +2 -1
  291. package/src/mcpl/transport.ts +162 -33
  292. package/src/mcpl/types.ts +23 -6
  293. package/src/module-registry.ts +92 -13
  294. package/src/modules/discord/index.ts +8 -13
  295. package/src/modules/history/index.ts +25 -3
  296. package/src/modules/history/semantic.ts +29 -6
  297. package/src/modules/index.ts +1 -0
  298. package/src/modules/unknown-tool.ts +4 -1
  299. package/src/modules/workspace/branch-intent.ts +3 -1
  300. package/src/modules/workspace/edit-plan.ts +954 -0
  301. package/src/modules/workspace/edit-text.ts +618 -0
  302. package/src/modules/workspace/effects.ts +70 -19
  303. package/src/modules/workspace/index.ts +2247 -642
  304. package/src/modules/workspace/observe.ts +465 -56
  305. package/src/modules/workspace/reconcile.ts +14 -7
  306. package/src/modules/workspace/text.ts +68 -0
  307. package/src/modules/workspace/types.ts +211 -6
  308. package/src/modules/workspace/watcher.ts +3 -2
  309. package/src/operator-log.ts +47 -19
  310. package/src/recovery/discord-awareness-outbox.ts +1500 -356
  311. package/src/recovery/offline-branch.ts +189 -55
  312. package/src/recovery/recover-cli.ts +25 -4
  313. package/src/send-tools.ts +11 -0
  314. package/src/tool-result-guard.ts +6 -5
  315. package/src/tool-result-history.ts +1 -1
  316. package/src/tune-out/coordinator.ts +163 -42
  317. package/src/types/agent.ts +4 -0
  318. package/src/types/events.ts +62 -9
  319. package/src/types/framework.ts +62 -58
  320. package/src/types/module.ts +40 -8
  321. package/src/types/trace.ts +121 -15
  322. package/src/usage/types.ts +16 -0
  323. package/src/usage/usage-tracker.ts +86 -43
  324. package/dist/src/code-execution/py-runner.d.ts +0 -191
  325. package/dist/src/code-execution/py-runner.d.ts.map +0 -1
  326. package/dist/src/code-execution/py-runner.js +0 -655
  327. package/dist/src/code-execution/py-runner.js.map +0 -1
  328. package/dist/src/code-execution/runtime-py.d.ts +0 -50
  329. package/dist/src/code-execution/runtime-py.d.ts.map +0 -1
  330. package/dist/src/code-execution/runtime-py.js +0 -677
  331. package/dist/src/code-execution/runtime-py.js.map +0 -1
  332. package/dist/src/mcpl/prose-stream-router.d.ts +0 -88
  333. package/dist/src/mcpl/prose-stream-router.d.ts.map +0 -1
  334. package/dist/src/mcpl/prose-stream-router.js +0 -291
  335. package/dist/src/mcpl/prose-stream-router.js.map +0 -1
  336. package/src/code-execution/py-runner.ts +0 -791
  337. package/src/code-execution/runtime-py.ts +0 -677
  338. package/src/mcpl/prose-stream-router.ts +0 -268
@@ -19,6 +19,10 @@ import type { McplServerRegistry } from './server-registry.js';
19
19
  import type { FeatureSetManager } from './feature-set-manager.js';
20
20
  import type { ToolDefinition, ToolResult, ProcessEvent } from '../types/index.js';
21
21
  import type { InboundSource } from './inbound-source.js';
22
+ /** Why a channel is selected for opening after a failed open by speech or a
23
+ * reply: its put-back couldn't be recorded, or the server's latest
24
+ * announcement or the subscription policy lifted the default it restored. */
25
+ export type SelectedForOpening = 'unrecorded' | 'server-bootstrap' | 'subscription-policy';
22
26
  type DesiredChannelState = 'open' | 'closed' | 'tuned-out';
23
27
  /**
24
28
  * Parameters of an active tune-out (issue #77), carried on the
@@ -34,8 +38,11 @@ export interface TuneOutParams {
34
38
  epochId: string;
35
39
  /** Subconscious summary cadence, in seconds. */
36
40
  cadenceSeconds: number;
37
- /** Maximum messages dumped raw at cancel; above the cap the subconscious
38
- * curates a digest and `fetch_history` covers the rest. */
41
+ /** Maximum messages dumped raw at cancel, the newest. Where the residents'
42
+ * strategies cap a message's size, the dump holds as many as fit whole in
43
+ * one message, or, when even the newest doesn't, as much of the newest as
44
+ * fits beside the cut's mark, if any does. For those left out the
45
+ * subconscious curates a digest, and `fetch_history` covers them. */
39
46
  backlogCap: number;
40
47
  /** Wake invocations before the tune-out auto-cancels. */
41
48
  maxWakes: number;
@@ -47,6 +54,14 @@ export interface TuneOutParams {
47
54
  * period chosen by the agent"). Unset = until cancelled. */
48
55
  expiresAtMs?: number;
49
56
  }
57
+ /**
58
+ * The desired state `store`'s lifecycle log records for `channelId` on
59
+ * `serverId`, for a reader with no registry yet, as a serving boot's
60
+ * deferred-write flush is, before initializeMcpl: the last decision wins, as
61
+ * the registry's replay reads it (getDesiredState answers once there is a
62
+ * registry). undefined when no decision names it.
63
+ */
64
+ export declare function desiredStateInLog(store: JsStore, serverId: string, channelId: string): DesiredChannelState | undefined;
50
65
  /** A registered channel entry, keyed by `{serverId}:{channelId}`. */
51
66
  interface ChannelEntry {
52
67
  serverId: string;
@@ -94,8 +109,27 @@ interface McplChannelIncomingEvent {
94
109
  * turn going on.
95
110
  */
96
111
  export declare function journalToolDescription(canSkipReply: boolean): string;
112
+ /**
113
+ * channel_open's and channel_close's descriptions for an agent whose window doesn't hold a channel's traffic: an
114
+ * ephemeral run, whose store is its own unless it's where channel messages are stored (on a host with no residents,
115
+ * the first registered agent), and on a host that routes conversations every agent but a fork, since a channel's
116
+ * messages go to its fork or, unrouted, nowhere. The descriptions in CHANNEL_TOOL_DEFINITIONS, written for the agents
117
+ * that read a channel, promised such an agent what it never gets: "Messages that address you still reach you", and
118
+ * history "preceding the message that invited you" when nothing invites it. Given per agent (AgentFramework's
119
+ * getToolsForAgent), so a holder's cached tool list doesn't change.
120
+ *
121
+ * Both say the call is for every agent: the decision is recorded per server and channel (setDesiredState), so a run's
122
+ * close closes the channel for the agents whose windows hold it, which "its messages don't come to you" alone could
123
+ * read as costing nothing. They claim only what holds on both lanes: a push that names a channel is stored in the
124
+ * residents' shared slot whether or not the channel is open (handleMcplPushEvent), so on a routed host the residents
125
+ * get the channel's pushes, and a server may push while a channel is closed and send channels/incoming once it's
126
+ * open. So opening brings the caller none of the channel's messages, and closing stops none reaching it. The history
127
+ * channel_open asks for comes back in its result (handleToolOpen), so that clause is true for anyone.
128
+ */
129
+ export declare const NON_HOLDER_CHANNEL_DESCRIPTIONS: Readonly<Record<'channel_open' | 'channel_close', string>>;
97
130
  /** Bound connector values for notices, logs and an outcome's reason. A
98
- * PublishOutcome keeps the connector's structured data (`detail`) whole. */
131
+ * PublishOutcome keeps the connector's structured data (`detail`) as the
132
+ * connection gives it, its strings redacted (redactedData). */
99
133
  export declare function connectorText(value: unknown): string;
100
134
  /** Where a publish was resolved to go — named by the registry, never by
101
135
  * the caller's spelling. */
@@ -105,6 +139,9 @@ export interface PublishDestination {
105
139
  /** The channel's registered label when the attempt was made. */
106
140
  label?: string;
107
141
  }
142
+ /** One key per destination, its server and channel: unlike joining the two
143
+ * with a separator, no two destinations share one, whatever their ids hold. */
144
+ export declare function destinationKey(to: PublishDestination): string;
108
145
  /**
109
146
  * What one channels/publish attempt established (ChannelRegistry.publish).
110
147
  * - `delivered`: the connector returned `delivered: true`. A historical
@@ -127,11 +164,29 @@ export interface PublishOutcome {
127
164
  /** Why it failed, or why its outcome is unknown — for a connector error,
128
165
  * its message, bounded (which may say what was already posted). */
129
166
  reason?: string;
130
- /** The connector error's structured `data`, verbatim, when it sent one. */
167
+ /** The connector error's structured `data`, with the strings in it
168
+ * redacted (redactedData), when it sent one. */
131
169
  detail?: unknown;
132
170
  /** When the outcome was established (epoch ms). */
133
171
  at: number;
134
172
  }
173
+ /**
174
+ * Why the destination refused a publishForAgent before anything went out.
175
+ * Its words say the same, since both come from one check: the channel isn't
176
+ * registered here, it takes no posts, or its server is gone, this host is
177
+ * connecting to it, it's reconnecting, or it's up without channels.publish
178
+ * (serverReach); or the request wasn't sent, as its connection refused it
179
+ * before anything was written (McplRequestError's `not-sent`). A refusal of
180
+ * the caller's own input, such as no text or no channel id, carries none,
181
+ * and so does a publish that went out, whatever became of it: its words say
182
+ * whether it may have been posted.
183
+ */
184
+ export type PublishRefusalReason = 'channel-not-registered' | 'inbound-only' | 'server-gone' | 'server-connecting' | 'server-down' | 'not-permitted' | 'not-sent';
185
+ /** A tune-out's open answered for a tune-out that ended, or that a later
186
+ * decision or a later tune-out replaced, while the open was out: by
187
+ * openForTuneOut, and by a repeated tune_out whose tune-out isn't the one it
188
+ * read before it waited. */
189
+ export declare const TUNE_OUT_REPLACED: string;
135
190
  interface ChannelRegistryOptions {
136
191
  /**
137
192
  * An ordinary (uncoalesced) `channels/incoming` message has just been
@@ -144,22 +199,32 @@ interface ChannelRegistryOptions {
144
199
  * RFC-006: an admitted `channels/incoming` message carrying `coalesce`, with
145
200
  * the event the ordinary path would have queued. The handler decides
146
201
  * replace / append / withdraw and returns the per-message result. Throws a
147
- * `CoalesceError` for a malformed or unauthorized occurrence.
202
+ * `CoalesceError` for a malformed or unauthorized occurrence. `firstView`
203
+ * says the coalescer delivered the message as the agent's first view of it,
204
+ * with the delivery's own `firstView` mark; it is the host's own, and isn't
205
+ * in the answer to the server.
148
206
  */
149
- handleCoalescedIncoming?: (serverId: string, message: ChannelIncomingMessage, event: McplChannelIncomingEvent) => Promise<ChannelIncomingMessageResult>;
207
+ handleCoalescedIncoming?: (serverId: string, message: ChannelIncomingMessage, event: McplChannelIncomingEvent) => Promise<ChannelIncomingMessageResult & {
208
+ firstView?: true;
209
+ }>;
150
210
  /** Chronicle store used for durable desired channel lifecycle state. */
151
211
  store?: JsStore;
152
212
  /** Callback to determine whether an incoming message should trigger inference. */
153
213
  shouldTriggerInference?: (content: string, metadata: Record<string, unknown>) => boolean;
154
214
  /**
155
- * Called when a text-only turn's speech could NOT be delivered to its
156
- * conversational locus — no locus, an unregistered channel, a missing
157
- * server, or the server reporting `delivered: false`. Not called for an
158
- * ephemeral run's speech with no locus, which is its caller's result
159
- * (`takesGlobalFallback`). The host wires this to drop a
160
- * `[discord-send-failed]` marker into chronicle so the failure is
161
- * visible to the agent (and operator) rather than silently lost. Must not
162
- * itself trigger inference (avoid wake loops).
215
+ * Called when a text-only turn's speech was not delivered to its
216
+ * conversational locus (`failed`), or may not have been (`unknown`), for
217
+ * whatever reason deliverSpeech gives, such as no locus, a channel that
218
+ * isn't registered or refuses speech, a missing server or grant, a failed
219
+ * open, the server reporting `delivered: false`, or a publish answered
220
+ * with an error, a timeout or a lost connection, after which whether it
221
+ * was posted is unknown. It isn't called for an ephemeral run's speech
222
+ * whose turn has no locus (a null locus), which is its caller's result
223
+ * (`takesGlobalFallback`), nor for a `failed` delivery whose caller holds
224
+ * its words with a notice of its own (deliverSpeech's `heldOnFailure`).
225
+ * The host wires this to drop a `[discord-send-failed]` marker into
226
+ * chronicle so the failure is visible to the agent (and operator) rather
227
+ * than silently lost. Must not itself trigger inference (avoid wake loops).
163
228
  */
164
229
  onRouteFailure?: (info: {
165
230
  conversationId: string;
@@ -198,9 +263,11 @@ interface ChannelRegistryOptions {
198
263
  * forks are spawned bound to a single channel (the framework tracks this in
199
264
  * `conversationAgentHomes` / `ConversationRouter.channelForAgent`); their
200
265
  * plain-text speech must route THERE. The process-global `defaultPublishChannel`
201
- * tracks only the most-recent inbound across ALL channels, so with one fork per
202
- * channel running concurrently it misroutes a fork's reply to whichever channel
203
- * last spoke (item 3). Returns undefined for the trunk/primary agent, which has
266
+ * tracks only the most recent inbound message across ALL channels (reaction,
267
+ * reaction-removal, edit and deletion markers aside, apart from an edit the
268
+ * coalescer delivers on acceptance as the agent's first view), so with one
269
+ * fork per channel running concurrently it misroutes a fork's reply to
270
+ * whichever channel last spoke (item 3). Returns undefined for the trunk/primary agent, which has
204
271
  * no home and correctly falls back to the global locus (heartbeats, etc.).
205
272
  */
206
273
  homeChannelResolver?: (agentName: string) => string | undefined;
@@ -210,11 +277,12 @@ interface ChannelRegistryOptions {
210
277
  * turn. This is the fix for single-TRUNK agents (the only mode
211
278
  * connectome-host runs — it never exposes conversation forks). A trunk agent
212
279
  * has no fork home, so without this its plain-text speech falls back to the
213
- * process-global `defaultPublishChannel`, which tracks the most-recent inbound
214
- * across ALL channels and misroutes a reply to whichever channel last spoke
215
- * under concurrency (item-3 redux). The framework keeps both on the live
216
- * turn's routing state and exposes them here. For a heartbeat / no-trigger
217
- * turn it resolves to undefined until the turn's locus is resolved, so that
280
+ * process-global `defaultPublishChannel`, which tracks the most recent inbound
281
+ * message across ALL channels (markers aside) and misroutes a reply to
282
+ * whichever channel last spoke under concurrency (item-3 redux). The
283
+ * framework keeps both on the live turn's routing state and exposes them
284
+ * here. For a heartbeat / no-trigger turn it resolves to undefined until the
285
+ * turn's locus is resolved, so that
218
286
  * resolution takes the global fallback, which the turn then keeps.
219
287
  * Consulted AFTER `homeChannelResolver` (a fork's home always wins), BEFORE
220
288
  * `defaultPublishChannel`.
@@ -234,6 +302,14 @@ interface ChannelRegistryOptions {
234
302
  takesGlobalFallback?: (conversationId: string) => boolean;
235
303
  /** Whether this framework has EventGate and can honor skip_reply self-wakes. */
236
304
  eventGatePresent?: boolean;
305
+ /**
306
+ * Whether this host is connecting to the server of this id: a connect or a
307
+ * restart of the id is in flight, or the policy exchange of the transport
308
+ * its registered connection is on is. Its channel tools and speech then say
309
+ * so, as its tool calls do, rather than read the empty grant meanwhile as a
310
+ * refusal (serverReach). It words a refusal only: the grant decides.
311
+ */
312
+ serverConnecting?: (serverId: string) => boolean;
237
313
  }
238
314
  /**
239
315
  * Who is actually making a channel tool call, as established by the
@@ -264,9 +340,17 @@ export declare class ChannelRegistry {
264
340
  private homeChannelResolver?;
265
341
  private activeChannelResolver?;
266
342
  private takesGlobalFallback?;
343
+ private serverConnecting?;
267
344
  private store?;
268
345
  /** Registered channels, keyed by `{serverId}:{channelId}`. */
269
346
  private channels;
347
+ /**
348
+ * Each outgoing stream, by `inferenceId`, with the destinations it has
349
+ * started at (destinationKey): the rest of the stream there goes on there,
350
+ * and its `complete` goes only there (sendOutgoingChunk). An entry goes
351
+ * with the stream's `complete` for its destination.
352
+ */
353
+ private takenStreams;
270
354
  /** Holds back repeats of the line for a message refused because its
271
355
  * channel isn't registered (RefusalLines). */
272
356
  private unknownChannelLines;
@@ -315,9 +399,12 @@ export declare class ChannelRegistry {
315
399
  * sent at all.
316
400
  */
317
401
  private dmClassified;
318
- /** Most recent incoming channel ID — used for speech routing / default publish. */
402
+ /** The channel of the most recent incoming message that isn't a reaction,
403
+ * reaction removal, edit or deletion marker, apart from an edit the
404
+ * coalescer delivers on acceptance as the agent's first view: used for
405
+ * speech routing and the default publish. */
319
406
  private defaultPublishChannel;
320
- /** Most recent incoming message metadata, used for buildChannelContext. */
407
+ /** The same message's id and thread, used for buildChannelContext. */
321
408
  private defaultPublishMessageId;
322
409
  private defaultPublishThreadId;
323
410
  /** Per-channel typing indicator timers. */
@@ -335,14 +422,71 @@ export declare class ChannelRegistry {
335
422
  private channelLifecycleTails;
336
423
  private serverRemovals;
337
424
  private lifecycleOutstanding;
425
+ private lifecycleQueuedRemovals;
338
426
  private unconfirmedChannelTargets;
427
+ private failedCloseTargets;
428
+ private tuneOutOpens;
429
+ private untargetedPushEntries;
430
+ private transportCloses;
339
431
  private invalidateTransportConfirmation;
340
432
  /** Whether the host directs this server's channel lifecycle (§14.1: its
341
433
  * `channels.lifecycle` grant). Where it doesn't, the server opens and
342
434
  * closes its own channels, and sends no receipt the host could wait for. */
343
- private directsLifecycle;
344
- /** A registration's entry: nothing about its transport is known until a
345
- * receipt for its target confirms it open or closed. */
435
+ directsLifecycle(serverId: string): boolean;
436
+ /** Whether a decided close holds words back from a channel: only where
437
+ * the host directs the server's lifecycle. Without that grant the host
438
+ * can't close the channel, and words go into a transport that is open,
439
+ * as the open step's engagesDecidedClosed reads it too. */
440
+ private closeStands;
441
+ /** Where this server stands for a request this host would make of it
442
+ * (ServerReach). One without a connection's state reads as connected.
443
+ * A closed connection the host is restarting reads as connecting, as
444
+ * one being connected does. */
445
+ private serverReach;
446
+ /**
447
+ * Why this host won't open or close this server's channels, for a caller
448
+ * that found its grant without channels.lifecycle (§14.1), which decides:
449
+ * the server's state says why. It isn't up, or it's up without that grant.
450
+ * Neither the grant's name, which is the operator's, nor an empty grant's
451
+ * refusal, which needn't be the server's, reaches the agent.
452
+ */
453
+ private lifecycleRefusal;
454
+ /**
455
+ * Why a send to this server's channels is refused before anything goes
456
+ * out, as one clause with what works from there, for a caller that found
457
+ * no server or a grant without channels.publish (§14.1), which decides:
458
+ * the server's state says why. Plain speech gives it as its reason, and
459
+ * channel_publish and speak_in_channel as theirs, so one refusal has one
460
+ * wording. `checked` is the connection whose grant refused, where that
461
+ * was read before the words waited: one the registry has replaced since
462
+ * says nothing about the server that stands now.
463
+ */
464
+ private sendRefusal;
465
+ /**
466
+ * What a failed channel_open or channel_close tells the agent. Its
467
+ * decision was recorded before the server was asked, and reconciling
468
+ * applies it when the server next registers the channel (for a push's
469
+ * placeholder, if it ever does), if this host directs their lifecycle then
470
+ * (laterClause). A decision the log refused changed nothing, so that comes
471
+ * first. A channel no longer registered
472
+ * can't be reached by any channel tool, so its text names none. A later
473
+ * decision that came first is what stands, so that comes next. Then the
474
+ * server's state: one that isn't up gets the change when it's back, though
475
+ * not a backscroll's history. For the request's own failure, its state is
476
+ * read now, since a request out when the server dropped may have taken
477
+ * effect, and reconciling settles it either way. A decision already in
478
+ * effect, with no other work for the channel under way, says so, as one
479
+ * whose backscroll alone failed is. Then the failure's kind, since each
480
+ * has its own next step (ChannelLifecycleError). A request's own failure
481
+ * is in the connection's words.
482
+ */
483
+ private lifecycleFailureText;
484
+ /** A registration's entry, or a push's (ensureChannelRegistered): nothing
485
+ * about its transport is known until a receipt for its target confirms it
486
+ * open or closed, or the channel's traffic shows it open, which it does
487
+ * where the host doesn't direct the lifecycle, and for a push's entry
488
+ * decided open or tuned out that no update has given a target
489
+ * (handleIncoming). */
346
490
  private registerEntry;
347
491
  private hasPendingLifecycle;
348
492
  private handleCoalescedIncoming?;
@@ -397,15 +541,17 @@ export declare class ChannelRegistry {
397
541
  * locus. A push event from a closed channel must never mutate lifecycle
398
542
  * state: direct-address events are intentionally usable without subscribing.
399
543
  *
400
- * Mirrors the lazy-registration inside handleIncoming(), but for channels
401
- * that only ever arrive as push/events rather than channels/incoming — the
402
- * motivating case is Discord DMs, which discord-mcpl forwards via push/event
403
- * with the channel closed (`channelIsOpen:false`). Such a channel is never
404
- * registered, so routeSpeech() can't resolve it and the agent's reply is
405
- * silently dropped even though the inbound message proves the channel is
406
- * reachable (item-3 redux, DM sub-case). Idempotent: a later authoritative
407
- * channels/register or channels/changed overwrites this descriptor with the
408
- * richer one.
544
+ * For channels that only ever arrive as push/events rather than
545
+ * channels/incoming — the motivating case is Discord DMs, which
546
+ * discord-mcpl forwards via push/event with the channel closed
547
+ * (`channelIsOpen:false`). Such a channel is never registered, so
548
+ * routeSpeech() can't resolve it and the agent's reply is silently dropped
549
+ * even though the inbound message proves the channel is reachable (item-3
550
+ * redux, DM sub-case). Its entry starts with its transport unknown, as a
551
+ * registration's does. Its traffic shows it open, for a channel decided
552
+ * open or tuned out, until an update gives it a target (handleIncoming).
553
+ * Idempotent: a later authoritative channels/register or channels/changed
554
+ * overwrites this descriptor with the richer one.
409
555
  */
410
556
  ensureChannelRegistered(serverId: string, channelId: string, label?: string, extraMetadata?: Record<string, unknown>): void;
411
557
  /**
@@ -425,7 +571,10 @@ export declare class ChannelRegistry {
425
571
  */
426
572
  stopTyping(channelId?: string): void;
427
573
  /**
428
- * Get the default publish channel ID (most recent incoming channel).
574
+ * Get the default publish channel ID: the channel of the most recent
575
+ * incoming message that isn't a reaction, reaction removal, edit or
576
+ * deletion marker, apart from an edit the coalescer delivers on acceptance
577
+ * as the agent's first view.
429
578
  */
430
579
  getDefaultPublishChannel(): string | null;
431
580
  /**
@@ -444,7 +593,12 @@ export declare class ChannelRegistry {
444
593
  /** The registered label of `channelId` on exactly `serverId` (no
445
594
  * cross-server first match), or undefined when it has none. */
446
595
  getChannelLabel(serverId: string, channelId: string): string | undefined;
447
- isChannelOpen(channelId: string): boolean;
596
+ /** Whether `channelId`'s transport is open: on `serverId` where the caller
597
+ * knows the channel's server, and otherwise on the first server found to
598
+ * register that id (findChannelEntry). The map's key joins the two ids
599
+ * with a colon, so another server's entry can sit under a key: it has to
600
+ * be this server's (as in streamingServerAt). */
601
+ isChannelOpen(channelId: string, serverId?: string): boolean;
448
602
  getDesiredState(serverId: string, channelId: string): DesiredChannelState | undefined;
449
603
  /**
450
604
  * Get all open channels.
@@ -479,6 +633,36 @@ export declare class ChannelRegistry {
479
633
  * server's channels don't linger and route speech into the void.
480
634
  */
481
635
  removeServer(serverId: string): void;
636
+ /**
637
+ * The server's transport closed. Whatever it confirmed about a channel,
638
+ * open or closed, held for that transport only, so each of the server's
639
+ * channels reads as a registration's does: not open, and not confirmed
640
+ * closed. Where the host directs the server's lifecycle, a receipt
641
+ * confirms it again: a `channel_open` asks the server, and delivery opens
642
+ * before it publishes (an untargeted push entry's traffic confirms it, as
643
+ * before). Where the host doesn't, its traffic does, as after a
644
+ * registration. The entries, their desired state, labels and tune-out
645
+ * state stay, since the server is expected back (`removeServer` is for one
646
+ * that isn't).
647
+ *
648
+ * What was under way here at the close confirms nothing afterwards
649
+ * (transportCloses). Traffic that arrived before the close and is admitted
650
+ * after it, behind the coalescer's await, marks nothing open
651
+ * (handleIncoming). A lifecycle request sent before the close is answered
652
+ * for an older transport: its failure withdraws the open flag, and its
653
+ * receipt confirms nothing, and the operation tries again, which on the
654
+ * closed connection is refused as unreachable, so the decision waits for
655
+ * the next registration (runLifecycleOperation's sameTarget). What the
656
+ * connection holds across the boundary is another matter: it reaches
657
+ * handleIncoming after the next transport's grant, as that transport's
658
+ * traffic (resetPolicyForTransportBoundary), as it does after a
659
+ * registration.
660
+ *
661
+ * The framework calls this first in its handler for the connection's
662
+ * `close`, so a registration on the next transport, whose entries are new,
663
+ * can't come first and lose its confirmations to it.
664
+ */
665
+ transportClosed(serverId: string): void;
482
666
  /**
483
667
  * Stop all typing intervals and clear all channel registrations.
484
668
  */
@@ -488,7 +672,9 @@ export declare class ChannelRegistry {
488
672
  /**
489
673
  * Each writer appends its record here before it changes memory for it, so
490
674
  * an append that throws, as on a full disk, fails the operation with that
491
- * change unmade, and memory and the log still agree. A retry then records
675
+ * change unmade, and memory and the log still agree. A record that changes
676
+ * no memory, as a decline's of the invitation it declined, is its
677
+ * caller's to handle (handleToolDecline). A retry then records
492
678
  * the change rather than finding it in memory already, where
493
679
  * setDesiredState's same-state return would record nothing, and a restart
494
680
  * replays what memory held. appendLabelSighting keeps the same order for
@@ -627,9 +813,59 @@ export declare class ChannelRegistry {
627
813
  * Enter (or re-enter with fresh params) the tuned-out state for a channel.
628
814
  * Re-entering under a new epochId replaces the active epoch; the previous
629
815
  * epoch's stamped messages stay excluded (stamps are permanent) and its
630
- * wake count is superseded. Transport stays open (see reconcile).
816
+ * wake count is superseded. Its transport is kept open: tune_out opens a
817
+ * closed one (openForTuneOut), and reconcile opens it at the channel's
818
+ * registration.
631
819
  */
632
820
  enterTuneOut(serverId: string, channelId: string, params: TuneOutParams, source: string): void;
821
+ /**
822
+ * Make a tuned-out channel's transport match its tune-out, which keeps it
823
+ * open so its traffic reaches the subconscious (TuneOutParams). The
824
+ * tune-out is recorded first (enterTuneOut), and this writes no decision,
825
+ * so the tune-out stands whatever becomes of the open. Lifecycle work
826
+ * queued for the channel goes first, until none is left, since each
827
+ * operation reads the decision when it runs and so opens a channel tuned
828
+ * out meanwhile; its own failure, such as a close the server refused,
829
+ * isn't this open's. Then the state is read. Where this host directs the
830
+ * lifecycle and the transport isn't confirmed open, the channel's
831
+ * lifecycle queue opens it once (applyDesiredChannelState reads a
832
+ * tune-out as open), and the answer waits for the work queued after it
833
+ * too, such as a registration that came back, and comes from the state
834
+ * that work leaves. Returns nothing where the transport is open, or, for
835
+ * tune_out's answer, why the channel's messages don't or may not reach the
836
+ * subconscious yet and when they will, or that the tune-out isn't in
837
+ * effect any more. A call while this tune-out's open is in flight, as a
838
+ * repeated tune_out makes, gets that open's answer rather than send
839
+ * another.
840
+ */
841
+ openForTuneOut(serverId: string, channelId: string): Promise<string | undefined>;
842
+ /**
843
+ * Whether a tune_out repeated now has nothing to wait for and finds its
844
+ * channel's transport open, as the tune-out reads it: no lifecycle work
845
+ * queued for the channel, since the tune-out's own call may still be
846
+ * waiting for the work queued after its open; its server up; and an open
847
+ * flag the tune-out takes as open (tuneOutReadsOpen). Where it doesn't,
848
+ * the repeat goes through openForTuneOut, which joins this tune-out's open
849
+ * in flight, or waits for the queued work and, where this host can open
850
+ * the channel, tries the open again.
851
+ */
852
+ tuneOutTransportOpen(serverId: string, channelId: string): boolean;
853
+ /**
854
+ * Whether a tune-out takes an entry's transport as open: its open flag,
855
+ * unless a close for it failed since it was confirmed (failedCloseTargets),
856
+ * since that close may have taken effect. The other readers take any flag
857
+ * a failed request left as it was (runLifecycleOperation), since a send
858
+ * through it fails where it can be seen, and channel_open sends an open
859
+ * after a failed close anyway, its decision having been closed. A
860
+ * tune-out's success says traffic will arrive, nothing would show it
861
+ * doesn't, and the decision it replaced isn't known here. An open that
862
+ * failed, as a redundant one queued behind a confirming one may, can't
863
+ * have closed the channel: one for the current target leaves the flag as
864
+ * it was, so an open flag stays open (one for a target no longer current
865
+ * withdraws it).
866
+ */
867
+ private tuneOutReadsOpen;
868
+ private openTuneOutTransport;
633
869
  /**
634
870
  * End the active tune-out, returning the channel to `nextState`.
635
871
  * The caller (tune-out coordinator) owns the dump/notice flow; this is
@@ -670,14 +906,24 @@ export declare class ChannelRegistry {
670
906
  writeCoordinatorState(stateId: string, value: unknown): void;
671
907
  /**
672
908
  * Publish into a channel on behalf of a named non-resident agent (the
673
- * subconscious's speak_in_channel). Same delivery path as the
674
- * channel_publish tool; the agent name rides the speech-routed trace.
909
+ * subconscious's speak_in_channel). Same delivery path, and the same result
910
+ * as the channel_publish tool apart from `reason` below. Its traces carry the agent's name and follow
911
+ * the publish's outcome as deliverSpeech's do: speech-routed after a
912
+ * confirmed delivery, and speech-route-failed otherwise, `unknown` when the
913
+ * words may have been posted and `failed` when nothing was, a refusal before
914
+ * the publish included. The routed trace carries no message id, as before:
915
+ * nothing here reads one, and a consumer that edits the newest traced
916
+ * message in a channel would have this post to edit when an interruption of
917
+ * the resident matches none. A refusal of the destination before anything
918
+ * went out carries its `reason` (PublishRefusalReason), so a caller can act
919
+ * on why nothing was posted without reading the words.
675
920
  */
676
921
  publishForAgent(channelId: string, text: string, agentName: string): Promise<{
677
922
  success: boolean;
678
923
  data?: unknown;
679
924
  error?: string;
680
925
  isError?: boolean;
926
+ reason?: PublishRefusalReason;
681
927
  }>;
682
928
  /**
683
929
  * Consume the old recipe policy exactly once. It seeds Chronicle for
@@ -707,6 +953,25 @@ export declare class ChannelRegistry {
707
953
  private applyDesiredChannelState;
708
954
  /** One queued lifecycle operation, run when its turn in the queue comes. */
709
955
  private runLifecycleOperation;
956
+ /** Whether lifecycle work already outstanding for the channel will carry out
957
+ * its current decision: some is, and the newest was queued since its
958
+ * server's registrations were last removed, so it won't cancel when it
959
+ * would run. Each operation reads the decision when it runs. It answers for
960
+ * now: a removal after it cancels that work too, and takes the channel's
961
+ * entry with it, so the channel's next registration by channels/register,
962
+ * or as added by channels/changed, carries the decision out, where this
963
+ * host directs the lifecycle then. A push's registration
964
+ * (ensureChannelRegistered) queues no lifecycle work. So where a push
965
+ * re-makes an entry that a channels/changed removal took while an RPC for
966
+ * the old entry was out, the operation running that RPC carries the
967
+ * decision out for the push's entry only if the RPC is answered: a failed
968
+ * one rethrows, since its retry covers only a retarget of its own entry.
969
+ * This answers true meanwhile, so a channels/changed update that retargets
970
+ * the push's entry is left to that operation, and stays undone after a
971
+ * failed RPC until lifecycle work is queued for the channel again
972
+ * (applyDesiredChannelState). No server we know of sends `updated` for a
973
+ * channel it has removed. */
974
+ private outstandingWorkCarriesOut;
710
975
  /**
711
976
  * A server that answered an open with another channel really opened that
712
977
  * one. When it's registered here and the host wants it closed, close it
@@ -717,11 +982,22 @@ export declare class ChannelRegistry {
717
982
  * doesn't know needs nothing: §14.5 refuses its traffic.
718
983
  */
719
984
  private closeSubstitutedChannel;
985
+ /**
986
+ * Carry out a channel's decision through its lifecycle queue, for a
987
+ * transport the server may have opened without the host's knowing: one it
988
+ * opened for another channel's request (closeSubstitutedChannel), or one a
989
+ * failed open's request may have reached (openChannelNow). The decision is
990
+ * a closed one, except after a failed open whose put-back the server's
991
+ * latest announcement or the policy lifted. A failure is reported as a
992
+ * trace; the decision stands.
993
+ */
994
+ private applyThroughQueue;
720
995
  /**
721
996
  * Join outstanding lifecycle work without recording new intent. Retargeting
722
997
  * may have invalidated open confirmation while the decision remains pending.
723
- * A close that settles during the wait remains authoritative; waiting for
724
- * it is not an instruction to open the channel again.
998
+ * A close that settles during the wait remains authoritative where the host
999
+ * directs the server's lifecycle (closeStands); waiting for it is not an
1000
+ * instruction to open the channel again.
725
1001
  */
726
1002
  private waitForOpenChannel;
727
1003
  private reconcileChannels;
@@ -736,6 +1012,13 @@ export declare class ChannelRegistry {
736
1012
  descriptorsForServer(serverId: string): ChannelDescriptor[];
737
1013
  /** Server id owning a registered channel (by descriptor id), or null. */
738
1014
  getChannelServerId(channelId: string): string | null;
1015
+ /**
1016
+ * The server that declared this channel inbound-only (isInboundOnly), or
1017
+ * undefined when it isn't one. The notices name that server. A caller that
1018
+ * knows the channel's server passes it; otherwise the channel is found by
1019
+ * its id alone (findChannelEntry).
1020
+ */
1021
+ inboundOnlyServer(channelId: string, serverId?: string): string | undefined;
739
1022
  /**
740
1023
  * Find a channel entry by channelId (searches across all servers).
741
1024
  * Returns the first match.
@@ -750,21 +1033,56 @@ export declare class ChannelRegistry {
750
1033
  /**
751
1034
  * Record durable desired-open intent, then queue the channel's lifecycle
752
1035
  * work (applyDesiredChannelState), which tells the server to subscribe and
753
- * marks the live entry open on a receipt for its current target. Every
754
- * open the agent or its speech asks for — the channel_open tool, delivery
1036
+ * marks the live entry open on a receipt for its current target, applying
1037
+ * an inbound-only declaration the answer carries (applyOpenDeclaration).
1038
+ * Every open the agent or its speech asks for — the channel_open tool, delivery
755
1039
  * into a closed locus, an explicit send into a closed channel — comes
756
1040
  * here. Reconciliation queues that work directly, as the closes do
757
1041
  * (channel_close, a decline, and the close of a substituted channel).
758
- * Desired state is recorded BEFORE the server round-trip: intent sticks
759
- * even if the subscribe fails, and reconciliation retries later.
760
- * Automatic delivery needs open transport, not a change of attention:
761
- * preserve tune-out epochs both before and during the round-trip.
1042
+ * Desired state is recorded BEFORE the server round-trip. The agent's own
1043
+ * open is a decision about the channel: it sticks even if the subscribe
1044
+ * fails, and reconciliation retries later. An open by speech or a reply is
1045
+ * incidental to its words: when its round-trip fails, the decision it
1046
+ * wrote goes back to the one it replaced, so the agent, told the channel
1047
+ * is closed and the open failed, isn't later put into it by that decision.
1048
+ * That holds only while the record is still the one it wrote: a decision
1049
+ * made meanwhile stands, and the failure then comes as `overtaken`
1050
+ * (ChannelLifecycleError). Where the decision put back is a pure default,
1051
+ * nobody had decided, so the channel then follows its server's latest
1052
+ * announcement and the policy: a re-announcement during the round-trip
1053
+ * found the open's own record and lifted nothing. If they select it for
1054
+ * opening, the failure says so (`selected-again`). And since
1055
+ * a failed request may have reached the server, the decision that stands
1056
+ * is carried out on the wire too. An open by speech or a reply that can't
1057
+ * send its request, on a server without channels.lifecycle, writes
1058
+ * nothing. Automatic delivery needs open transport, not a change of
1059
+ * attention: preserve tune-out epochs both before and during the
1060
+ * round-trip.
762
1061
  *
763
1062
  * `opened` says whether this call turned a closed channel open, which is
764
1063
  * what the delivery and reply notices tell the resident. Reconfirming the
765
1064
  * transport of a channel already decided open or tuned out isn't one.
766
1065
  */
767
1066
  private openChannelNow;
1067
+ /**
1068
+ * An open's answer carries the server's descriptor for the channel
1069
+ * (§14.3), the first place a lazily registered channel's own declaration
1070
+ * can arrive. runLifecycleOperation hands it here, wherever the host opens
1071
+ * a channel, once the answer is known to be for the channel's current
1072
+ * entry and target. An answer that names another channel never arrives
1073
+ * here: it's a substitution, which fails the open. One whose channel names
1074
+ * no id is this channel's, as runLifecycleOperation takes it, so its
1075
+ * declaration applies too.
1076
+ *
1077
+ * Only an inbound-only declaration is applied. The answer isn't always the
1078
+ * server's latest word: a `channels/changed` that arrives in the same read
1079
+ * as the answer is handled before the open's operation resumes, so the
1080
+ * answer is applied over it. Applying only a refusal keeps that race
1081
+ * fail-closed: a stale answer can keep a channel refused until the
1082
+ * server's next update, but never lift a newer refusal. A server lifts its
1083
+ * declaration by changing or registering the channel again.
1084
+ */
1085
+ private applyOpenDeclaration;
768
1086
  /**
769
1087
  * Resolve a `>>` prose-routing target (explicit prose routing —
770
1088
  * docs/explicit-prose-routing.md) to a registered channel.
@@ -778,14 +1096,25 @@ export declare class ChannelRegistry {
778
1096
  *
779
1097
  * Ambiguity is an error carrying candidates — never a guess: this is the
780
1098
  * mechanism that makes prose misdelivery structurally impossible.
1099
+ *
1100
+ * `forSpeech` is for a target the agent's speech will be posted to: a
1101
+ * channel its server declared inbound-only (isInboundOnly) then resolves
1102
+ * to an error that says so, since nothing is published there, and that
1103
+ * names the channel as `refusedChannelId`, so a caller can tell a refused
1104
+ * channel from a name that resolved to nothing. Without it, as for reading
1105
+ * a channel's history, any registered channel resolves.
781
1106
  */
782
- resolveProseTarget(spec: string): {
1107
+ resolveProseTarget(spec: string, opts?: {
1108
+ forSpeech?: boolean;
1109
+ }): {
783
1110
  channelId: string;
784
1111
  label?: string;
785
1112
  } | {
786
1113
  error: string;
787
1114
  candidates?: string[];
1115
+ refusedChannelId?: string;
788
1116
  };
1117
+ private resolveTargetByName;
789
1118
  /**
790
1119
  * The `>>` target to show an agent for a channel: one whitespace-free token
791
1120
  * that `resolveProseTarget()` maps back to this same channel. The prefix
@@ -808,12 +1137,24 @@ export declare class ChannelRegistry {
808
1137
  * engaging a channel opens it, so typing indicators, reaction machinery,
809
1138
  * and inbound forwarding all come alive with the first outbound message.
810
1139
  * Accepts composite MCPL ids or raw server-internal ids (explicit send
811
- * tools receive whatever the agent typed).
1140
+ * tools receive whatever the agent typed). An `open-failed` answer says
1141
+ * whether the open that failed was one that would have opened a channel
1142
+ * not selected open (`opening`), as opposed to one reconfirming the
1143
+ * transport of a channel already selected open or tuned out. It isn't one
1144
+ * either where a decision made during its round-trip replaced it
1145
+ * (`overtaken`), or where queued lifecycle work left the
1146
+ * channel closed before any open was sent. `selectedForOpening` says the
1147
+ * channel is selected for opening after the failure, and why: the put-back
1148
+ * couldn't be recorded (`put-back-unrecorded`), or the server's latest
1149
+ * announcement or the policy lifted the default it restored
1150
+ * (`selected-again`).
812
1151
  */
813
1152
  openIfClosedForSend(rawChannelId: string, serverId?: string): Promise<{
814
1153
  status: 'opened' | 'already-open' | 'unknown-channel' | 'ambiguous' | 'open-failed';
815
1154
  channelId?: string;
816
1155
  label?: string;
1156
+ opening?: boolean;
1157
+ selectedForOpening?: SelectedForOpening;
817
1158
  }>;
818
1159
  private handleToolOpen;
819
1160
  private handleToolClose;
@@ -850,29 +1191,47 @@ export declare class ChannelRegistry {
850
1191
  */
851
1192
  private handleToolSkipReply;
852
1193
  /**
853
- * Host-owned output routing (see forking-knowledge-miner LOCUS-ROUTING-DESIGN).
854
- * Publish the agent's plain-text speech to the current conversational locus
855
- * (the most recent incoming channel, tracked cross-surface here in the host).
856
- * Called by the framework on a text-only turn — replaces the per-surface
857
- * sticky auto-post that used to live in discord-mcpl. Returns null when there
858
- * is no locus / the channel or its server can't be resolved (in which case
859
- * the speech simply stays in chronicle + module surfaces).
1194
+ * MCPL Spec 14.3 outgoing streaming: forward a text delta to the server
1195
+ * owning the channel. The framework sends each piece of prose once its
1196
+ * publish succeeds, so a stream carries only what was published.
1197
+ * Emitted only when that server's grant has `channels.streaming` —
1198
+ * servers that never opted in receive nothing. Fire-and-forget and never
1199
+ * throws: streaming is an observer surface; the authoritative delivery is
1200
+ * channels/publish.
1201
+ *
1202
+ * `to` is the destination the publish went to, its server and channel.
1203
+ * The chunk goes there while that server registers that channel (Spec
1204
+ * 14.3: the recipient is "the server that registered the target
1205
+ * channel"). A stream doesn't start in a channel its server has declared
1206
+ * inbound-only (#350's rule), not even for a publish delivered there just
1207
+ * before the declaration. One that started there before it goes on, since
1208
+ * the declaration doesn't unregister the channel: a later chunk is for a
1209
+ * publish delivered before it. A bare channel id is resolved as before, to
1210
+ * the one server that registers it, at each call (streamingServerFor), and
1211
+ * keeps no record: a stream sent by destination ends by destination, since
1212
+ * only a destination's `complete` removes its record.
860
1213
  */
861
- /** Resolve the outbound locus (fork HOME → this-turn's TRIGGERING channel →
862
- * process-global default). Public so a multi-segment caller can snapshot it
863
- * ONCE and pin every segment to it via routeSpeech's `overrideChannelId`. */
1214
+ sendOutgoingChunk(to: string | PublishDestination, conversationId: string, inferenceId: string, index: number, delta: string): void;
864
1215
  /**
865
- * MCPL Spec 14.3 outgoing streaming: forward a moderated text delta to the
866
- * server owning the channel, AS THE MODEL GENERATES. Emitted only when that
867
- * server declared `channels.streaming` in its initialize capabilities —
868
- * servers that never opted in (the whole existing fleet) receive nothing.
869
- * Fire-and-forget and never throws: streaming is an observer surface; the
870
- * authoritative delivery is the eventual channels/publish.
1216
+ * Spec 14.3 companion: final moderated content per channel at stream end.
1217
+ * Given a destination, it goes only where the stream started
1218
+ * (sendOutgoingChunk), while that server still registers that channel,
1219
+ * inbound-only or not, and its grant has `channels.streaming`: once per
1220
+ * destination, whose consumer, such as a TTS utterance, finalizes on it
1221
+ * and reconciles any chunk it missed. A bare channel id is resolved as
1222
+ * before (streamingServerFor).
871
1223
  */
872
- sendOutgoingChunk(channelId: string, conversationId: string, inferenceId: string, index: number, delta: string): void;
873
- /** Spec 14.3 companion: final moderated content per channel at stream end. */
874
- sendOutgoingComplete(channelId: string, conversationId: string, inferenceId: string, text: string): void;
1224
+ sendOutgoingComplete(to: string | PublishDestination, conversationId: string, inferenceId: string, text: string): void;
875
1225
  private streamingServerFor;
1226
+ /**
1227
+ * A destination's server, for a stream message: while that server
1228
+ * registers that channel, and its grant has `channels.streaming` (as in
1229
+ * streamingServerFor). A stream doesn't start in an inbound-only channel.
1230
+ */
1231
+ private streamingServerAt;
1232
+ /** Resolve the outbound locus (fork HOME → this-turn's TRIGGERING channel →
1233
+ * process-global default). Public so a multi-segment caller can snapshot it
1234
+ * ONCE and pin every segment to it via routeSpeech's `overrideChannelId`. */
876
1235
  resolveLocus(conversationId: string): string | null;
877
1236
  /** The process-global fallback channel for this conversation, or null when it takes none (takesGlobalFallback). */
878
1237
  private globalFallbackFor;
@@ -908,7 +1267,33 @@ export declare class ChannelRegistry {
908
1267
  * possible to send into a closed channel: a closed but registered
909
1268
  * destination is opened first (`openSource` names why), once its server
910
1269
  * and that server's channels.publish grant are known to take the send, so
911
- * a refused send opens nothing; and a failed open sends nothing.
1270
+ * a send refused before the open opens nothing; and a failed open sends
1271
+ * nothing.
1272
+ *
1273
+ * A channel its server declared inbound-only (isInboundOnly) takes nothing
1274
+ * the host would post, whichever path the words took, a drafts resend
1275
+ * included: it's refused before anything opens. The channel's queued
1276
+ * lifecycle work and the open await the server, and a channels/changed
1277
+ * update, a re-register or the open's own answer (applyOpenDeclaration)
1278
+ * may declare the channel inbound-only meanwhile, or a removal take it
1279
+ * away. So the check just before the publish, after every await, reads
1280
+ * the channel's registration as it is then, and refuses one that is gone
1281
+ * or inbound-only, or whose server's grant no longer has channels.publish.
1282
+ * A removal during a wait already fails it there (waitForOpenChannel); the
1283
+ * check also catches one made after the last wait, as the opening's notice
1284
+ * can. A registration nothing has confirmed open, such as one that
1285
+ * replaced the one waited for or a retarget of it, or one whose close now
1286
+ * stands (where the host directs the server's lifecycle), is treated as at
1287
+ * the first wait: the words wait for its queued lifecycle work, and the
1288
+ * check runs again on the registration that work leaves. Words refused
1289
+ * after an open for a declaration leave the channel open, and the agent
1290
+ * gets the failure for the words. An open by delivery (`openSource`
1291
+ * 'opened-by-delivery', the default) that turned a closed channel open
1292
+ * also gives the notice that it's open, which says how to close it to a
1293
+ * reader that can.
1294
+ * One that only reconfirmed the transport of a channel already decided
1295
+ * open or tuned out changes no decision, and gives none. When the
1296
+ * registration is gone, the failure says the channel was removed.
912
1297
  *
913
1298
  * Only the connector's `delivered: true` confirms a post. `failed` means
914
1299
  * nothing was posted: the attempt stopped before the request was written,
@@ -932,9 +1317,13 @@ export declare class ChannelRegistry {
932
1317
  * Plain speech for one turn segment: publish `text` to the turn's frozen
933
1318
  * locus and report the PublishOutcome. A failure (or an uncertain outcome)
934
1319
  * is surfaced as a trace and through onRouteFailure, so the agent learns
935
- * its words did not (or may not have) reached anyone. An ephemeral run's
936
- * speech with no locus is the exception: it is its caller's result, so it
937
- * returns as not delivered and surfaces nothing.
1320
+ * its words did not (or may not have) reached anyone. A `failed` one whose
1321
+ * caller holds its words with a notice of its own (`heldOnFailure`) gets
1322
+ * the trace but not onRouteFailure. An ephemeral run's speech with no locus
1323
+ * (null) is its caller's result: it returns as not delivered and surfaces
1324
+ * nothing. This is host-owned output routing (see forking-knowledge-miner
1325
+ * LOCUS-ROUTING-DESIGN), which replaced the per-surface sticky auto-post
1326
+ * that used to live in discord-mcpl.
938
1327
  */
939
1328
  deliverSpeech(conversationId: string, text: string,
940
1329
  /** The turn's FROZEN locus, snapshotted once by the caller (resolveLocus
@@ -956,9 +1345,16 @@ export declare class ChannelRegistry {
956
1345
  /** deliverSpeech, reduced to the confirmed delivery (null when there was none). */
957
1346
  routeSpeech(conversationId: string, text: string, locusChannelId: string | null): Promise<{
958
1347
  delivered: boolean;
1348
+ serverId: string;
959
1349
  channelId: string;
960
1350
  messageId?: string;
961
1351
  } | null>;
1352
+ /**
1353
+ * channel_publish, and the subconscious's speak_in_channel
1354
+ * (publishForAgent), which shares its delivery. Where an agent finds a
1355
+ * channel id differs: the subconscious isn't offered channel_list, and is
1356
+ * given speak_in_channel for the tuned-out channels whose messages it reads.
1357
+ */
962
1358
  private handleToolPublish;
963
1359
  }
964
1360
  export {};