@animalabs/agent-framework 0.26.1 → 0.27.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 (276) hide show
  1. package/README.md +57 -7
  2. package/dist/src/agent.d.ts +263 -22
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +531 -54
  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 +1 -2
  8. package/dist/src/api/server.js.map +1 -1
  9. package/dist/src/auto-continue.d.ts +33 -0
  10. package/dist/src/auto-continue.d.ts.map +1 -0
  11. package/dist/src/auto-continue.js +35 -0
  12. package/dist/src/auto-continue.js.map +1 -0
  13. package/dist/src/code-execution/isolate/scripts.d.ts +11 -0
  14. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
  15. package/dist/src/code-execution/isolate/scripts.js +5 -1
  16. package/dist/src/code-execution/isolate/scripts.js.map +1 -1
  17. package/dist/src/framework.d.ts +829 -156
  18. package/dist/src/framework.d.ts.map +1 -1
  19. package/dist/src/framework.js +3906 -1008
  20. package/dist/src/framework.js.map +1 -1
  21. package/dist/src/gate/event-gate.d.ts +309 -46
  22. package/dist/src/gate/event-gate.d.ts.map +1 -1
  23. package/dist/src/gate/event-gate.js +2117 -395
  24. package/dist/src/gate/event-gate.js.map +1 -1
  25. package/dist/src/gate/index.d.ts +2 -2
  26. package/dist/src/gate/index.d.ts.map +1 -1
  27. package/dist/src/gate/index.js +1 -1
  28. package/dist/src/gate/index.js.map +1 -1
  29. package/dist/src/gate/recipe-reconcile.d.ts +103 -0
  30. package/dist/src/gate/recipe-reconcile.d.ts.map +1 -0
  31. package/dist/src/gate/recipe-reconcile.js +325 -0
  32. package/dist/src/gate/recipe-reconcile.js.map +1 -0
  33. package/dist/src/gate/types.d.ts +68 -8
  34. package/dist/src/gate/types.d.ts.map +1 -1
  35. package/dist/src/index.d.ts +6 -4
  36. package/dist/src/index.d.ts.map +1 -1
  37. package/dist/src/index.js +5 -2
  38. package/dist/src/index.js.map +1 -1
  39. package/dist/src/mcpl/channel-registry.d.ts +433 -113
  40. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  41. package/dist/src/mcpl/channel-registry.js +1152 -434
  42. package/dist/src/mcpl/channel-registry.js.map +1 -1
  43. package/dist/src/mcpl/conversation-router.d.ts +16 -8
  44. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  45. package/dist/src/mcpl/conversation-router.js +29 -15
  46. package/dist/src/mcpl/conversation-router.js.map +1 -1
  47. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  48. package/dist/src/mcpl/convert-block.js +14 -2
  49. package/dist/src/mcpl/convert-block.js.map +1 -1
  50. package/dist/src/mcpl/hook-orchestrator.d.ts +7 -1
  51. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  52. package/dist/src/mcpl/hook-orchestrator.js +32 -7
  53. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  54. package/dist/src/mcpl/index.d.ts +5 -1
  55. package/dist/src/mcpl/index.d.ts.map +1 -1
  56. package/dist/src/mcpl/index.js +4 -0
  57. package/dist/src/mcpl/index.js.map +1 -1
  58. package/dist/src/mcpl/inference-router.d.ts +14 -2
  59. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  60. package/dist/src/mcpl/inference-router.js +73 -28
  61. package/dist/src/mcpl/inference-router.js.map +1 -1
  62. package/dist/src/mcpl/prose-grammar.d.ts +4 -0
  63. package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
  64. package/dist/src/mcpl/prose-grammar.js +6 -0
  65. package/dist/src/mcpl/prose-grammar.js.map +1 -1
  66. package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
  67. package/dist/src/mcpl/protocol-family.js +3 -0
  68. package/dist/src/mcpl/protocol-family.js.map +1 -1
  69. package/dist/src/mcpl/push-coalescer.d.ts +20 -2
  70. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  71. package/dist/src/mcpl/push-coalescer.js +33 -11
  72. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  73. package/dist/src/mcpl/reference-fetcher.d.ts +63 -42
  74. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  75. package/dist/src/mcpl/reference-fetcher.js +177 -145
  76. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  77. package/dist/src/mcpl/references.d.ts +380 -46
  78. package/dist/src/mcpl/references.d.ts.map +1 -1
  79. package/dist/src/mcpl/references.js +737 -78
  80. package/dist/src/mcpl/references.js.map +1 -1
  81. package/dist/src/mcpl/server-connection.d.ts +82 -3
  82. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  83. package/dist/src/mcpl/server-connection.js +132 -7
  84. package/dist/src/mcpl/server-connection.js.map +1 -1
  85. package/dist/src/mcpl/tool-lifecycle.d.ts +11 -0
  86. package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
  87. package/dist/src/mcpl/tool-lifecycle.js +21 -0
  88. package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
  89. package/dist/src/mcpl/tool-presence.d.ts +259 -0
  90. package/dist/src/mcpl/tool-presence.d.ts.map +1 -0
  91. package/dist/src/mcpl/tool-presence.js +628 -0
  92. package/dist/src/mcpl/tool-presence.js.map +1 -0
  93. package/dist/src/mcpl/tool-result-normalize.d.ts +9 -3
  94. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
  95. package/dist/src/mcpl/tool-result-normalize.js +14 -6
  96. package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
  97. package/dist/src/mcpl/tool-signature.d.ts +39 -0
  98. package/dist/src/mcpl/tool-signature.d.ts.map +1 -0
  99. package/dist/src/mcpl/tool-signature.js +292 -0
  100. package/dist/src/mcpl/tool-signature.js.map +1 -0
  101. package/dist/src/mcpl/toolset-host.d.ts +200 -0
  102. package/dist/src/mcpl/toolset-host.d.ts.map +1 -0
  103. package/dist/src/mcpl/toolset-host.js +621 -0
  104. package/dist/src/mcpl/toolset-host.js.map +1 -0
  105. package/dist/src/mcpl/toolsets-module.d.ts +80 -0
  106. package/dist/src/mcpl/toolsets-module.d.ts.map +1 -0
  107. package/dist/src/mcpl/toolsets-module.js +431 -0
  108. package/dist/src/mcpl/toolsets-module.js.map +1 -0
  109. package/dist/src/mcpl/transport.d.ts +2 -2
  110. package/dist/src/mcpl/transport.d.ts.map +1 -1
  111. package/dist/src/mcpl/transport.js +3 -2
  112. package/dist/src/mcpl/transport.js.map +1 -1
  113. package/dist/src/mcpl/types.d.ts +21 -1
  114. package/dist/src/mcpl/types.d.ts.map +1 -1
  115. package/dist/src/mcpl/types.js.map +1 -1
  116. package/dist/src/mcpl/visible-content.d.ts +7 -9
  117. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  118. package/dist/src/mcpl/visible-content.js +21 -9
  119. package/dist/src/mcpl/visible-content.js.map +1 -1
  120. package/dist/src/module-registry.d.ts +23 -8
  121. package/dist/src/module-registry.d.ts.map +1 -1
  122. package/dist/src/module-registry.js +68 -15
  123. package/dist/src/module-registry.js.map +1 -1
  124. package/dist/src/modules/discord/index.js +1 -1
  125. package/dist/src/modules/discord/index.js.map +1 -1
  126. package/dist/src/modules/history/index.d.ts +2 -1
  127. package/dist/src/modules/history/index.d.ts.map +1 -1
  128. package/dist/src/modules/history/index.js +51 -17
  129. package/dist/src/modules/history/index.js.map +1 -1
  130. package/dist/src/modules/history/semantic.d.ts +108 -6
  131. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  132. package/dist/src/modules/history/semantic.js +273 -57
  133. package/dist/src/modules/history/semantic.js.map +1 -1
  134. package/dist/src/modules/workspace/direct-write.d.ts +181 -0
  135. package/dist/src/modules/workspace/direct-write.d.ts.map +1 -0
  136. package/dist/src/modules/workspace/direct-write.js +384 -0
  137. package/dist/src/modules/workspace/direct-write.js.map +1 -0
  138. package/dist/src/modules/workspace/edit-text.d.ts +26 -12
  139. package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
  140. package/dist/src/modules/workspace/edit-text.js +58 -28
  141. package/dist/src/modules/workspace/edit-text.js.map +1 -1
  142. package/dist/src/modules/workspace/effects.d.ts +10 -0
  143. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  144. package/dist/src/modules/workspace/effects.js +1 -1
  145. package/dist/src/modules/workspace/effects.js.map +1 -1
  146. package/dist/src/modules/workspace/index.d.ts +246 -27
  147. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  148. package/dist/src/modules/workspace/index.js +1216 -236
  149. package/dist/src/modules/workspace/index.js.map +1 -1
  150. package/dist/src/modules/workspace/read-page.d.ts +41 -0
  151. package/dist/src/modules/workspace/read-page.d.ts.map +1 -0
  152. package/dist/src/modules/workspace/read-page.js +213 -0
  153. package/dist/src/modules/workspace/read-page.js.map +1 -0
  154. package/dist/src/modules/workspace/result-text.d.ts +67 -0
  155. package/dist/src/modules/workspace/result-text.d.ts.map +1 -0
  156. package/dist/src/modules/workspace/result-text.js +94 -0
  157. package/dist/src/modules/workspace/result-text.js.map +1 -0
  158. package/dist/src/modules/workspace/text.d.ts +29 -12
  159. package/dist/src/modules/workspace/text.d.ts.map +1 -1
  160. package/dist/src/modules/workspace/text.js +35 -33
  161. package/dist/src/modules/workspace/text.js.map +1 -1
  162. package/dist/src/modules/workspace/types.d.ts +41 -17
  163. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  164. package/dist/src/modules/workspace/types.js.map +1 -1
  165. package/dist/src/sent-rows.d.ts +59 -0
  166. package/dist/src/sent-rows.d.ts.map +1 -0
  167. package/dist/src/sent-rows.js +145 -0
  168. package/dist/src/sent-rows.js.map +1 -0
  169. package/dist/src/silent-heartbeat.d.ts +17 -10
  170. package/dist/src/silent-heartbeat.d.ts.map +1 -1
  171. package/dist/src/silent-heartbeat.js.map +1 -1
  172. package/dist/src/stable-hash.d.ts +3 -0
  173. package/dist/src/stable-hash.d.ts.map +1 -0
  174. package/dist/src/stable-hash.js +15 -0
  175. package/dist/src/stable-hash.js.map +1 -0
  176. package/dist/src/stored-injections.d.ts +99 -0
  177. package/dist/src/stored-injections.d.ts.map +1 -0
  178. package/dist/src/stored-injections.js +106 -0
  179. package/dist/src/stored-injections.js.map +1 -0
  180. package/dist/src/text-as-stored.d.ts +4 -3
  181. package/dist/src/text-as-stored.d.ts.map +1 -1
  182. package/dist/src/text-as-stored.js +4 -3
  183. package/dist/src/text-as-stored.js.map +1 -1
  184. package/dist/src/thinking-drops.d.ts +81 -0
  185. package/dist/src/thinking-drops.d.ts.map +1 -0
  186. package/dist/src/thinking-drops.js +103 -0
  187. package/dist/src/thinking-drops.js.map +1 -0
  188. package/dist/src/tool-image-ledger.d.ts +9 -8
  189. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  190. package/dist/src/tool-image-ledger.js +9 -8
  191. package/dist/src/tool-image-ledger.js.map +1 -1
  192. package/dist/src/tool-presentation.d.ts.map +1 -1
  193. package/dist/src/tool-presentation.js +3 -1
  194. package/dist/src/tool-presentation.js.map +1 -1
  195. package/dist/src/tool-result-guard.d.ts +4 -2
  196. package/dist/src/tool-result-guard.d.ts.map +1 -1
  197. package/dist/src/tool-result-guard.js +21 -6
  198. package/dist/src/tool-result-guard.js.map +1 -1
  199. package/dist/src/tool-result-history.d.ts +186 -21
  200. package/dist/src/tool-result-history.d.ts.map +1 -1
  201. package/dist/src/tool-result-history.js +428 -89
  202. package/dist/src/tool-result-history.js.map +1 -1
  203. package/dist/src/tune-out/coordinator.d.ts +17 -9
  204. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  205. package/dist/src/tune-out/coordinator.js +84 -37
  206. package/dist/src/tune-out/coordinator.js.map +1 -1
  207. package/dist/src/types/agent.d.ts +186 -7
  208. package/dist/src/types/agent.d.ts.map +1 -1
  209. package/dist/src/types/events.d.ts +19 -2
  210. package/dist/src/types/events.d.ts.map +1 -1
  211. package/dist/src/types/framework.d.ts +24 -3
  212. package/dist/src/types/framework.d.ts.map +1 -1
  213. package/dist/src/types/module.d.ts +90 -7
  214. package/dist/src/types/module.d.ts.map +1 -1
  215. package/dist/src/types/trace.d.ts +57 -2
  216. package/dist/src/types/trace.d.ts.map +1 -1
  217. package/package.json +4 -3
  218. package/src/agent.ts +593 -60
  219. package/src/api/server.ts +1 -2
  220. package/src/auto-continue.ts +42 -0
  221. package/src/code-execution/isolate/scripts.ts +11 -3
  222. package/src/framework.ts +4004 -1026
  223. package/src/gate/event-gate.ts +2315 -389
  224. package/src/gate/index.ts +2 -1
  225. package/src/gate/recipe-reconcile.ts +355 -0
  226. package/src/gate/types.ts +70 -8
  227. package/src/index.ts +7 -4
  228. package/src/mcpl/channel-registry.ts +1220 -467
  229. package/src/mcpl/conversation-router.ts +34 -16
  230. package/src/mcpl/convert-block.ts +14 -1
  231. package/src/mcpl/hook-orchestrator.ts +33 -6
  232. package/src/mcpl/index.ts +6 -1
  233. package/src/mcpl/inference-router.ts +79 -34
  234. package/src/mcpl/prose-grammar.ts +7 -0
  235. package/src/mcpl/protocol-family.ts +2 -0
  236. package/src/mcpl/push-coalescer.ts +51 -13
  237. package/src/mcpl/reference-fetcher.ts +201 -162
  238. package/src/mcpl/references.ts +852 -78
  239. package/src/mcpl/server-connection.ts +145 -7
  240. package/src/mcpl/tool-lifecycle.ts +21 -0
  241. package/src/mcpl/tool-presence.ts +650 -0
  242. package/src/mcpl/tool-result-normalize.ts +18 -6
  243. package/src/mcpl/tool-signature.ts +285 -0
  244. package/src/mcpl/toolset-host.ts +637 -0
  245. package/src/mcpl/toolsets-module.ts +451 -0
  246. package/src/mcpl/transport.ts +3 -2
  247. package/src/mcpl/types.ts +22 -1
  248. package/src/mcpl/visible-content.ts +22 -9
  249. package/src/module-registry.ts +77 -24
  250. package/src/modules/discord/index.ts +1 -1
  251. package/src/modules/history/index.ts +48 -18
  252. package/src/modules/history/semantic.ts +308 -54
  253. package/src/modules/workspace/direct-write.ts +425 -0
  254. package/src/modules/workspace/edit-text.ts +82 -35
  255. package/src/modules/workspace/effects.ts +1 -1
  256. package/src/modules/workspace/index.ts +1274 -241
  257. package/src/modules/workspace/read-page.ts +222 -0
  258. package/src/modules/workspace/result-text.ts +119 -0
  259. package/src/modules/workspace/text.ts +58 -32
  260. package/src/modules/workspace/types.ts +41 -17
  261. package/src/sent-rows.ts +162 -0
  262. package/src/silent-heartbeat.ts +17 -10
  263. package/src/stable-hash.ts +14 -0
  264. package/src/stored-injections.ts +131 -0
  265. package/src/text-as-stored.ts +4 -3
  266. package/src/thinking-drops.ts +141 -0
  267. package/src/tool-image-ledger.ts +9 -8
  268. package/src/tool-presentation.ts +3 -1
  269. package/src/tool-result-guard.ts +19 -6
  270. package/src/tool-result-history.ts +488 -88
  271. package/src/tune-out/coordinator.ts +80 -37
  272. package/src/types/agent.ts +189 -7
  273. package/src/types/events.ts +19 -2
  274. package/src/types/framework.ts +21 -3
  275. package/src/types/module.ts +86 -7
  276. package/src/types/trace.ts +58 -2
@@ -62,7 +62,7 @@ export interface TuneOutParams {
62
62
  * registry). undefined when no decision names it.
63
63
  */
64
64
  export declare function desiredStateInLog(store: JsStore, serverId: string, channelId: string): DesiredChannelState | undefined;
65
- /** A registered channel entry, keyed by `{serverId}:{channelId}`. */
65
+ /** A registered channel entry, keyed by its server and id (destinationKey). */
66
66
  interface ChannelEntry {
67
67
  serverId: string;
68
68
  descriptor: ChannelDescriptor;
@@ -131,17 +131,35 @@ export declare const NON_HOLDER_CHANNEL_DESCRIPTIONS: Readonly<Record<'channel_o
131
131
  * PublishOutcome keeps the connector's structured data (`detail`) as the
132
132
  * connection gives it, its strings redacted (redactedData). */
133
133
  export declare function connectorText(value: unknown): string;
134
- /** Where a publish was resolved to go — named by the registry, never by
135
- * the caller's spelling. */
136
- export interface PublishDestination {
134
+ /**
135
+ * A registered channel, by its server and its id. MCPL requires a channel id
136
+ * to be unique only within one server's connection (§14.2), so two servers
137
+ * can each register `general`, and only the pair names one of them.
138
+ */
139
+ export interface ChannelRef {
137
140
  serverId: string;
138
141
  channelId: string;
142
+ }
143
+ /** Where a publish was resolved to go — named by the registry, never by
144
+ * the caller's spelling. */
145
+ export interface PublishDestination extends ChannelRef {
139
146
  /** The channel's registered label when the attempt was made. */
140
147
  label?: string;
141
148
  }
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;
149
+ /** One key per channel, its server and id: unlike joining the two with a
150
+ * separator, no two channels share one, whatever their ids hold. */
151
+ export declare function destinationKey(to: ChannelRef): string;
152
+ /** Whether two refs name the same channel. Two absent refs are the same:
153
+ * neither names a channel. */
154
+ export declare function sameChannel(a: ChannelRef | null | undefined, b: ChannelRef | null | undefined): boolean;
155
+ /**
156
+ * A channel's server-qualified name, `serverId:channelId`. A name given as a
157
+ * string (a tool's channelId, a `>>` target, a host's call) may take this
158
+ * form to pick one server's channel where several servers register the id.
159
+ * It is looked up among the registered pairs, never split at a colon, since
160
+ * ids hold colons (ChannelRegistry.entriesNamed).
161
+ */
162
+ export declare function qualifiedChannelName(ref: ChannelRef): string;
145
163
  /**
146
164
  * What one channels/publish attempt established (ChannelRegistry.publish).
147
165
  * - `delivered`: the connector returned `delivered: true`. A historical
@@ -173,7 +191,9 @@ export interface PublishOutcome {
173
191
  /**
174
192
  * Why the destination refused a publishForAgent before anything went out.
175
193
  * 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
194
+ * registered here, the name given means more than one registered channel
195
+ * (`shared-id`: a ref picks one, as a server-qualified name does where it names
196
+ * one channel alone), it takes no posts, or its server is gone, this host is
177
197
  * connecting to it, it's reconnecting, or it's up without channels.publish
178
198
  * (serverReach); or the request wasn't sent, as its connection refused it
179
199
  * before anything was written (McplRequestError's `not-sent`). A refusal of
@@ -181,7 +201,7 @@ export interface PublishOutcome {
181
201
  * and so does a publish that went out, whatever became of it: its words say
182
202
  * whether it may have been posted.
183
203
  */
184
- export type PublishRefusalReason = 'channel-not-registered' | 'inbound-only' | 'server-gone' | 'server-connecting' | 'server-down' | 'not-permitted' | 'not-sent';
204
+ export type PublishRefusalReason = 'channel-not-registered' | 'shared-id' | 'inbound-only' | 'server-gone' | 'server-connecting' | 'server-down' | 'not-permitted' | 'not-sent';
185
205
  /** A tune-out's open answered for a tune-out that ended, or that a later
186
206
  * decision or a later tune-out replaced, while the open was out: by
187
207
  * openForTuneOut, and by a repeated tune_out whose tune-out isn't the one it
@@ -229,6 +249,8 @@ interface ChannelRegistryOptions {
229
249
  onRouteFailure?: (info: {
230
250
  conversationId: string;
231
251
  channelId: string | null;
252
+ /** The channel's server, beside `channelId` whenever the failure names a channel. */
253
+ serverId?: string;
232
254
  reason: string;
233
255
  textLen: number;
234
256
  /** `failed`: nothing was posted; `unknown`: the request was dispatched
@@ -270,7 +292,7 @@ interface ChannelRegistryOptions {
270
292
  * whichever channel last spoke (item 3). Returns undefined for the trunk/primary agent, which has
271
293
  * no home and correctly falls back to the global locus (heartbeats, etc.).
272
294
  */
273
- homeChannelResolver?: (agentName: string) => string | undefined;
295
+ homeChannelResolver?: (agentName: string) => ChannelRef | undefined;
274
296
  /**
275
297
  * Resolve an agent's channel for its CURRENT inference turn, by agent name:
276
298
  * the turn's locus once it has one, else the channel that triggered the
@@ -285,9 +307,11 @@ interface ChannelRegistryOptions {
285
307
  * turn's locus is resolved, so that
286
308
  * resolution takes the global fallback, which the turn then keeps.
287
309
  * Consulted AFTER `homeChannelResolver` (a fork's home always wins), BEFORE
288
- * `defaultPublishChannel`.
310
+ * `defaultPublishChannel`. Null where the turn deliberately has no channel,
311
+ * as one woken for a channel id two servers register, given without its
312
+ * server: that takes no fallback either.
289
313
  */
290
- activeChannelResolver?: (agentName: string) => string | undefined;
314
+ activeChannelResolver?: (agentName: string) => ChannelRef | null | undefined;
291
315
  /**
292
316
  * Whether a conversation takes the process-global fallback, the channel of
293
317
  * the most recent delivered inbound message: as where its plain speech lands
@@ -323,11 +347,23 @@ export type ChannelToolOrigin = {
323
347
  } | {
324
348
  kind: 'agent';
325
349
  agentName: string;
350
+ /** For a call made inside one of the agent's turns, that turn's own
351
+ * channels, every channel that has been its trigger or its locus: a
352
+ * name one of them answers to by its id means that channel
353
+ * (ChannelRegistry.ownChannelNamed). */
354
+ own?: readonly ChannelRef[];
326
355
  };
327
356
  /** The machine decision sources a module-origin channel_close may record.
328
357
  * Closed on purpose: honest provenance means a small, auditable vocabulary,
329
358
  * not arbitrary self-description. */
330
359
  export declare const MACHINE_CLOSE_SOURCES: Set<string>;
360
+ /** A close a module made through its own tool call (ctx.callTool) without a
361
+ * machine source, such as connectome-host's mentions mode: a decision the
362
+ * module owns, with its own way back. Speech leaves it closed, as it does a
363
+ * channel the agent closed (speechOpens), but the agent isn't told how to
364
+ * open it, since channel_open alone would undo only part of what the
365
+ * module did. */
366
+ export declare const MODULE_CLOSE_SOURCE = "module-tool";
331
367
  export declare class ChannelRegistry {
332
368
  private serverRegistry;
333
369
  private featureSetManager;
@@ -342,7 +378,11 @@ export declare class ChannelRegistry {
342
378
  private takesGlobalFallback?;
343
379
  private serverConnecting?;
344
380
  private store?;
345
- /** Registered channels, keyed by `{serverId}:{channelId}`. */
381
+ /**
382
+ * Registered channels, keyed by their server and id (destinationKey): a
383
+ * key that joined the two with a separator would let server `a`'s channel
384
+ * `b:room` and server `a:b`'s channel `room` share one entry.
385
+ */
346
386
  private channels;
347
387
  /**
348
388
  * Each outgoing stream, by `inferenceId`, with the destinations it has
@@ -407,10 +447,12 @@ export declare class ChannelRegistry {
407
447
  /** The same message's id and thread, used for buildChannelContext. */
408
448
  private defaultPublishMessageId;
409
449
  private defaultPublishThreadId;
410
- /** Per-channel typing indicator timers. */
450
+ /** Per-channel typing indicator timers, by destinationKey, with the
451
+ * channel each one types in. */
411
452
  private typingIntervals;
412
- /** Per-channel typing metadata — carried on the 7s refresh so the target
413
- * server keeps getting the same routing hints (e.g. Zulip topic). */
453
+ /** Per-channel typing metadata, by destinationKey — carried on the 7s
454
+ * refresh so the target server keeps getting the same routing hints (e.g.
455
+ * Zulip topic). */
414
456
  private typingMetadata;
415
457
  /** Chronicle-projected desired lifecycle state, keyed by server + channel.
416
458
  * Provenance is kept so reconcile can tell a pure default (nobody ever
@@ -424,6 +466,7 @@ export declare class ChannelRegistry {
424
466
  private lifecycleOutstanding;
425
467
  private lifecycleQueuedRemovals;
426
468
  private unconfirmedChannelTargets;
469
+ private answeredElsewhere;
427
470
  private failedCloseTargets;
428
471
  private tuneOutOpens;
429
472
  private untargetedPushEntries;
@@ -436,7 +479,7 @@ export declare class ChannelRegistry {
436
479
  /** Whether a decided close holds words back from a channel: only where
437
480
  * the host directs the server's lifecycle. Without that grant the host
438
481
  * can't close the channel, and words go into a transport that is open,
439
- * as the open step's engagesDecidedClosed reads it too. */
482
+ * as the open step reads it too (speechOpens). */
440
483
  private closeStands;
441
484
  /** Where this server stands for a request this host would make of it
442
485
  * (ServerReach). One without a connection's state reads as connected.
@@ -555,32 +598,42 @@ export declare class ChannelRegistry {
555
598
  */
556
599
  ensureChannelRegistered(serverId: string, channelId: string, label?: string, extraMetadata?: Record<string, unknown>): void;
557
600
  /**
558
- * Start sending typing indicators for a channel.
601
+ * Start sending typing indicators for a channel: the channel a ref names,
602
+ * or each channel a name given as a string means (entriesNamed). A host
603
+ * names channels by the ids it keeps (connectome-host's activity module),
604
+ * and typing posts nothing, so a name two servers' channels answer to
605
+ * shows typing in both, rather than in neither or in whichever registered
606
+ * first. Where two servers reach one platform channel, two bots can then
607
+ * show typing there.
559
608
  *
560
609
  * Sends a typing notification immediately and every 7 seconds thereafter.
561
610
  * Discord typing indicators last ~10s, so 7s keeps them alive.
562
611
  *
563
- * No-op if already typing on this channel.
612
+ * No-op for a channel already typing, or not registered.
564
613
  */
565
- startTyping(channelId: string, metadata?: Record<string, unknown>): void;
614
+ startTyping(channel: string | ChannelRef, metadata?: Record<string, unknown>): void;
615
+ private startTypingAt;
566
616
  /**
567
617
  * Stop sending typing indicators.
568
618
  *
569
- * If channelId is specified, stops typing on that channel only.
570
- * If no channelId, stops all typing indicators.
571
- */
572
- stopTyping(channelId?: string): void;
619
+ * Given a ref, stops typing in that channel. Given a name as a string,
620
+ * stops typing in each channel the name means, by its id or its
621
+ * server-qualified name (entriesNamed), as startTyping started it: a
622
+ * channel types only while it's registered, since startTypingAt starts
623
+ * none that isn't and each removal stops its channel's typing. Given
624
+ * nothing, stops all typing indicators.
625
+ */
626
+ stopTyping(channel?: string | ChannelRef): void;
627
+ /** The explicit 'stop' for a channel whose typing ended, while its
628
+ * server still registers it. */
629
+ private sendTypingStop;
573
630
  /**
574
631
  * Get the default publish channel ID: the channel of the most recent
575
632
  * incoming message that isn't a reaction, reaction removal, edit or
576
633
  * deletion marker, apart from an edit the coalescer delivers on acceptance
577
634
  * as the agent's first view.
578
635
  */
579
- getDefaultPublishChannel(): string | null;
580
- /**
581
- * Get the descriptor for a channel by its channelId (first match across
582
- * servers). Used by the conversation router for DM classification.
583
- */
636
+ getDefaultPublishChannel(): ChannelRef | null;
584
637
  /**
585
638
  * RFC-006 §3.2: is `channelId` registered BY `serverId` through a server
586
639
  * declaration (channels/register, channels/changed) or an authorized open?
@@ -589,16 +642,13 @@ export declare class ChannelRegistry {
589
642
  * registration.
590
643
  */
591
644
  isDeclaredChannel(serverId: string, channelId: string): boolean;
592
- getDescriptor(channelId: string): ChannelDescriptor | undefined;
645
+ /** A registered channel's descriptor. Used by the conversation router for
646
+ * DM classification. */
647
+ getDescriptor(ref: ChannelRef): ChannelDescriptor | undefined;
593
648
  /** The registered label of `channelId` on exactly `serverId` (no
594
649
  * cross-server first match), or undefined when it has none. */
595
650
  getChannelLabel(serverId: string, channelId: string): string | undefined;
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;
651
+ isChannelOpen(ref: ChannelRef): boolean;
602
652
  getDesiredState(serverId: string, channelId: string): DesiredChannelState | undefined;
603
653
  /**
604
654
  * Get all open channels.
@@ -620,13 +670,42 @@ export declare class ChannelRegistry {
620
670
  * decision it actually is. Absent origin is treated as agent-origin (the
621
671
  * untrusted-safe default).
622
672
  */
623
- handleChannelToolCall(toolName: string, input: unknown, origin?: ChannelToolOrigin): Promise<ToolResult>;
624
- /**
625
- * Build channel context for inclusion in beforeInference params.
626
- *
627
- * Returns undefined if no channels are active.
628
- */
629
- buildChannelContext(agentName?: string): ChannelContext | undefined;
673
+ handleChannelToolCall(toolName: string, input: unknown, origin?: ChannelToolOrigin,
674
+ /** Filled with a channel_publish's outcome once its request is
675
+ * attempted, so the caller learns where it was delivered. */
676
+ seen?: {
677
+ outcome?: PublishOutcome;
678
+ }): Promise<ToolResult>;
679
+ /**
680
+ * The message the agent is replying to, with its channel: the most recent
681
+ * inbound message that isn't a reaction, reaction removal, edit or deletion
682
+ * marker, apart from an edit the coalescer delivers on acceptance as the
683
+ * agent's first view. Left process-global: per-channel inbound tracking
684
+ * (the right messageId for a fork's own channel) is a separate concern from
685
+ * the outbound routing fix. An ephemeral run has none (takesGlobalFallback),
686
+ * so a subagent's refusal marks no one else's channel. Nor has a turn that
687
+ * deliberately has no channel (activeChannelResolver's null): woken for a
688
+ * channel id two servers register, it isn't replying to the latest inbound
689
+ * message, which may be anywhere. A fork's turn always has a channel, its
690
+ * home, so it's taken to be replying to the latest inbound message as every
691
+ * fork's turn is, in explicit mode as in locus mode, where its locus is
692
+ * its home.
693
+ */
694
+ incomingFor(agentName?: string): {
695
+ channel: ChannelRef;
696
+ messageId: string;
697
+ threadId?: string;
698
+ } | undefined;
699
+ /**
700
+ * The channel context one server is told for an agent's inference
701
+ * (context/beforeInference, §14.4). Each server is told only of its own
702
+ * channels: ids are unique only within one server's connection (§14.2), so
703
+ * another server's channel named by its id alone could read as this
704
+ * server's own, and §14.4 leaves the field to the host. So `incoming` and
705
+ * `defaultOutgoing` appear only where that channel is `serverId`'s, and
706
+ * `candidates` lists only its open channels. Undefined when none applies.
707
+ */
708
+ buildChannelContext(agentName: string | undefined, serverId: string): ChannelContext | undefined;
630
709
  /**
631
710
  * Remove all channel state belonging to a single server. Called by the
632
711
  * framework when an MCPL server is disconnected at runtime, so a dead
@@ -926,8 +1005,16 @@ export declare class ChannelRegistry {
926
1005
  * the resident matches none. A refusal of the destination before anything
927
1006
  * went out carries its `reason` (PublishRefusalReason), so a caller can act
928
1007
  * on why nothing was posted without reading the words.
929
- */
930
- publishForAgent(channelId: string, text: string, agentName: string): Promise<{
1008
+ *
1009
+ * The channel is a ref, or a name given as a string, which resolves as
1010
+ * channel_publish's channelId does: one channel the name means, which may
1011
+ * be server-qualified, and a name two servers' channels answer to is
1012
+ * refused with them as candidates (`shared-id`). A host keeps channels by
1013
+ * their ids (connectome-host's notices module). Where several servers
1014
+ * register an id, a ref picks one server's channel, as a qualified name does
1015
+ * where it names one channel alone.
1016
+ */
1017
+ publishForAgent(channel: string | ChannelRef, text: string, agentName: string): Promise<{
931
1018
  success: boolean;
932
1019
  data?: unknown;
933
1020
  error?: string;
@@ -1019,25 +1106,121 @@ export declare class ChannelRegistry {
1019
1106
  private sendTypingNotification;
1020
1107
  /** Descriptors registered by one server — the §14.3 channels/list answer. */
1021
1108
  descriptorsForServer(serverId: string): ChannelDescriptor[];
1022
- /** Server id owning a registered channel (by descriptor id), or null. */
1023
- getChannelServerId(channelId: string): string | null;
1024
1109
  /**
1025
1110
  * The server that declared this channel inbound-only (isInboundOnly), or
1026
- * undefined when it isn't one. The notices name that server. A caller that
1027
- * knows the channel's server passes it; otherwise the channel is found by
1028
- * its id alone (findChannelEntry).
1029
- */
1030
- inboundOnlyServer(channelId: string, serverId?: string): string | undefined;
1031
- /**
1032
- * Find a channel entry by channelId (searches across all servers).
1033
- * Returns the first match.
1111
+ * undefined when it isn't one, or isn't registered. The notices name that
1112
+ * server.
1113
+ */
1114
+ inboundOnlyServer(ref: ChannelRef): string | undefined;
1115
+ /** A channel's entry, by its server and id: the map is keyed by destinationKey. */
1116
+ private entryAt;
1117
+ /**
1118
+ * The registered channels a name given as a string can mean (a tool's
1119
+ * channelId, a `>>` target, a host's call): those whose id is the name and
1120
+ * those whose server-qualified name (qualifiedChannelName) is the name,
1121
+ * taken together. The qualified form is matched against the registered
1122
+ * pairs, never split at a colon, since ids hold colons (discord-mcpl's are
1123
+ * `discord:<guild>:<channel>`). So a name that two entries answer to, in
1124
+ * either form, means both of them, and a caller that has to pick one
1125
+ * refuses it with both as candidates rather than taking the first.
1126
+ */
1127
+ private entriesNamed;
1128
+ /** The channels a name given as a string means (entriesNamed), by ref:
1129
+ * for a caller that has to settle a host's or a tool's name itself. */
1130
+ channelsNamed(name: string): ChannelRef[];
1131
+ /**
1132
+ * The name to show an agent for a channel: one that picks it alone, which
1133
+ * a channel tool's channelId takes back, and a `>>` target too where the
1134
+ * name is one (isProseTargetToken: a single token other than `skip_reply`,
1135
+ * which a `>>` line reads as private speech); proseTargetFor gives the
1136
+ * target to write. A registered
1137
+ * channel's is nameFor's: its id, or, where another server registers the
1138
+ * same id, its server-qualified name. A channel no string picks alone, as
1139
+ * where qualified names collide too, is shown by its id and its server, as
1140
+ * the refusals show it (channelOnServer), which no input takes for a name.
1141
+ * A channel no longer registered is shown by its id, or by its qualified
1142
+ * name, only where no registered channel takes that name, as a channel
1143
+ * tool reads it or a `>>` target does (by label or raw id too), so the
1144
+ * name can't send anything to another channel; else by its id and server.
1145
+ */
1146
+ nameShown(ref: ChannelRef): string;
1147
+ /**
1148
+ * A tool's refusal of a tuned-out channel, whose decision is the tune-out:
1149
+ * its cancel is what ends it (tuneOutEndText), and a decision recorded over
1150
+ * it would end it without that cancel, so the backlog it diverted would
1151
+ * never reach the agent.
1152
+ */
1153
+ private tunedOutRefusal;
1154
+ /**
1155
+ * How a tuned-out channel's tune-out ends, for the texts that tell an agent
1156
+ * channel_open doesn't change it: a tune_out cancel, by the name that
1157
+ * picks the channel (nameFor), or, where no name picks it alone, its own
1158
+ * end, since tune_out takes a name.
1159
+ * For channel_close, which refuses a tuned-out channel too, it adds that
1160
+ * the close comes after that.
1161
+ */
1162
+ /** How this channel's tune-out ends, as tune_out's own refusals say it
1163
+ * (tuneOutEndText), or undefined for a channel no longer registered. */
1164
+ tuneOutEnd(ref: ChannelRef): string | undefined;
1165
+ private tuneOutEndText;
1166
+ /**
1167
+ * A channel's name as nameShown gives it, quoted for a sentence: the name
1168
+ * in quotes, or the id-and-server form, whose parts are quoted already.
1169
+ */
1170
+ quotedShown(ref: ChannelRef): string;
1171
+ /**
1172
+ * The string that names exactly this entry by entriesNamed: its id when no
1173
+ * other entry answers to that (the id is unique), else its server-qualified
1174
+ * name when no other entry answers to that, else undefined. The last can
1175
+ * happen only where qualified names collide, as server `a`'s channel
1176
+ * `b:room` and server `a:b`'s channel `room` do; such a channel is named
1177
+ * only by its server and id apart.
1178
+ */
1179
+ private nameFor;
1180
+ /**
1181
+ * The refusal for a name that more than one registered channel answers
1182
+ * to (entriesNamed). Where each one has a name that picks it alone
1183
+ * (nameFor), as two servers' channels with one id do, it lists those.
1184
+ * Where one has none, the name reads two ways, as server `a`'s `b:room`
1185
+ * and server `a:b`'s `room` both answer to `a:b:room`, and the refusal
1186
+ * gives a line per channel, each with its own route, or, where the tool
1187
+ * has none to it, saying that no channelId reaches it alone.
1188
+ * `takesServerId` says whether the tool also takes a serverId to pick
1189
+ * with; `nothing` says what didn't happen, "nothing ran" unless given.
1190
+ */
1191
+ private sharedNameRefusal;
1192
+ /**
1193
+ * The refusal for a name more than one registered channel answers to, for
1194
+ * a tool outside the registry whose schema has no serverId (tune_out, the
1195
+ * subconscious's cancel_tuneout and deliver_summary), or undefined when the
1196
+ * name means one channel or none. `nothing` says what didn't happen,
1197
+ * "nothing ran" unless given.
1198
+ */
1199
+ sharedNameRefusalFor(name: string, nothing?: string): string | undefined;
1200
+ /**
1201
+ * Where a channel id given with a server that doesn't have it is, for that
1202
+ * refusal: the servers that register the id, each a serverId to call again
1203
+ * with, and leaving serverId out only where the id alone then names one
1204
+ * channel (entriesNamed), since another channel's qualified name can be
1205
+ * the same string. For a publish, whose schema has no serverId
1206
+ * (`takesServerId` false: a channel_publish given one the agent added, or
1207
+ * a host's publishForAgent given a ref), where the id is, by the names its
1208
+ * channelId takes (nameFor), and no advice to post there, since each is
1209
+ * another destination, perhaps another community's.
1210
+ */
1211
+ private elsewhereText;
1212
+ /**
1213
+ * The channel a channel tool names: the channel with id `channelId` on
1214
+ * `serverId` when the call gives a serverId, the exact pair, as every
1215
+ * selector that gives a server reads it; otherwise the one channel its
1216
+ * name means (entriesNamed), which may be server-qualified. A name more
1217
+ * than one channel answers to is refused with them as candidates, never
1218
+ * resolved to the first. Its callers are the tools that take a serverId
1219
+ * (channel_open, channel_close, channel_decline), so the refusal offers
1220
+ * one. `own` is the calling turn's own channels, for an agent's call made
1221
+ * inside it (ChannelToolOrigin.own).
1034
1222
  */
1035
- private findChannelEntry;
1036
1223
  private resolveToolChannelEntry;
1037
- /**
1038
- * Find the composite key for a channel by its channelId.
1039
- */
1040
- private findChannelKey;
1041
1224
  private handleToolList;
1042
1225
  /**
1043
1226
  * Record durable desired-open intent, then queue the channel's lifecycle
@@ -1097,7 +1280,10 @@ export declare class ChannelRegistry {
1097
1280
  * docs/explicit-prose-routing.md) to a registered channel.
1098
1281
  *
1099
1282
  * Accepted spellings, tried in order:
1100
- * 1. exact descriptor id (`discord:guild:123`, always unambiguous)
1283
+ * 1. exact descriptor id (`discord:guild:123`), or its server-qualified
1284
+ * name (`a:discord:guild:123`), as entriesNamed matches them; a name
1285
+ * two channels answer to, such as an id two servers register, is
1286
+ * refused with both as candidates
1101
1287
  * 2. exact raw server-internal id (`address.channelId`)
1102
1288
  * 3. `#label` / bare label — case-insensitive match on descriptor label
1103
1289
  * with any leading '#' stripped from both sides
@@ -1112,17 +1298,68 @@ export declare class ChannelRegistry {
1112
1298
  * names the channel as `refusedChannelId`, so a caller can tell a refused
1113
1299
  * channel from a name that resolved to nothing. Without it, as for reading
1114
1300
  * a channel's history, any registered channel resolves.
1301
+ *
1302
+ * `own` is the turn's own channels, for a target its speech names: a name
1303
+ * one of them answers to by its id means that channel, on its server,
1304
+ * before any other spelling (ownChannelNamed).
1115
1305
  */
1116
1306
  resolveProseTarget(spec: string, opts?: {
1117
1307
  forSpeech?: boolean;
1118
- }): {
1119
- channelId: string;
1120
- label?: string;
1121
- } | {
1308
+ own?: readonly ChannelRef[];
1309
+ }): PublishDestination | {
1122
1310
  error: string;
1123
1311
  candidates?: string[];
1124
1312
  refusedChannelId?: string;
1125
1313
  };
1314
+ /**
1315
+ * The turn's own channel a name means: where the name is the id of one of
1316
+ * the turn's own channels, a channel that has been its trigger or its
1317
+ * locus, that channel, on its server, whether or not that server still
1318
+ * registers it. A message's
1319
+ * reply goes where the message came from, so another server that
1320
+ * registers the id, or takes it once the message's server withdrew it,
1321
+ * isn't reached through the name. Where the turn's channels with that id
1322
+ * are on more than one server, the name means neither. Undefined where no
1323
+ * channel of the turn's has that id.
1324
+ */
1325
+ private ownChannelNamed;
1326
+ /** Why a name the turn's own channel answers to (ownChannelNamed) reaches
1327
+ * nothing once that channel's server no longer registers it. It offers no
1328
+ * other channel with the id, since that's another server's. */
1329
+ private ownChannelGoneText;
1330
+ /**
1331
+ * A name the calling turn's own channels answer to by their id, for a tool
1332
+ * outside the registry that takes a channel's name (tune_out, and the
1333
+ * subconscious's cancel_tuneout, speak_in_channel and deliver_summary),
1334
+ * read as the registry's channel tools read it: the turn's channel, or a
1335
+ * refusal where its server no longer registers it, or where the name is
1336
+ * the id of the turn's channels on more than one server. Undefined where
1337
+ * none of the turn's channels has that id. `tool` words the refusal for
1338
+ * what the tool would have done: posted (speak_in_channel), delivered
1339
+ * (deliver_summary) or run (the others).
1340
+ */
1341
+ ownChannelFor(name: string, own: readonly ChannelRef[] | undefined, tool?: 'publish' | 'deliver' | 'other'): {
1342
+ channel: ChannelRef;
1343
+ } | {
1344
+ error: string;
1345
+ } | undefined;
1346
+ /** A refusal of a name the turn's own channel answers to, once its server
1347
+ * no longer registers it: nothing was posted (channel_publish,
1348
+ * speak_in_channel), delivered (deliver_summary) or ran (the other channel
1349
+ * tools, tune_out, cancel_tuneout), and nothing can be until it does
1350
+ * again. */
1351
+ private ownChannelGoneRefusal;
1352
+ /**
1353
+ * The turn's channels a name means neither of (ownChannelNamed), each by a
1354
+ * string that reaches it outside the turn's rule: its server-qualified
1355
+ * name, where that picks it alone, since its id is the name refused. One
1356
+ * no string picks alone, and one its server no longer registers, is named
1357
+ * by its id and server, saying so; `reaches` says which a name reaches.
1358
+ */
1359
+ private ownCandidates;
1360
+ /** A tool's refusal of a name the turn's channels on more than one server
1361
+ * answer to by their id (ownChannelNamed's neither). */
1362
+ private ownNeitherRefusal;
1126
1363
  private resolveTargetByName;
1127
1364
  /**
1128
1365
  * The `>>` target to show an agent for a channel: one whitespace-free token
@@ -1131,22 +1368,98 @@ export declare class ChannelRegistry {
1131
1368
  * a space can't be quoted verbatim: `>>#DM: alice` parses as target `#DM:`
1132
1369
  * plus body `alice …`, and `>>#fable (antra's server)` delivers
1133
1370
  * `(antra's server)` as text. Tried in order: `@name` for a DM, `#label`,
1134
- * `#name` (label without its server suffix), the descriptor id.
1371
+ * `#name` (label without its server suffix), the name that picks the
1372
+ * channel alone (nameFor): its id, or, where another server registers the
1373
+ * same id, its server-qualified name; and last its server-qualified name,
1374
+ * for a channel whose id is no target (isProseTargetToken), as
1375
+ * `skip_reply` isn't.
1135
1376
  *
1136
- * Undefined when there is no safe token: the channel isn't registered (on
1137
- * `serverId`, when given), no candidate is whitespace-free and resolves back,
1138
- * or the id is registered by more than one server. A resolved target names a
1139
- * channel by id alone, and ids are unique only within a connection, so a
1140
- * shared id could route the reply through the wrong server.
1141
- */
1142
- proseTargetFor(channelId: string, serverId?: string): string | undefined;
1143
- /**
1144
- * Open a channel because something was DELIVERED into it (explicit send
1145
- * tool or routed speech). Sending into a closed channel is not a thing:
1146
- * engaging a channel opens it, so typing indicators, reaction machinery,
1147
- * and inbound forwarding all come alive with the first outbound message.
1148
- * Accepts composite MCPL ids or raw server-internal ids (explicit send
1149
- * tools receive whatever the agent typed). An `open-failed` answer says
1377
+ * Undefined when there is no safe token: the channel isn't registered, or
1378
+ * no candidate is whitespace-free and resolves back to this channel, on
1379
+ * this server.
1380
+ */
1381
+ proseTargetFor(ref: ChannelRef): string | undefined;
1382
+ /** Whether the decision on record is a close the agent or a module made
1383
+ * (isCloseDecision). */
1384
+ private closeDecided;
1385
+ /**
1386
+ * Whether the agent's words delivered into this channel open it first, by
1387
+ * publish() or after an explicit send (openIfClosedForSend): one that isn't
1388
+ * open, or whose close stands (closeStands: a close that failed may have
1389
+ * left its transport open), unless speech leaves it as it stands
1390
+ * (speechLeavesAsItStands). Where nobody decided (no record, a pure
1391
+ * default) or a machine closed the channel (subscription-gc,
1392
+ * housekeeping), it opens, as it always did, and so does one decided open
1393
+ * whose transport isn't confirmed. A tuned-out channel whose transport
1394
+ * isn't open is opened for its traffic, and its tune-out stays
1395
+ * (openChannelNow). A server that isn't up isn't one this host can't
1396
+ * direct, though a drop empties its grant until its next exchange, so
1397
+ * speech there is refused as it always was: by the open's failure, which
1398
+ * says the server is connecting or reconnecting, or, where its grant is
1399
+ * empty, by publish()'s grant check before any open.
1400
+ */
1401
+ speechOpens(ref: ChannelRef): boolean;
1402
+ /** Whether speech leaves this registration as it stands, never opening
1403
+ * it, and words go into it as it is: the decision on record is a close
1404
+ * the agent made itself (channel_close, channel_decline) or a module made
1405
+ * (MODULE_CLOSE_SOURCE), which stands against the agent's own speech
1406
+ * whatever the channel's transport is; or its server is up and this host
1407
+ * doesn't direct its lifecycle (lifecycleWithheld), so nothing opens
1408
+ * there. A wait or an open that failed gives way to such a registration,
1409
+ * as when the agent closed the channel while the words waited, since
1410
+ * there's nothing for the words to wait for (refusalGivesWay). */
1411
+ private speechLeavesAsItStands;
1412
+ /** Whether words a wait or an open refused go into the channel after all:
1413
+ * its registration `now` is one speech leaves as it stands
1414
+ * (speechLeavesAsItStands), such as one the agent closed or declined
1415
+ * while the words waited, and the failure isn't convergence that never
1416
+ * established its target (convergeFailure). A registration that went
1417
+ * still fails them, and publish() refuses one the server last answered
1418
+ * for another channel (answeredElsewhere), whenever that happened. */
1419
+ private refusalGivesWay;
1420
+ /** Whether this host doesn't direct the server's channel lifecycle: the
1421
+ * server is up, and its grant lacks channels.lifecycle, so nothing opens
1422
+ * or closes its channels. A server that isn't up has an empty grant from
1423
+ * a drop until its next exchange, which says nothing yet of what it
1424
+ * grants, so it isn't counted. */
1425
+ lifecycleWithheld(serverId: string): boolean;
1426
+ /** Whether the agent's words go into this channel and leave it closed: it
1427
+ * isn't open at the transport, and speech leaves it as it stands
1428
+ * (speechLeavesAsItStands: the agent's or a module's close, or a server
1429
+ * that's up without channels.lifecycle). False for a channel open at the
1430
+ * transport, whose traffic still flows, and for one speech opens. */
1431
+ speechLeavesClosed(ref: ChannelRef): boolean;
1432
+ /**
1433
+ * How the agent kept this registered channel closed itself, if it did:
1434
+ * 'closed' for its channel_close, 'declined' for its channel_decline. Its
1435
+ * speech there leaves such a channel as it stands (speechOpens). A
1436
+ * channel no longer registered has nothing to keep closed, whatever its
1437
+ * record says, so it answers undefined.
1438
+ */
1439
+ closedByAgent(ref: ChannelRef): 'closed' | 'declined' | undefined;
1440
+ /** The registered channels an explicit send tool may have delivered into,
1441
+ * from the id the tool reported or was given: a composite MCPL id, or the
1442
+ * raw server-internal id (`address.channelId`), since send tools receive
1443
+ * whatever the agent typed. Matches on `serverId` when it is given,
1444
+ * across every server otherwise. */
1445
+ private sendTargets;
1446
+ /** The one registered channel an explicit send tool delivered into
1447
+ * (sendTargets), or undefined when none or more than one matches. A
1448
+ * lookup only, for what's said beside the send's answer before it's
1449
+ * queued; openIfClosedForSend decides whether the send opens it. */
1450
+ resolveSendTarget(rawChannelId: string, serverId?: string): ChannelRef | undefined;
1451
+ /**
1452
+ * Open a channel because something was DELIVERED into it by an explicit
1453
+ * send tool, so typing indicators, reaction machinery and inbound
1454
+ * forwarding all come alive with the first outbound message, unless
1455
+ * speech leaves it as it stands (speechLeavesAsItStands: the agent closed
1456
+ * or declined it itself, a module closed it, or its server is up and this
1457
+ * host doesn't direct its lifecycle). Such a channel opens nothing, and
1458
+ * answers `stays-closed`, or `already-open` where its transport is open
1459
+ * with no close standing, whether the send found it so or it became so
1460
+ * while the send's wait or open was out.
1461
+ * Accepts composite MCPL ids or raw server-internal ids (sendTargets). An
1462
+ * `open-failed` answer says
1150
1463
  * whether the open that failed was one that would have opened a channel
1151
1464
  * not selected open (`opening`), as opposed to one reconfirming the
1152
1465
  * transport of a channel already selected open or tuned out. It isn't one
@@ -1159,12 +1472,19 @@ export declare class ChannelRegistry {
1159
1472
  * (`selected-again`).
1160
1473
  */
1161
1474
  openIfClosedForSend(rawChannelId: string, serverId?: string): Promise<{
1162
- status: 'opened' | 'already-open' | 'unknown-channel' | 'ambiguous' | 'open-failed';
1475
+ status: 'opened' | 'already-open' | 'stays-closed' | 'unknown-channel' | 'ambiguous' | 'open-failed';
1163
1476
  channelId?: string;
1477
+ /** The channel's server, beside `channelId` whenever it names one. */
1478
+ serverId?: string;
1164
1479
  label?: string;
1165
1480
  opening?: boolean;
1166
1481
  selectedForOpening?: SelectedForOpening;
1167
1482
  }>;
1483
+ /** What a send answers for a channel it leaves as it is
1484
+ * (speechLeavesAsItStands):
1485
+ * `already-open` where its transport is open with no close standing,
1486
+ * `stays-closed` otherwise. */
1487
+ private sendLeavesChannel;
1168
1488
  private handleToolOpen;
1169
1489
  private handleToolClose;
1170
1490
  private handleToolDecline;
@@ -1215,40 +1535,37 @@ export declare class ChannelRegistry {
1215
1535
  * inbound-only (#350's rule), not even for a publish delivered there just
1216
1536
  * before the declaration. One that started there before it goes on, since
1217
1537
  * the declaration doesn't unregister the channel: a later chunk is for a
1218
- * publish delivered before it. A bare channel id is resolved as before, to
1219
- * the one server that registers it, at each call (streamingServerFor), and
1220
- * keeps no record: a stream sent by destination ends by destination, since
1221
- * only a destination's `complete` removes its record.
1538
+ * publish delivered before it.
1222
1539
  */
1223
- sendOutgoingChunk(to: string | PublishDestination, conversationId: string, inferenceId: string, index: number, delta: string): void;
1540
+ sendOutgoingChunk(to: PublishDestination, conversationId: string, inferenceId: string, index: number, delta: string): void;
1224
1541
  /**
1225
1542
  * Spec 14.3 companion: final moderated content per channel at stream end.
1226
- * Given a destination, it goes only where the stream started
1227
- * (sendOutgoingChunk), while that server still registers that channel,
1228
- * inbound-only or not, and its grant has `channels.streaming`: once per
1229
- * destination, whose consumer, such as a TTS utterance, finalizes on it
1230
- * and reconciles any chunk it missed. A bare channel id is resolved as
1231
- * before (streamingServerFor).
1543
+ * It goes only where the stream started (sendOutgoingChunk), while that
1544
+ * server still registers that channel, inbound-only or not, and its grant
1545
+ * has `channels.streaming`: once per destination, whose consumer, such as
1546
+ * a TTS utterance, finalizes on it and reconciles any chunk it missed.
1232
1547
  */
1233
- sendOutgoingComplete(to: string | PublishDestination, conversationId: string, inferenceId: string, text: string): void;
1234
- private streamingServerFor;
1548
+ sendOutgoingComplete(to: PublishDestination, conversationId: string, inferenceId: string, text: string): void;
1235
1549
  /**
1236
1550
  * A destination's server, for a stream message: while that server
1237
- * registers that channel, and its grant has `channels.streaming` (as in
1238
- * streamingServerFor). A stream doesn't start in an inbound-only channel.
1551
+ * registers that channel, and its grant has `channels.streaming` (§5.4:
1552
+ * the grant gates streaming, not the raw advertisement). A stream doesn't
1553
+ * start in an inbound-only channel.
1239
1554
  */
1240
1555
  private streamingServerAt;
1241
1556
  /** Resolve the outbound locus (fork HOME → this-turn's TRIGGERING channel →
1242
- * process-global default). Public so a multi-segment caller can snapshot it
1243
- * ONCE and pin every segment to it via routeSpeech's `overrideChannelId`. */
1244
- resolveLocus(conversationId: string): string | null;
1557
+ * process-global default), with its server. Public so a multi-segment
1558
+ * caller can snapshot it ONCE and pin every segment to it via
1559
+ * routeSpeech's locus. */
1560
+ resolveLocus(conversationId: string): ChannelRef | null;
1245
1561
  /** The process-global fallback channel for this conversation, or null when it takes none (takesGlobalFallback). */
1246
1562
  private globalFallbackFor;
1247
1563
  /**
1248
1564
  * Resolve a publish target to exactly one registered channel: `serverId`
1249
- * plus `channelId` when the caller has the server, otherwise a channel id
1250
- * registered by exactly one server. A shared id is refused, never resolved
1251
- * to whichever server registered it first.
1565
+ * plus `channelId` when the caller has the server, otherwise the one
1566
+ * channel the id, or its server-qualified name, means (entriesNamed). A
1567
+ * shared id is refused, never resolved to whichever server registered it
1568
+ * first.
1252
1569
  */
1253
1570
  resolveDestination(target: {
1254
1571
  serverId?: string;
@@ -1268,16 +1585,19 @@ export declare class ChannelRegistry {
1268
1585
  * publishReceiptOutcome and publishErrorOutcome.
1269
1586
  *
1270
1587
  * Resolution is exact (resolveDestination): `serverId` plus `channelId`
1271
- * when the caller has the server, otherwise the channel id must be
1272
- * registered by exactly one server (a shared id is refused, never routed
1273
- * through whichever server came first). An omitted `serverId` means "not
1588
+ * when the caller has the server, otherwise the channel id, or its
1589
+ * server-qualified name, must mean exactly one channel (a shared id is
1590
+ * refused, never routed through whichever server came first). An omitted `serverId` means "not
1274
1591
  * in use"; a supplied one must be a non-empty server id, and an empty or
1275
1592
  * non-string selector is refused rather than read as omission. It is not
1276
- * possible to send into a closed channel: a closed but registered
1277
- * destination is opened first (`openSource` names why), once its server
1278
- * and that server's channels.publish grant are known to take the send, so
1279
- * a send refused before the open opens nothing; and a failed open sends
1280
- * nothing.
1593
+ * possible to send into a closed channel that speech opens (speechOpens):
1594
+ * it's opened first (`openSource` names why), once its server and that
1595
+ * server's channels.publish grant are known to take the send, so a send
1596
+ * refused before the open opens nothing; and a failed open sends nothing.
1597
+ * A channel speech leaves as it stands (speechLeavesAsItStands), one the
1598
+ * agent closed or declined itself or a module closed, or a closed one on
1599
+ * a server that's up but whose lifecycle this host doesn't direct, takes
1600
+ * the words as it is, once its queued lifecycle work has settled.
1281
1601
  *
1282
1602
  * A channel its server declared inbound-only (isInboundOnly) takes nothing
1283
1603
  * the host would post, whichever path the words took, a drafts resend
@@ -1335,7 +1655,7 @@ export declare class ChannelRegistry {
1335
1655
  * that used to live in discord-mcpl.
1336
1656
  */
1337
1657
  deliverSpeech(conversationId: string, text: string,
1338
- /** The turn's FROZEN locus, snapshotted once by the caller (resolveLocus
1658
+ /** The turn's FROZEN locus, with its server, snapshotted once by the caller (resolveLocus
1339
1659
  * at turn start) and passed to every segment of the turn. Mandatory:
1340
1660
  * there is deliberately no live-resolution fallback here — a late
1341
1661
  * dispatch resolving live can read the NEXT turn's trigger state or a
@@ -1344,7 +1664,7 @@ export declare class ChannelRegistry {
1344
1664
  * "this turn is pinned to no locus": fail loudly rather than guess,
1345
1665
  * except for an ephemeral run, whose speech is then its caller's
1346
1666
  * result. */
1347
- locusChannelId: string | null,
1667
+ locus: ChannelRef | null,
1348
1668
  /** `heldOnFailure`: the caller holds a `failed` delivery's words as a
1349
1669
  * draft with its own notice, so no failure marker is written for it.
1350
1670
  * An `unknown` outcome still gets its marker. */
@@ -1352,7 +1672,7 @@ export declare class ChannelRegistry {
1352
1672
  heldOnFailure?: boolean;
1353
1673
  }): Promise<PublishOutcome>;
1354
1674
  /** deliverSpeech, reduced to the confirmed delivery (null when there was none). */
1355
- routeSpeech(conversationId: string, text: string, locusChannelId: string | null): Promise<{
1675
+ routeSpeech(conversationId: string, text: string, locus: ChannelRef | null): Promise<{
1356
1676
  delivered: boolean;
1357
1677
  serverId: string;
1358
1678
  channelId: string;