@animalabs/agent-framework 0.22.4 → 0.23.1

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 (234) hide show
  1. package/README.md +105 -3
  2. package/dist/src/agent.d.ts +36 -10
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +76 -32
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/code-execution/index.d.ts +1 -1
  7. package/dist/src/code-execution/index.d.ts.map +1 -1
  8. package/dist/src/code-execution/index.js.map +1 -1
  9. package/dist/src/code-execution/py-runner.d.ts +34 -2
  10. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  11. package/dist/src/code-execution/py-runner.js +72 -16
  12. package/dist/src/code-execution/py-runner.js.map +1 -1
  13. package/dist/src/code-execution/runtime-py.d.ts +3 -2
  14. package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
  15. package/dist/src/code-execution/runtime-py.js +47 -7
  16. package/dist/src/code-execution/runtime-py.js.map +1 -1
  17. package/dist/src/code-execution/tool-definition.d.ts +12 -7
  18. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  19. package/dist/src/code-execution/tool-definition.js +59 -11
  20. package/dist/src/code-execution/tool-definition.js.map +1 -1
  21. package/dist/src/config-checks.d.ts +2 -2
  22. package/dist/src/config-checks.d.ts.map +1 -1
  23. package/dist/src/config-checks.js +6 -2
  24. package/dist/src/config-checks.js.map +1 -1
  25. package/dist/src/framework.d.ts +744 -120
  26. package/dist/src/framework.d.ts.map +1 -1
  27. package/dist/src/framework.js +3546 -906
  28. package/dist/src/framework.js.map +1 -1
  29. package/dist/src/gate/event-gate.d.ts.map +1 -1
  30. package/dist/src/gate/event-gate.js +13 -6
  31. package/dist/src/gate/event-gate.js.map +1 -1
  32. package/dist/src/held-writes.d.ts +16 -0
  33. package/dist/src/held-writes.d.ts.map +1 -0
  34. package/dist/src/held-writes.js +56 -0
  35. package/dist/src/held-writes.js.map +1 -0
  36. package/dist/src/index.d.ts +7 -3
  37. package/dist/src/index.d.ts.map +1 -1
  38. package/dist/src/index.js +8 -4
  39. package/dist/src/index.js.map +1 -1
  40. package/dist/src/mcpl/capability-grant.d.ts +4 -0
  41. package/dist/src/mcpl/capability-grant.d.ts.map +1 -1
  42. package/dist/src/mcpl/capability-grant.js +1 -1
  43. package/dist/src/mcpl/capability-grant.js.map +1 -1
  44. package/dist/src/mcpl/channel-registry.d.ts +95 -18
  45. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  46. package/dist/src/mcpl/channel-registry.js +578 -150
  47. package/dist/src/mcpl/channel-registry.js.map +1 -1
  48. package/dist/src/mcpl/convert-block.d.ts +2 -2
  49. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  50. package/dist/src/mcpl/convert-block.js +6 -7
  51. package/dist/src/mcpl/convert-block.js.map +1 -1
  52. package/dist/src/mcpl/index.d.ts +5 -1
  53. package/dist/src/mcpl/index.d.ts.map +1 -1
  54. package/dist/src/mcpl/index.js +3 -1
  55. package/dist/src/mcpl/index.js.map +1 -1
  56. package/dist/src/mcpl/inference-router.d.ts +4 -2
  57. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  58. package/dist/src/mcpl/inference-router.js +6 -7
  59. package/dist/src/mcpl/inference-router.js.map +1 -1
  60. package/dist/src/mcpl/log-value.d.ts +7 -0
  61. package/dist/src/mcpl/log-value.d.ts.map +1 -1
  62. package/dist/src/mcpl/log-value.js +9 -0
  63. package/dist/src/mcpl/log-value.js.map +1 -1
  64. package/dist/src/mcpl/modern-connection.d.ts +227 -0
  65. package/dist/src/mcpl/modern-connection.d.ts.map +1 -0
  66. package/dist/src/mcpl/modern-connection.js +1088 -0
  67. package/dist/src/mcpl/modern-connection.js.map +1 -0
  68. package/dist/src/mcpl/prefix-rule.d.ts +18 -0
  69. package/dist/src/mcpl/prefix-rule.d.ts.map +1 -0
  70. package/dist/src/mcpl/prefix-rule.js +25 -0
  71. package/dist/src/mcpl/prefix-rule.js.map +1 -0
  72. package/dist/src/mcpl/protocol-family.d.ts +179 -0
  73. package/dist/src/mcpl/protocol-family.d.ts.map +1 -0
  74. package/dist/src/mcpl/protocol-family.js +697 -0
  75. package/dist/src/mcpl/protocol-family.js.map +1 -0
  76. package/dist/src/mcpl/push-coalescer.d.ts +38 -19
  77. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  78. package/dist/src/mcpl/push-coalescer.js +52 -23
  79. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  80. package/dist/src/mcpl/reference-fetcher.d.ts +66 -15
  81. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  82. package/dist/src/mcpl/reference-fetcher.js +243 -76
  83. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  84. package/dist/src/mcpl/references.d.ts +63 -18
  85. package/dist/src/mcpl/references.d.ts.map +1 -1
  86. package/dist/src/mcpl/references.js +117 -32
  87. package/dist/src/mcpl/references.js.map +1 -1
  88. package/dist/src/mcpl/server-connection.d.ts +326 -30
  89. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  90. package/dist/src/mcpl/server-connection.js +995 -212
  91. package/dist/src/mcpl/server-connection.js.map +1 -1
  92. package/dist/src/mcpl/server-registry.d.ts +25 -5
  93. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  94. package/dist/src/mcpl/server-registry.js +51 -12
  95. package/dist/src/mcpl/server-registry.js.map +1 -1
  96. package/dist/src/mcpl/tool-result-normalize.d.ts +54 -0
  97. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -0
  98. package/dist/src/mcpl/tool-result-normalize.js +232 -0
  99. package/dist/src/mcpl/tool-result-normalize.js.map +1 -0
  100. package/dist/src/mcpl/transport.d.ts +56 -13
  101. package/dist/src/mcpl/transport.d.ts.map +1 -1
  102. package/dist/src/mcpl/transport.js +206 -70
  103. package/dist/src/mcpl/transport.js.map +1 -1
  104. package/dist/src/mcpl/types.d.ts +66 -20
  105. package/dist/src/mcpl/types.d.ts.map +1 -1
  106. package/dist/src/mcpl/types.js.map +1 -1
  107. package/dist/src/mcpl/visible-content.d.ts +2 -1
  108. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  109. package/dist/src/mcpl/visible-content.js +2 -1
  110. package/dist/src/mcpl/visible-content.js.map +1 -1
  111. package/dist/src/module-registry.d.ts +16 -6
  112. package/dist/src/module-registry.d.ts.map +1 -1
  113. package/dist/src/module-registry.js +65 -6
  114. package/dist/src/module-registry.js.map +1 -1
  115. package/dist/src/modules/api/index.d.ts +1 -1
  116. package/dist/src/modules/api/index.d.ts.map +1 -1
  117. package/dist/src/modules/api/index.js +3 -2
  118. package/dist/src/modules/api/index.js.map +1 -1
  119. package/dist/src/modules/discord/discord-js-client.d.ts +42 -2
  120. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
  121. package/dist/src/modules/discord/discord-js-client.js +268 -65
  122. package/dist/src/modules/discord/discord-js-client.js.map +1 -1
  123. package/dist/src/modules/discord/index.d.ts +27 -5
  124. package/dist/src/modules/discord/index.d.ts.map +1 -1
  125. package/dist/src/modules/discord/index.js +366 -76
  126. package/dist/src/modules/discord/index.js.map +1 -1
  127. package/dist/src/modules/discord/refusal.d.ts +14 -0
  128. package/dist/src/modules/discord/refusal.d.ts.map +1 -0
  129. package/dist/src/modules/discord/refusal.js +14 -0
  130. package/dist/src/modules/discord/refusal.js.map +1 -0
  131. package/dist/src/modules/discord/types.d.ts +17 -7
  132. package/dist/src/modules/discord/types.d.ts.map +1 -1
  133. package/dist/src/modules/health/index.d.ts.map +1 -1
  134. package/dist/src/modules/health/index.js +6 -2
  135. package/dist/src/modules/health/index.js.map +1 -1
  136. package/dist/src/modules/index.d.ts +1 -1
  137. package/dist/src/modules/index.d.ts.map +1 -1
  138. package/dist/src/modules/index.js +1 -1
  139. package/dist/src/modules/index.js.map +1 -1
  140. package/dist/src/modules/workspace/disk-agreement.d.ts +25 -2
  141. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
  142. package/dist/src/modules/workspace/disk-agreement.js +45 -5
  143. package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
  144. package/dist/src/modules/workspace/effects.d.ts +2 -1
  145. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  146. package/dist/src/modules/workspace/effects.js +19 -9
  147. package/dist/src/modules/workspace/effects.js.map +1 -1
  148. package/dist/src/modules/workspace/index.d.ts +214 -42
  149. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  150. package/dist/src/modules/workspace/index.js +667 -113
  151. package/dist/src/modules/workspace/index.js.map +1 -1
  152. package/dist/src/modules/workspace/observe.d.ts +240 -8
  153. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  154. package/dist/src/modules/workspace/observe.js +554 -24
  155. package/dist/src/modules/workspace/observe.js.map +1 -1
  156. package/dist/src/modules/workspace/types.d.ts +10 -1
  157. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  158. package/dist/src/modules/workspace/types.js.map +1 -1
  159. package/dist/src/modules/workspace/watcher.d.ts +42 -0
  160. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  161. package/dist/src/modules/workspace/watcher.js +130 -25
  162. package/dist/src/modules/workspace/watcher.js.map +1 -1
  163. package/dist/src/silent-heartbeat.d.ts +112 -39
  164. package/dist/src/silent-heartbeat.d.ts.map +1 -1
  165. package/dist/src/silent-heartbeat.js +100 -29
  166. package/dist/src/silent-heartbeat.js.map +1 -1
  167. package/dist/src/tool-args.d.ts +40 -0
  168. package/dist/src/tool-args.d.ts.map +1 -0
  169. package/dist/src/tool-args.js +207 -0
  170. package/dist/src/tool-args.js.map +1 -0
  171. package/dist/src/tool-presentation.d.ts.map +1 -1
  172. package/dist/src/tool-presentation.js +9 -2
  173. package/dist/src/tool-presentation.js.map +1 -1
  174. package/dist/src/types/agent.d.ts +27 -17
  175. package/dist/src/types/agent.d.ts.map +1 -1
  176. package/dist/src/types/events.d.ts +8 -0
  177. package/dist/src/types/events.d.ts.map +1 -1
  178. package/dist/src/types/framework.d.ts +17 -2
  179. package/dist/src/types/framework.d.ts.map +1 -1
  180. package/dist/src/types/module.d.ts +54 -10
  181. package/dist/src/types/module.d.ts.map +1 -1
  182. package/dist/src/types/trace.d.ts +2 -2
  183. package/dist/src/types/trace.d.ts.map +1 -1
  184. package/package.json +3 -4
  185. package/src/agent.ts +99 -36
  186. package/src/code-execution/index.ts +1 -0
  187. package/src/code-execution/py-runner.ts +104 -17
  188. package/src/code-execution/runtime-py.ts +47 -7
  189. package/src/code-execution/tool-definition.ts +65 -11
  190. package/src/config-checks.ts +4 -2
  191. package/src/framework.ts +3633 -956
  192. package/src/gate/event-gate.ts +14 -6
  193. package/src/held-writes.ts +59 -0
  194. package/src/index.ts +19 -4
  195. package/src/mcpl/capability-grant.ts +1 -1
  196. package/src/mcpl/channel-registry.ts +613 -160
  197. package/src/mcpl/convert-block.ts +15 -7
  198. package/src/mcpl/index.ts +14 -1
  199. package/src/mcpl/inference-router.ts +8 -7
  200. package/src/mcpl/log-value.ts +10 -0
  201. package/src/mcpl/modern-connection.ts +1182 -0
  202. package/src/mcpl/prefix-rule.ts +32 -0
  203. package/src/mcpl/protocol-family.ts +691 -0
  204. package/src/mcpl/push-coalescer.ts +67 -29
  205. package/src/mcpl/reference-fetcher.ts +288 -82
  206. package/src/mcpl/references.ts +123 -33
  207. package/src/mcpl/server-connection.ts +1050 -221
  208. package/src/mcpl/server-registry.ts +50 -12
  209. package/src/mcpl/tool-result-normalize.ts +294 -0
  210. package/src/mcpl/transport.ts +204 -62
  211. package/src/mcpl/types.ts +67 -20
  212. package/src/mcpl/visible-content.ts +2 -1
  213. package/src/module-registry.ts +68 -13
  214. package/src/modules/api/index.ts +3 -2
  215. package/src/modules/discord/discord-js-client.ts +288 -72
  216. package/src/modules/discord/index.ts +380 -77
  217. package/src/modules/discord/refusal.ts +13 -0
  218. package/src/modules/discord/types.ts +17 -9
  219. package/src/modules/health/index.ts +6 -2
  220. package/src/modules/index.ts +1 -1
  221. package/src/modules/workspace/disk-agreement.ts +46 -5
  222. package/src/modules/workspace/effects.ts +18 -7
  223. package/src/modules/workspace/index.ts +722 -124
  224. package/src/modules/workspace/observe.ts +591 -22
  225. package/src/modules/workspace/types.ts +10 -1
  226. package/src/modules/workspace/watcher.ts +126 -25
  227. package/src/silent-heartbeat.ts +184 -59
  228. package/src/tool-args.ts +213 -0
  229. package/src/tool-presentation.ts +9 -2
  230. package/src/types/agent.ts +27 -17
  231. package/src/types/events.ts +8 -0
  232. package/src/types/framework.ts +17 -2
  233. package/src/types/module.ts +55 -10
  234. package/src/types/trace.ts +2 -2
@@ -83,9 +83,15 @@ export interface HostModeStatus {
83
83
  /** Context writes (module events, api message.send) withheld because the
84
84
  * host is quiesced. Persisted while quiesced and flushed by resume(). */
85
85
  deferredWrites: number;
86
- /** Agents whose turn `abandon` could NOT cancel: the turn token is held but
87
- * no stream exists yet (still in hooks/compile) or ever will (puppet tool
88
- * turns). Present only after an abandon that left such turns behind. */
86
+ /** Agents whose turn `abandon` had not ended when its grace ran out: a
87
+ * turn token held with no stream to cancel and no check ahead to stop it
88
+ * at (a puppet tool turn, a store surgery's reservation, a turn finishing
89
+ * its last delivery), or a turn with no stream that hasn't reached its
90
+ * next check, such as one in a hook that hasn't returned. A turn whose
91
+ * stream the abandon cancelled isn't listed, even one still running when
92
+ * the grace runs out, as one whose tool-result guard waits on its speech
93
+ * may be. Present only after an abandon that left such turns behind, while
94
+ * the host stays quiesced. */
89
95
  unabandonable?: string[];
90
96
  }
91
97
  /**
@@ -203,21 +209,25 @@ export declare class AgentFramework {
203
209
  private stopPromise;
204
210
  private stopCompleted;
205
211
  private ownedStoreClosed;
206
- /** Whether the latest stop() stopped in time everything it waits on with
207
- * its deadline (an operator's operations, modules and MCPL servers),
208
- * whatever failed after that. create()'s release reads it to say whether
209
- * part of what a failed create() started may still be running. */
212
+ /** Whether the latest stop() got through its teardown with nothing failed:
213
+ * every step and every wait up to and including the one on awareness
214
+ * drains, whatever failed after that, in the storage work that finishes
215
+ * the stop. A step that threw may have left part of its work running, as
216
+ * a wait that ran out does. create()'s release reads it to say whether part
217
+ * of what a failed create() started may still be running. */
210
218
  private stopFinishedTeardown;
211
219
  /** An operator's operations in flight, and the config materializations they
212
220
  * start. stop() waits until this set is empty before it tears down, unless
213
221
  * its wait budget runs out. */
214
222
  private readonly operatorOperations;
215
223
  /** What a wait of stop()'s left running when its budget ran out, by its
216
- * promise: a stream it cancelled or an event loop still in a call. A later
224
+ * promise: a stream it cancelled or an event loop still in a call; and a
225
+ * stream whose cancel threw, which stop() doesn't wait for. A later
217
226
  * stop() waits for each again, beside what it finds, whatever has happened
218
227
  * since to the entry it was found under (a run's end removes its stream's,
219
- * a start() replaces the loop), so a budget never lets the store close
220
- * under work still running. Each leaves once it settles. */
228
+ * a start() replaces the loop), so neither a budget nor a cancel that
229
+ * threw lets the store close under work still running. Each leaves once it
230
+ * settles. */
221
231
  private readonly leftRunning;
222
232
  private membrane;
223
233
  private queue;
@@ -285,7 +295,6 @@ export declare class AgentFramework {
285
295
  /** Per-agent last inference activity (epoch ms), for /healthz + doctor tooling. */
286
296
  private lastInferenceAt;
287
297
  private processLoggingPersist;
288
- private processLoggingBroadcast;
289
298
  private activeStreams;
290
299
  /** Told once per inbound acceptance (mcpl/inbound-source.ts). An internal
291
300
  * seam, unset here: the receipt work (shelf-354) assigns it inside the
@@ -364,10 +373,14 @@ export declare class AgentFramework {
364
373
  private ephemeralRuns;
365
374
  /** Ephemeral namespaces/names are single-generation for this framework
366
375
  * lifetime. Reusing one while an old stream drains would let name-keyed
367
- * liveness/settlement state cross generations. */
376
+ * liveness/settlement state cross generations. Each name records what
377
+ * reserved it: an ephemeral run (createEphemeralAgent), which takes no
378
+ * global fallback even after its release, or a conversation fork. */
368
379
  private usedEphemeralAgentNames;
369
380
  /** One-shot generation tickets minted by createEphemeralAgent. */
370
381
  private ephemeralCandidates;
382
+ /** Warn only once per Agent instance after utilities are registered. */
383
+ private emptyUtilityPermissionWarnings;
371
384
  /** Physical ephemeral frames deliberately disposed by their run watchdog/caller.
372
385
  * The name is single-generation, so these frames still own their terminal
373
386
  * typing/outgoing close even after runEphemeralToCompletion deregisters them. */
@@ -394,7 +407,7 @@ export declare class AgentFramework {
394
407
  private refusalStreak;
395
408
  /** Per-agent count of consecutive refusal-driven rewinds in the current turn
396
409
  * chain (reset when a turn completes without a refusal). Bounds the auto
397
- * rewind loop — see refusalHandling + rewindTriggeringTurn. */
410
+ * rewind loop — see refusalHandling + shedNewestTurn. */
398
411
  private refusalRewinds;
399
412
  /** Per-agent count of poison-history rewinds performed by the hard-down
400
413
  * breaker (noteInferenceExhausted). Reset when a turn completes
@@ -461,7 +474,9 @@ export declare class AgentFramework {
461
474
  private unackedDeferredWrites;
462
475
  /** Monotonic order stamp for deferred writes: the durable queue is always
463
476
  * written, restored, drained and flushed in `seq` order, whatever the
464
- * pending/un-acked split — a re-deferred entry keeps its place. */
477
+ * pending/un-acked split — a re-deferred entry keeps its place. Boot
478
+ * recovery numbers the restored queue 1..N in that order, and the stamp
479
+ * goes on from N (restorePersistedDeferredWrites). */
465
480
  private deferredSeq;
466
481
  /**
467
482
  * Per target agent: the store's message count when the OLDEST currently
@@ -480,8 +495,38 @@ export declare class AgentFramework {
480
495
  * guessing from the newest message in the window.
481
496
  */
482
497
  private activeTurnTriggers;
498
+ /**
499
+ * The `[Continue]` prompt the current round of each turn follows, keyed by
500
+ * the turn's trigger. Each stream start sets it from what the Agent read in
501
+ * its request (`StartStreamResult.continuePrompt`), and a tool boundary
502
+ * that hands the live stream messages after the results clears it, since
503
+ * the next round follows those messages. `silentTurnRowMetadata` stamps the
504
+ * round's rows with it.
505
+ */
506
+ private readonly turnPrompts;
483
507
  /** The trigger of `agentName`'s turn in progress, if a turn is running. */
484
508
  getActiveTurnTrigger(agentName: string): InferenceRequest | undefined;
509
+ /**
510
+ * The window of the frame that holds an agent's turn with no stream to
511
+ * carry a stop and one of its checks ahead: a start, from its entry until
512
+ * it hands its turn to a stream or ends (its checks after the hooks, after
513
+ * the compile, at a failure and after a wait to retry its compile),
514
+ * driveStream's wait to retry a failed inference (its check after the
515
+ * wait), and the tool-result guard's wait for the agent's speech (its
516
+ * check after the wait, which the window lets the abandon reach once a
517
+ * caller's cancelStream() has left the agent holding no stream). Each
518
+ * frame opens a window of its own and closes it only while it
519
+ * is still the agent's, so a start that ends after a later frame opened
520
+ * one, such as its retry's stream waiting to retry, leaves that one open.
521
+ * An operator's abandon marks the window, and the frame's check collects
522
+ * the mark, which goes with the window. abortInference() and the abandon
523
+ * count a turn with no stream only here; a stop made anywhere else, as
524
+ * while an agent is idle, a turn finishes its last delivery or a provider
525
+ * hold has parked it, has no check to end it, and the next start drops
526
+ * it. Keyed by the Agent, so a replacement agent under the same name
527
+ * never shares one.
528
+ */
529
+ private checkAhead;
485
530
  /** Structured calls executed in the current logical turn. Unlike the
486
531
  * per-stream local this survives framework retries and budget restarts. */
487
532
  private logicalTurnToolCalls;
@@ -523,6 +568,9 @@ export declare class AgentFramework {
523
568
  * ephemeral agents have isolated slots and are their own only readers.
524
569
  */
525
570
  private readonly sharedSlotAgents;
571
+ /** The windows of forks and runs deregistered while the host was quiesced,
572
+ * by agent name; resume() releases them (releaseWindowNowOrAfterResume). */
573
+ private readonly releasesAfterResume;
526
574
  /** The coalescing commit in flight, if any (see commitCoalescingDurable). */
527
575
  private coalescingCommit;
528
576
  private inferenceRouter;
@@ -587,11 +635,20 @@ export declare class AgentFramework {
587
635
  private mcplToolRefreshPending;
588
636
  /** Maps tool prefix → serverId for dispatch routing. */
589
637
  private mcplPrefixMap;
590
- /** Namespaced tool name → the server whose tools/list produced it. Prefixes
591
- * can nest (`foo`, `foo--bar`), so a prefix match alone can name two. */
638
+ /** Namespaced tool name → the server whose tools/list produced it, from
639
+ * its first listing of the name. resolveMcplTool reads it first. */
592
640
  private mcplToolServers;
593
641
  /** Servers whose tools/list answered in the latest refresh. */
594
642
  private mcplListedServers;
643
+ /** refreshMcplTools' starts, and the start whose result was published
644
+ * last: a refresh that a later-started one has overtaken publishes
645
+ * nothing, since that one listed every server after it. */
646
+ private mcplToolsRefreshStarted;
647
+ private mcplToolsRefreshPublished;
648
+ /** How many times each server's connection has closed. A listing that
649
+ * began before its server's latest close is of a session that's over,
650
+ * and refreshMcplTools publishes none of it. */
651
+ private mcplServerCloses;
595
652
  /** Zero-match tool-pattern diagnostics (checkToolPatterns): set at the end
596
653
  * of create(), when the tool universe is first complete. */
597
654
  private toolPatternChecksArmed;
@@ -602,6 +659,27 @@ export declare class AgentFramework {
602
659
  private toolPatternKnownPrefixes;
603
660
  /** Maps serverId → McplServerConfig for prefix lookup. */
604
661
  private mcplServerConfigs;
662
+ /** Modern-MCP (2026-07-28) servers, by id; read through modernServers().
663
+ * They are kept apart from the MCPL registry on purpose: they have no
664
+ * grant, planes or server→host traffic, so the MCPL machinery (quiesce's
665
+ * plane pauses, awareness gates, policy) never sees them. They take part
666
+ * only through the tool paths, and their list changes reach agents
667
+ * through ordinary admission. */
668
+ private modernMcpConnections;
669
+ /** Connects and disconnects in flight, by server id (holdMcpServerId);
670
+ * created on first use, like modernMcpConnections. */
671
+ private mcpServerOps;
672
+ /** Each legacy connect in flight's own abort (connectHeldMcplServer):
673
+ * stop() and a disconnect of its id end it at once, its launch reaped,
674
+ * rather than waiting out its handshake. Created on first use, as the
675
+ * restart claims are (legacyConnectAborts()). */
676
+ private legacyConnectAbortMap;
677
+ /** The ids and tool prefixes restarts hold, each with the claim of the
678
+ * restart holding it, from the restart's checks through its connect
679
+ * (restartMcplServer): no other connect or restart takes them while the
680
+ * old server is torn down. Created on first use (restartClaims), like
681
+ * mcpServerOps. */
682
+ private mcpRestartClaims;
605
683
  /** Host capabilities advertised during the MCP handshake — stored so servers
606
684
  * can be connected at runtime (connectMcplServer) after initialization. */
607
685
  private mcplHostCapabilities;
@@ -639,9 +717,10 @@ export declare class AgentFramework {
639
717
  * Create and start the framework. A create() that fails releases what it
640
718
  * had acquired before its error reaches the caller, so that the caller can
641
719
  * try again in the same process: the framework it had begun is stopped, as
642
- * stop() stops one, but giving up after 30 s on each of stop()'s two cleanup
643
- * waits, for an operator's operations already running and then for its
644
- * modules and MCPL servers, and a store it opened is closed. A store the
720
+ * stop() stops one, but giving up after 30 s on each of stop()'s cleanup
721
+ * waits, for an operator's operations already running, then for its
722
+ * modules and MCPL servers, and then for awareness drains a failed startup
723
+ * barrier left running, and a store it opened is closed. A store the
645
724
  * caller passed in is left open.
646
725
  */
647
726
  static create(config: FrameworkConfig): Promise<AgentFramework>;
@@ -687,23 +766,38 @@ export declare class AgentFramework {
687
766
  private static joinDiagnostics;
688
767
  private printDiagnostics;
689
768
  /**
690
- * Start the event loop.
769
+ * Start the event loop. A start() on a running framework does nothing.
770
+ * Refused, with an error, from a stop() call until that stop() settles,
771
+ * since that stop would wait on the lifecycle a start() begins, or tear it
772
+ * down under it. What a start() after a settled stop() should do is #315's
773
+ * question.
691
774
  */
692
775
  start(): void;
693
776
  /**
694
- * Stop the event loop. After a failed or timed-out wait, storage stays open
695
- * and stop() can be called again. A timed-out wait lets teardown go on while
696
- * what it waited for may still act on what is being torn down.
777
+ * Stop the event loop. Until it settles, start() and addModule() are
778
+ * refused. Each step of the teardown runs whatever an earlier one did: one
779
+ * that throws, such as a host stream's cancel(), fails the stop() once the
780
+ * rest has run. When a step or a wait up to and including
781
+ * the wait on awareness drains fails or times out, storage stays open and
782
+ * stop() can be called again; one that fails after it, in the storage work
783
+ * that finishes the stop, fails the stop() without keeping storage open.
784
+ * A timed-out wait lets teardown go on while what it waited for may still
785
+ * act on what is being torn down.
697
786
  * @param teardownTimeoutMs Optional budget for each of stop()'s waits, in
698
787
  * turn: on the streams it cancels, on maintenance in flight (a pass or an
699
788
  * over-budget drain kick), on the event loop, on an operator's operations
700
- * already running, then on modules and MCPL together. With a budget, a
701
- * stop() can take up to five of them, besides the withheld-spill deadline
702
- * of a guard's write still under way. Omit to wait until each settles, as
703
- * required by existing module/child contracts. A wait whose budget runs out
704
- * fails the stop() with a ShutdownTimeoutError naming what it waited for;
705
- * with more than one, or beside another failure, stop() fails with an
706
- * AggregateError of them all. After it:
789
+ * already running, on modules and MCPL together, then on Discord awareness
790
+ * drains, which send nothing once stop() has begun, recording the call the
791
+ * close ended. With a budget, a stop() can take up to six of them, besides
792
+ * the withheld-spill deadline of a guard's write still under way. Omit to
793
+ * wait until each settles, as required by existing module/child contracts.
794
+ * A wait whose budget runs out fails the stop() with a ShutdownTimeoutError
795
+ * naming what it waited for; with more than one, or beside another failure,
796
+ * stop() fails with an AggregateError of them all. A stream whose cancel
797
+ * threw is treated as one whose wait ran out, whatever the budget, and the
798
+ * stop() fails with the cancel's error: that stop() doesn't wait for it,
799
+ * even with no budget, and a later stop() waits for it as for anything an
800
+ * earlier one left running (the last point below). After either:
707
801
  * - an agent still registered whose stream is still running keeps its
708
802
  * deferred writes queued, since the stream may still write. The stream's
709
803
  * own turn-end flush takes them when it settles, if the process lives on,
@@ -716,11 +810,18 @@ export declare class AgentFramework {
716
810
  * land in the run's namespace after them.
717
811
  * - an event loop still in a call, such as a module's onProcess, finishes
718
812
  * that call and the rest of its iteration, and then exits. A start()
719
- * made meanwhile runs its own loop beside that last iteration.
813
+ * made once that stop() has failed runs its own loop beside that last
814
+ * iteration.
720
815
  * - a later stop() waits again, within its own budget, for any stream or
721
816
  * loop this one left running, so the store never closes under it.
722
817
  */
723
818
  stop(teardownTimeoutMs?: number | undefined): Promise<void>;
819
+ /** What start() and addModule() pass before either begins work of a
820
+ * lifecycle. A stop() still running refuses both, from the moment it
821
+ * claims its attempt until that attempt settles. A failed stop() has
822
+ * cleared its attempt, unless it closed an owned store, which is refused
823
+ * first. A completed stop() is cleared, and the call goes on as before;
824
+ * whether a lifecycle should begin after a stop() is #315's question. */
724
825
  private resetCompletedStop;
725
826
  /** Register before invoking the body, so the set holds the operation for
726
827
  * its whole life: its synchronous trace callbacks can reenter stop(), and
@@ -733,6 +834,22 @@ export declare class AgentFramework {
733
834
  * queue (after a failed or completed stop()). The admission rule for every
734
835
  * operator entry point, the host/command listener included. */
735
836
  private stopHasBegun;
837
+ /** stop()'s first step for each connect still in flight, in either engine:
838
+ * end it, so it fails at once and its operation ends, its launch reaped,
839
+ * rather than holding stop()'s wait until its handshake's deadline. A
840
+ * modern connect's connection is registered before it starts and is
841
+ * closed; a legacy one's handshake, and the access provider a WebSocket
842
+ * dial waits for, are aborted (legacyConnectAborts). A modern close's
843
+ * outcome is the teardown's to report (closeModernMcpConnections). */
844
+ private endConnectsInFlight;
845
+ /** Wakers for the waits a connect makes that a stop() ends: see
846
+ * untilStopBegins. Made on first use. */
847
+ private stopWaiters;
848
+ /** `work`, or undefined once stop() has begun, whichever comes first: a
849
+ * connect's last step (its tool listing, which asks every server) needn't
850
+ * hold stop()'s wait for operator operations on a server that doesn't
851
+ * answer. The work goes on; the teardown's close ends what it waits for. */
852
+ private untilStopBegins;
736
853
  private refuseOperatorAfterStop;
737
854
  /** Wait in rounds until the set is empty: admitted work can still start work
738
855
  * of its own. All rounds share the caller's deadline rather than renewing
@@ -837,7 +954,9 @@ export declare class AgentFramework {
837
954
  * Wait an error policy's retry delay, all of it: the delay can be a
838
955
  * provider's retry-after, and a single timer past 2^31-1 ms fires almost at
839
956
  * once. Steps through longer waits; stop() wakes it so a shutting-down
840
- * framework is never left with a wait parked for days.
957
+ * framework is never left with a wait parked for days. The frame's window
958
+ * (checkWindow) carries a waker that ends the whole wait: a stop
959
+ * abortInference() records, or an operator's abandon, wakes it.
841
960
  */
842
961
  private waitForRetry;
843
962
  /**
@@ -968,7 +1087,10 @@ export declare class AgentFramework {
968
1087
  offTrace(listener: TraceEventListener): void;
969
1088
  private removeTraceRegistration;
970
1089
  /**
971
- * Add a module at runtime.
1090
+ * Add a module at runtime. Refused, with a rejection, while a stop() is
1091
+ * still running: a module added then would either be stopped by that stop
1092
+ * or, once the stop has taken its list of modules to stop, stay started
1093
+ * past it.
972
1094
  */
973
1095
  addModule(module: Module): Promise<void>;
974
1096
  /**
@@ -989,7 +1111,10 @@ export declare class AgentFramework {
989
1111
  */
990
1112
  getConversationRouter(): ConversationRouter | null;
991
1113
  /**
992
- * Abort an in-flight inference for an agent.
1114
+ * Abort an in-flight inference for an agent. True when there was one to
1115
+ * stop: a stream, or a turn with no stream and one of its checks ahead (its
1116
+ * hooks or compile, a wait to retry, the tool-result guard's wait), which
1117
+ * ends at that check, before it sends anything more.
993
1118
  */
994
1119
  abortInference(agentName: string, reason?: string): boolean;
995
1120
  /**
@@ -1015,9 +1140,10 @@ export declare class AgentFramework {
1015
1140
  * Terminal for a stop the framework itself made: stop()'s shutdown and an
1016
1141
  * operator's quiesce abandon, each recorded in frameworkCancelledStreams
1017
1142
  * before the cancel. A stream may report that cancel as `aborted`, as
1018
- * `error`, or by just closing, and each of those ends here, so the three
1019
- * cannot drift: settle the turn (an ephemeral run rejects with the stop's
1020
- * own text), an attributable inference-log terminal, ONE inference:aborted
1143
+ * `error`, by just closing, or by throwing, and each of those ends here
1144
+ * (through endFrameworkCancel()), so the four cannot drift: settle the
1145
+ * turn (an ephemeral run rejects with the stop's own text), an
1146
+ * attributable inference-log terminal, ONE inference:aborted
1021
1147
  * carrying the framework's reason, gate release. No [turn-interrupted]
1022
1148
  * marker, since nobody stopped the agent, and none of the failure
1023
1149
  * accounting that inference:failed / inference:exhausted drive.
@@ -1030,6 +1156,8 @@ export declare class AgentFramework {
1030
1156
  * Get all registered modules.
1031
1157
  */
1032
1158
  getAllModules(): Module[];
1159
+ /** Read the namespaced utility catalogue without adding first-class model tools. */
1160
+ getAllUtilities(): import('./types/index.js').ToolDefinition[];
1033
1161
  /**
1034
1162
  * Get all available tools from all modules and MCPL servers.
1035
1163
  */
@@ -1040,11 +1168,22 @@ export declare class AgentFramework {
1040
1168
  * a writable workspace mount for storage). */
1041
1169
  private referenceFetcherInstance?;
1042
1170
  private getReferenceFetcher;
1043
- /** Register every RFC-005 reference in a tool-result content array against
1044
- * its server, then eagerly fetch the ones inside the autofetch envelope
1045
- * (below the eager ceiling, connection origin — never worse than the
1046
- * server having inlined the bytes). Failures are non-fatal: the stub then
1047
- * says fetch_reference instead of a path. */
1171
+ /** Register every RFC-005 reference in an MCPL peer's tool-result content
1172
+ * against its server, then fetch by `mode`:
1173
+ * - `register`: nothing. An in-process caller (executeMcplToolCall) may
1174
+ * never show the result to an agent, and an eager fetch would spend the
1175
+ * server's fetch budget, which the agent's own fetch_reference needs, on
1176
+ * bytes nobody asked for. A stub later made of a registered reference is
1177
+ * bound, so fetch_reference can fetch it.
1178
+ * - `model`: eagerly fetch the ones inside the autofetch envelope (below
1179
+ * the eager ceiling, connection origin — never worse than the server
1180
+ * having inlined the bytes). Failures are non-fatal: the stub then says
1181
+ * fetch_reference instead of a path.
1182
+ * - `script`: as `model`, then fetch every reference ("eagerly lazy"): a
1183
+ * script receiving a result is the strongest available signal that the
1184
+ * bytes are wanted (scripts process, they do not browse), so the stub it
1185
+ * receives carries a workspace path it can open directly.
1186
+ * A uri the result calls expired is fetched in no mode. */
1048
1187
  private autoFetchReferences;
1049
1188
  /** Core agent_settings keys — extension keys must not collide with these. */
1050
1189
  private static readonly AGENT_SETTINGS_CORE_KEYS;
@@ -1084,7 +1223,7 @@ export declare class AgentFramework {
1084
1223
  * is open again for an instance kept with a store it doesn't own and that
1085
1224
  * is still open (start() doesn't restore stopped modules or MCPL
1086
1225
  * connections, and refuses an owned store that stop() closed). A start()
1087
- * made while a stop() is still draining isn't a supported order. */
1226
+ * made while a stop() is still running is refused. */
1088
1227
  private guardSpillsClosedFor;
1089
1228
  /** A new agent's guard: its persisted setting, the tool-results spill its
1090
1229
  * withheld originals are written to, and the set stop() waits on
@@ -1097,9 +1236,11 @@ export declare class AgentFramework {
1097
1236
  /** Each guard that may have queued storage work, once, by agent name: the
1098
1237
  * live agents' and every other that isn't idle. */
1099
1238
  private guardsWithStorageWork;
1239
+ /** `think` for one agent: its same-round policy, and `skip_reply` named only where the agent may call it. */
1100
1240
  private buildThinkTool;
1101
1241
  /** agent_settings tool definition with module extension fields merged in. */
1102
1242
  private buildAgentSettingsTool;
1243
+ private warnEmptyUtilityPermissions;
1103
1244
  private getToolsForAgent;
1104
1245
  /** Available is independent of presentation visibility; execution callers keep this surface. */
1105
1246
  private availableToolsForPresentation;
@@ -1472,11 +1613,12 @@ export declare class AgentFramework {
1472
1613
  /** previewAgentRuntimeSettings for an agent in hand: boot restore previews one not yet registered. */
1473
1614
  private previewRuntimeSettingsFor;
1474
1615
  /**
1475
- * Check if process logging is enabled.
1616
+ * Whether process events and their responses are written to Chronicle
1617
+ * (`processLogging.persist`). `process:completed` trace events are emitted
1618
+ * either way.
1476
1619
  */
1477
1620
  isProcessLoggingEnabled(): {
1478
1621
  persist: boolean;
1479
- broadcast: boolean;
1480
1622
  };
1481
1623
  /**
1482
1624
  * Get the underlying store.
@@ -1605,8 +1747,10 @@ export declare class AgentFramework {
1605
1747
  /**
1606
1748
  * Create an ephemeral agent that is NOT registered in the main event loop.
1607
1749
  *
1608
- * Used by SubagentModule (and similar) to create short-lived agents
1609
- * that are driven externally (not by the framework's event loop).
1750
+ * Used by SubagentModule (and similar) to prepare short-lived agents.
1751
+ * Call runEphemeralToCompletion(agent, contextManager) to register and run
1752
+ * the returned agent. Framework-created identities have tool authority only
1753
+ * while registered; public tool calls before a run or after retirement fail.
1610
1754
  * The returned agent has its own ContextManager on a namespaced state
1611
1755
  * within the framework's shared Chronicle store — messages go to
1612
1756
  * `{namespace}/messages`, context log to `{namespace}/context`.
@@ -1713,23 +1857,24 @@ export declare class AgentFramework {
1713
1857
  private static readonly CHAT_CORE_TAGS;
1714
1858
  /**
1715
1859
  * Mark an inference refusal visibly: react on the message that holds the
1716
- * conversational locus (the most recent incoming channel message) with a
1717
- * category-specific emoji. Best-effort — failures are logged, never thrown,
1718
- * and non-Discord loci are silently skipped.
1860
+ * refused agent's conversational locus (the most recent incoming channel
1861
+ * message; an ephemeral run has none, so a subagent's refusal marks no
1862
+ * message) with a category-specific emoji. Best-effort — failures are
1863
+ * logged, never thrown, and non-Discord loci are silently skipped.
1719
1864
  */
1720
1865
  private reactToRefusal;
1721
1866
  /**
1722
- * Rewind the single turn that fed a refused generation: redact the newest
1723
- * message the agent did NOT author (a tool result, or an incoming message)
1724
- * and inject a metadata-only marker in its place. The marker carries a
1725
- * *description* of what was withheld (kind + size), never the content — so it
1726
- * cannot itself re-trip the classifier — while the raw record survives in the
1727
- * chronicle log for forensics. Returns a record describing the removal, or
1728
- * null if there is nothing eligible to rewind (e.g. only the agent's own
1729
- * turns, or a prior system marker, remain).
1867
+ * Shed the newest turn: remove the newest message that isn't a system
1868
+ * message, whoever wrote it, with the rest of its exchange (the tool_use
1869
+ * turn paired with a tool result, and the whole of a body stored in
1870
+ * shards). It adds no marker; the caller keeps one consolidated marker for
1871
+ * the episode (updateRewindMarker). The returned record's descriptor says
1872
+ * what was removed and its size, never the content, while the raw record
1873
+ * survives in the chronicle log for forensics. Returns null when only
1874
+ * system messages remain.
1730
1875
  *
1731
- * Shared by the auto path (on `stop_reason: refusal`) and the `/rewind`
1732
- * host command.
1876
+ * Shared by the refusal rewind, automatic or forced by `/unstick`, and by
1877
+ * the shed after inference exhaustion (quarantinePoisonedHistory).
1733
1878
  */
1734
1879
  private shedNewestTurn;
1735
1880
  /**
@@ -1741,12 +1886,6 @@ export declare class AgentFramework {
1741
1886
  * refusal clears. Returns the running count.
1742
1887
  */
1743
1888
  private updateRewindMarker;
1744
- /**
1745
- * Announce a refusal-rewind on the conversational surface (Discord), used
1746
- * when the withheld turn was a *human* message so it isn't dropped silently.
1747
- * Best-effort; mirrors reactToRefusal's locus resolution.
1748
- */
1749
- private announceRewind;
1750
1889
  /**
1751
1890
  * A turn that ended WITHOUT a refusal (a natural completion that wasn't
1752
1891
  * one, or a tool-ended turn) ends the rewind episode: the model responded.
@@ -2070,13 +2209,13 @@ export declare class AgentFramework {
2070
2209
  * reconnect or start delivers what is still pending.
2071
2210
  *
2072
2211
  * Like a server's drain, the delivery runs apart from the operation that
2073
- * started it: it isn't an operator's operation, so stop() doesn't wait for
2074
- * it. A drain already running when stop() begins runs on while the
2075
- * connections close, and what it can't deliver stays pending in the
2076
- * durable outbox. Once stop() has begun, none is started, since there's
2077
- * no point opening a drain that the teardown is about to cut off: the
2078
- * marks stay pending, and the next start's barrier reconciles the outbox
2079
- * with the branch and delivers them.
2212
+ * started it: it isn't an operator's operation. A drain already running
2213
+ * when stop() begins sends nothing more, and stop() waits for it to record
2214
+ * the call it had in flight, which closing the connections ends; what it
2215
+ * hasn't sent stays pending in the durable outbox, unrecorded. Once stop()
2216
+ * has begun, none is started, since there's no point opening a drain that
2217
+ * the teardown is about to cut off: the marks stay pending, and the next
2218
+ * start's barrier reconciles the outbox with the branch and delivers them.
2080
2219
  *
2081
2220
  * @param after What moved the branch, for the log and the alert
2082
2221
  * ("rollback", "branch switch", "undo" …).
@@ -2366,6 +2505,11 @@ export declare class AgentFramework {
2366
2505
  /** Deliver through the lane's ordinary path; report where it landed. */
2367
2506
  private deliverCoalesced;
2368
2507
  private wakeForCoalescedBatch;
2508
+ /** Queue a wake for each agent of the batch's audience, when its latest
2509
+ * occurrence triggers inference. `plain`: an ordinary push wake, without
2510
+ * the subject a withdrawal of the batch's wakes goes by, for a render
2511
+ * placed in its agent's deferred queue (wakeToRead). */
2512
+ private queueCoalescedWakes;
2369
2513
  /** Withdraw unstarted wakes whose sole cause is this subject (§4.2). */
2370
2514
  private cancelCoalescedWakes;
2371
2515
  /**
@@ -2412,13 +2556,25 @@ export declare class AgentFramework {
2412
2556
  private derivePushEventChannel;
2413
2557
  private processInferenceRequests;
2414
2558
  /**
2415
- * Metadata for rows the agent's current turn stores: a silent heartbeat
2416
- * tick stamps each of them (assistant responses, tool_result rows) so they
2417
- * stay identifiable after the turn — request builds key the tick separator
2418
- * on it, and a later retention policy can find or collapse them. The stamp
2419
- * names the agent: residents share one message slot and a broadcast tick
2420
- * reaches each of them with the same eventId. Undefined for ordinary
2421
- * turns, whose rows are stored exactly as before.
2559
+ * Stamps for the rows the agent's reply stores in its current turn: its
2560
+ * assistant responses, its tool_result rows (through `storeResults`), and
2561
+ * the contained-wrapper row that stands in for a response. The turn's other
2562
+ * stores (a delivery receipt, a routing notice, a deferred flush) aren't the
2563
+ * reply's, and carry none.
2564
+ * - A silent heartbeat tick stamps each of them for the whole turn, with the
2565
+ * separator text its request rendered. The stamps keep every row of the
2566
+ * tick identifiable after the turn, for a later retention policy to find
2567
+ * or collapse. The stamp names the agent: residents share one message
2568
+ * slot, and a broadcast tick reaches each of them with the same eventId.
2569
+ * - A `[Continue]` prompt stamps the rows of each round that follows it
2570
+ * (`turnPrompts`).
2571
+ * Request builds render each turn before the first surviving assistant row
2572
+ * that carries its stamp and that they can recognise: one with a tool call,
2573
+ * or with signed or redacted thinking (see silent-heartbeat.ts). The
2574
+ * tool_result rows and the wrapper row are stored as `user`, so their
2575
+ * stamps place nothing: they mark each row as the reply's.
2576
+ * Undefined for ordinary turns, whose rows are stored exactly as before.
2577
+ * (Named for its first use, the silent tick.)
2422
2578
  */
2423
2579
  private silentTurnRowMetadata;
2424
2580
  /** Record plain-prose segments this turn suppressed without drafts (proseRouting=disabled). */
@@ -2609,13 +2765,15 @@ export declare class AgentFramework {
2609
2765
  */
2610
2766
  private repinTurnLocus;
2611
2767
  /**
2612
- * Durable window notice for a channel the agent did not ask to open —
2613
- * subscription-policy admission, or an open forced by the agent's own
2614
- * delivery into a closed channel. The agent must always learn that the
2615
- * channel opened, and that `channel_close` opts out (their decision
2616
- * outranks policy). What an open channel delivers is its server's to say
2617
- * (#406), so the notice doesn't promise its traffic. System message: never
2618
- * triggers inference, never conversational, never locus-eligible.
2768
+ * Durable window notice for a channel the agent did not ask to open: a
2769
+ * subscription-policy admission, announced as the decision before its
2770
+ * transport RPC (a failed open keeps the decision, and channel_list shows
2771
+ * transport state), or an open that the agent's own delivery or reply
2772
+ * caused (`opened-by-delivery`, `opened-by-reply`), once its transport is
2773
+ * confirmed. Either way it names `channel_close` as the opt-out (their
2774
+ * decision outranks policy). What an open channel delivers is its server's
2775
+ * to say (#406), so the notice doesn't promise its traffic. System message:
2776
+ * never triggers inference, never conversational, never locus-eligible.
2619
2777
  */
2620
2778
  private recordChannelAutoOpenNotice;
2621
2779
  /**
@@ -2727,6 +2885,27 @@ export declare class AgentFramework {
2727
2885
  * newer turn instead.
2728
2886
  */
2729
2887
  private abortDroppedTurn;
2888
+ /**
2889
+ * The stop no stream will carry, collected at a check of the frame whose
2890
+ * window it is (a start's checks before it sends anything, and the checks
2891
+ * at a failure and after a wait to retry): an operator's quiesce abandon of
2892
+ * the turn (the window's mark), else a caller's cancelStream() or
2893
+ * abortInference() made while the agent held no stream, since the start
2894
+ * that dropped an older one at its entry. When both are there, the
2895
+ * framework's own stop is the one that ends the turn, as at a stream's
2896
+ * terminal, and the caller's goes with it.
2897
+ */
2898
+ private takeStartStop;
2899
+ /**
2900
+ * End a start that a stop reached before it sent anything, through the
2901
+ * terminal the same stop gets on a live stream: a caller's stop as
2902
+ * settleDeliberateCancel ends a cancelled stream, with the note saying the
2903
+ * turn was stopped before it sent anything (or, for a start that continues
2904
+ * a turn, before it finished), and a quiesce abandon as
2905
+ * settleFrameworkCancel ends one. Each settles the turn, an ephemeral run
2906
+ * included, and emits the one inference:aborted.
2907
+ */
2908
+ private endStoppedStart;
2730
2909
  /**
2731
2910
  * Body of startAgentStream, split out so the caller's try/finally owns the
2732
2911
  * turn token unconditionally. Returns true iff the token was handed off to
@@ -2744,8 +2923,12 @@ export declare class AgentFramework {
2744
2923
  * call entered through — a public method shared with model callers can
2745
2924
  * never confer module trust, whatever the input claims. Module origin
2746
2925
  * exists only through the private closure handed to ModuleRegistry
2747
- * (ctx.callTool → executeToolCallFrom with {kind:'module'}). Callers that
2748
- * don't identify themselves fail safe to agent semantics too.
2926
+ * (ctx.callTool → executeToolCallFrom with {kind:'module'}), which is exempt
2927
+ * from agent allowlists. Registered agents' allowedTools is enforced at
2928
+ * dispatch, including namespaced utility names; utils only grants the
2929
+ * list/describe/run slot. Genuinely unidentified public callers (including
2930
+ * unknown legacy names) remain unrestricted. Framework-created identities
2931
+ * are refused while unregistered, even if their configured list allows it.
2749
2932
  */
2750
2933
  executeToolCall(call: ToolCall): Promise<ToolResult>;
2751
2934
  /**
@@ -2904,14 +3087,45 @@ export declare class AgentFramework {
2904
3087
  private getOrCreateScriptRunner;
2905
3088
  /**
2906
3089
  * Resolve one script-inner tool call to the string the script's tool
2907
- * function returns. Never rejects — errors become "Error: ..." strings,
2908
- * matching the managed PTC runtime ("Claude's code receives this error").
3090
+ * function returns, and whether that result is an error (the script's
3091
+ * `.is_error`). Never rejects: a failure without structured content becomes
3092
+ * an "Error: ..." string, matching the managed PTC runtime ("Claude's code
3093
+ * receives this error"), and a structured one its JSON object.
2909
3094
  *
2910
- * Results are serialized with the HISTORY serializer (images become
2911
- * placeholders): programmatic tool results are text-only by contract, and
2912
- * a script variable holding megabytes of base64 helps nobody.
3095
+ * A result without structured content is serialized with the HISTORY
3096
+ * serializer: images become placeholders, since a script variable holding
3097
+ * megabytes of base64 helps nobody. A result with structured content is
3098
+ * one JSON object holding both of the server's views
3099
+ * (scriptStructuredResult), so its content blocks reach the script as the
3100
+ * dispatch gave them, images and payloads included. A module's result that
3101
+ * can't be stored or sent, such as a cyclic object or a BigInt, is already
3102
+ * an error naming the tool when the dispatch returns it (storableToolResult),
3103
+ * so a script reads that error's text.
2913
3104
  */
2914
3105
  private handleScriptToolCall;
3106
+ /** The 5MB protocol safety cap on what a script receives. */
3107
+ private static readonly SCRIPT_RESULT_MAX_CHARS;
3108
+ /**
3109
+ * A structured MCP result as a script receives it: one JSON object holding
3110
+ * both of the server's views, `{ content, structuredContent, isError }`,
3111
+ * plus `error` (the text) on a tool error. `content` is the content array
3112
+ * the dispatch gave this call (the direct reading): the server's blocks as
3113
+ * sent, so payloads and images survive, except that an MCPL peer's blocks
3114
+ * get the treatment RFC-005 gives them on every lane the model reads
3115
+ * (referenceBlockForScript). `structuredContent` is the tool's own JSON, as
3116
+ * sent. Past the size cap the result stays valid JSON: the whole object is
3117
+ * saved to the workspace, and the script gets
3118
+ * `{ isError, oversized: { chars, savedTo } }`. If it can't be saved, the
3119
+ * script gets a failure to materialize the result, saying the tool may
3120
+ * already have completed, never a success missing its result. A result
3121
+ * that can't be converted to JSON (a cyclic object, a BigInt), which the
3122
+ * dispatch's own check makes unlikely here, gets the same object empty,
3123
+ * `{ content: [], structuredContent: null, isError: true, error }`, the
3124
+ * error naming the tool. In every form, the script's `.is_error` is the
3125
+ * `isError` of the object its string holds, and its `.structured` is read
3126
+ * from that string.
3127
+ */
3128
+ private scriptStructuredResult;
2915
3129
  /**
2916
3130
  * Dispatch a script-inner tool call through the NORMAL dispatch path and
2917
3131
  * resolve with its result. The waiter intercept in handleProcessEvent
@@ -2959,6 +3173,9 @@ export declare class AgentFramework {
2959
3173
  * entry for it, and its usage can be counted twice in the log. */
2960
3174
  private logInference;
2961
3175
  private logProcessEvent;
3176
+ /** Count one server's close (see mcplServerCloses). Partially-constructed
3177
+ * frameworks (tests build them with Object.create) have no map yet. */
3178
+ private noteMcplServerClosed;
2962
3179
  /** Forget one server's RFC-008 class declarations (see the close handler). */
2963
3180
  private dropMcplToolClasses;
2964
3181
  /**
@@ -2994,15 +3211,20 @@ export declare class AgentFramework {
2994
3211
  }>;
2995
3212
  /**
2996
3213
  * Find the MCPL server for a tool: [serverId, prefix], or null.
2997
- * Prefixes can nest (`foo` and `foo--bar`), so `foo--bar--x` can match
2998
- * both. The tool listing decides: the server whose tools/list offered the
3214
+ * The tool listing decides: the server whose tools/list offered the
2999
3215
  * name, after its tool policy, as of the last tool refresh (a server that
3000
- * closed since keeps its names until the next one). Where two servers list
3001
- * the same name, the listing holds the one listed last (#265). A name the
3002
- * listing doesn't hold (invented by the model, or no longer listed) goes to
3003
- * the longest matching prefix. Never prefix-map insertion order.
3216
+ * closed since keeps its names until the next one). A name the listing
3217
+ * doesn't hold (invented by the model, or no longer listed) goes to the
3218
+ * longest matching prefix. Prefixes that meet, such as `foo` and
3219
+ * `foo--bar`, are refused at configuration (prefixesMeet, #265), so a name
3220
+ * fits at most one configured server's prefix; this rule doesn't rely on
3221
+ * that. Never prefix-map insertion order.
3004
3222
  */
3005
3223
  private resolveMcplTool;
3224
+ /** Registered policy; framework-created identities have no dispatch authority
3225
+ * while unregistered. Genuine legacy caller names retain their surface. */
3226
+ private canAgentUseTool;
3227
+ private agentToolPermissionFailure;
3006
3228
  /**
3007
3229
  * Why this host refuses a skip_reply call, as the model will read it, or
3008
3230
  * undefined when it doesn't (skipReplyWakeRefusal): without an EventGate,
@@ -3085,13 +3307,20 @@ export declare class AgentFramework {
3085
3307
  * of the body isn't stored. (Requeued, the
3086
3308
  * landed shards would be stored twice, and only the rest could not be
3087
3309
  * appended later without splitting the body.) Boot's scan for landed
3088
- * writes counts a landed shard the same way.
3310
+ * writes counts a landed shard the same way. When the store can't be read
3311
+ * back (storedCopiesOf logs it), nothing is found, and the entry is put
3312
+ * back as one that couldn't be stored: if it had landed, a later flush
3313
+ * stores it again.
3089
3314
  *
3090
3315
  * Returns what was written (stored, or deferred again by addMessage, whose
3091
3316
  * id is then empty), in queue order: for one target, a prefix of its
3092
- * entries. `onLanded` hears an entry whose write threw after storing it,
3093
- * with the id it landed under, as soon as it's found: before the writes
3094
- * after it, so whatever it records keeps their order.
3317
+ * entries. Each comes with its content: as given, unless the store took it
3318
+ * as more than one message (a sharded body) and can be read back, when its
3319
+ * copies are returned joined, as later compiles render them (asCompiled),
3320
+ * or its write threw after storing it (above). `onLanded` hears an entry
3321
+ * whose write threw after storing it, with the id it landed under, as soon
3322
+ * as it's found: before the writes after it, so whatever it records keeps
3323
+ * their order.
3095
3324
  */
3096
3325
  private writeDrainedInOrder;
3097
3326
  /** How many messages the agent's store holds: undefined when its store
@@ -3116,6 +3345,149 @@ export declare class AgentFramework {
3116
3345
  * the writes after it: an open turn's arrivals keep the order the writes
3117
3346
  * landed in, and its newest stays the newest (remainingArrival). */
3118
3347
  private readdDrained;
3348
+ /**
3349
+ * Where a notice about an agent's own action belongs: its failed reply, its
3350
+ * bounced prose (beside its `{{unsent}}` clipboard), its background script's
3351
+ * wake, the notices about its drafts. Every registered agent's notice is
3352
+ * addressed to that agent, so it lands in the window that agent reads and
3353
+ * waits on that agent's own turn, stream and tool cycle: a conversation
3354
+ * fork or an ephemeral run reads only its isolated slot, and a resident's
3355
+ * notice would otherwise wait on the primary's turn rather than its own.
3356
+ * Residents share one message slot, so a resident's notice can land there
3357
+ * while another resident's turn is alive; agent-framework #291 covers that.
3358
+ *
3359
+ * null when the agent is no longer registered (a fork or a run already
3360
+ * released): there is no window to write, so the caller writes nothing,
3361
+ * traces nothing and requests no wake. The drop is logged here, once. A
3362
+ * failed reply is passed on by name instead (passFailedReplyToPrimary). The
3363
+ * notice of a channel an agent's own send opened follows the same rule in
3364
+ * recordChannelAutoOpenNotice, which passes it on by name too.
3365
+ */
3366
+ private noticeTargetFor;
3367
+ /**
3368
+ * A conversation fork or an ephemeral run has been deregistered. A release
3369
+ * never runs while the host is quiesced: it waits for resume()
3370
+ * (releaseWindowNowOrAfterResume), since a write could land mid-surgery.
3371
+ *
3372
+ * The agent never reads its window again, so every failed-reply notice it
3373
+ * never read is passed to the primary, by name, once: each one stored in
3374
+ * its window above its consumed watermark (flushed there at its turn's end,
3375
+ * or stored while it was idle) and not heard at a tool boundary, and each
3376
+ * one still queued for it. Writes still
3377
+ * queued would wait for a boundary it never reaches, and the flush that
3378
+ * finds it gone would drop them, so they land in its own window now, which
3379
+ * Chronicle keeps for investigation. Its stream is over or cancelled, so it
3380
+ * compiles nothing more. One path still stores after these writes: a
3381
+ * `complete` that arrived while the agent still waited for tools awaits
3382
+ * the spill of its last tool results, and stores them once that resolves,
3383
+ * even if a watchdog has ended the run meanwhile. A tool boundary stores
3384
+ * nothing for a round whose turn was stopped while its results were
3385
+ * prepared.
3386
+ */
3387
+ private releaseWindow;
3388
+ /**
3389
+ * The failed-reply notices stored in `agent`'s window that it never read:
3390
+ * those above the lower of its consumed watermark and the point where the
3391
+ * watermark's branch and the current one part (branchesPartAt), or every
3392
+ * one, when it never compiled. The watermark moves at a compile, and at a
3393
+ * tool boundary whose
3394
+ * stream declares it carries the messages injected there
3395
+ * (markContextConsumed), so a notice the live stream heard is under it, and
3396
+ * one a boundary stored without sending (a turn a tool ended, a budget
3397
+ * restart, a stream that may drop injections) is above it.
3398
+ */
3399
+ private unreadFailedReplies;
3400
+ /**
3401
+ * The last sequence two branches' histories share: Infinity for one branch,
3402
+ * and for two, the point where they part. A branch's records are numbered
3403
+ * on from its branch point (Chronicle gives each record its branch's head
3404
+ * plus one), so up to that point both hold the same records under the same
3405
+ * numbers, and past it each branch's numbers are its own. -Infinity when
3406
+ * the store knows no shared history for them, as for an empty branch.
3407
+ */
3408
+ private branchesPartAt;
3409
+ /**
3410
+ * Release a deregistered fork's or run's window (releaseWindow) now, or,
3411
+ * while the host is quiesced, when it resumes. A release lands the agent's
3412
+ * queued writes in its window, and while quiesced a write could land
3413
+ * mid-surgery; dropping them instead would lose them from both the window
3414
+ * and the recovery queue. Only the window waits: the agent is already
3415
+ * deregistered, so its stream can't dispatch anything more and resume
3416
+ * doesn't weigh its context, and a write addressed to it meanwhile is
3417
+ * treated as for any agent no longer registered (dropped, or passed to the
3418
+ * primary if it's a failed reply).
3419
+ */
3420
+ private releaseWindowNowOrAfterResume;
3421
+ /** resume(): the windows whose release the quiesce window postponed, each on its own. */
3422
+ private runReleasesAfterResume;
3423
+ /**
3424
+ * A fork or an ephemeral run ended before reading that its reply's delivery
3425
+ * to a channel failed: it was released with the notice stored in its window
3426
+ * unread, or its delivery failed after release. (A notice still queued for
3427
+ * it, at release or restored at boot, goes on as the queue entry itself:
3428
+ * queuedFailedReplyForPrimary.) Tell the primary instead, naming the
3429
+ * speaker, so an agent who can still act learns of it and can tell the
3430
+ * human. It gives the reply's length, not its words, which stay in the
3431
+ * speaker's window. Its wording follows the speaker's own notice
3432
+ * (onRouteFailure): a delivery that wasn't confirmed may have been posted,
3433
+ * and neither text says no one saw the reply, since only this channel's
3434
+ * routing failed. Only for a delivery to a channel; a reply that had no
3435
+ * channel to go to (no locus) had no human waiting for it. Never throws:
3436
+ * it runs inside a release, which must complete whatever this write meets.
3437
+ */
3438
+ private passFailedReplyToPrimary;
3439
+ /**
3440
+ * A queued failed-reply entry for `speaker`, who is gone, as the primary's
3441
+ * by-name notice (passFailedReplyToPrimary's words): the same entry, under
3442
+ * its own id and seq, now addressed to the primary. Written through the
3443
+ * drain, it lands stamped with that id, or waits in the queue under it, so
3444
+ * boot's exactly-once holds for it: a crash before the original's ack
3445
+ * replays one entry, never the original and a separate notice. null when no
3446
+ * one is told (logged): a reply that had no channel to reach, or no primary.
3447
+ */
3448
+ private queuedFailedReplyForPrimary;
3449
+ /** The primary's by-name notice of `speaker`'s failed reply, or null when no
3450
+ * one is told, which is logged here (see passFailedReplyToPrimary). */
3451
+ private failedReplyNoticeForPrimary;
3452
+ /**
3453
+ * A channel as a notice names it: the label when the registry has one, since
3454
+ * a bare snowflake is unresolvable for the agent (the 2026-07-21 incident
3455
+ * read as "a stale artifact", not a live failure).
3456
+ */
3457
+ private channelForNotice;
3458
+ /**
3459
+ * A module's write through its context: addMessage's, except that a write
3460
+ * the framework holds is answered by its handle (heldHandle) rather than
3461
+ * '', so the module can find, read, edit and remove what it wrote before
3462
+ * the write lands. The framework's own callers keep the '' they've always
3463
+ * had, which their traces and checks take as a write not stored yet.
3464
+ */
3465
+ private addModuleMessage;
3466
+ /**
3467
+ * The writes the framework holds for the store `reader` reads, in queue
3468
+ * order: those addressed to it and, when it's an ordinary resident, those
3469
+ * addressed to any ordinary resident, since residents share one message
3470
+ * slot (sharedSlotAgents). A write whose target is gone isn't among them:
3471
+ * no module reads that agent's store, and the write waits for its window's
3472
+ * release (drainDeferredFor) or, at its flush, is dropped (addMessage), a
3473
+ * failed reply passing to the primary as its notice
3474
+ * (queuedFailedReplyForPrimary).
3475
+ */
3476
+ private heldWritesFor;
3477
+ /**
3478
+ * The writes held for `reader`'s store (heldWritesFor) as the rows a
3479
+ * module's context reads: each under its handle, marked `held`, with the
3480
+ * participant, content and metadata it was written with, the time it was
3481
+ * first held as its timestamp, and a sequence after every row the store
3482
+ * holds, in queue order. The sequence and timestamp are provisional: the
3483
+ * stored row has the store's own.
3484
+ */
3485
+ private heldRows;
3486
+ /** The row a held write landed as, found by the id it's stored with
3487
+ * (`metadata.deferredWriteId`): the first, for a body stored in shards,
3488
+ * as addMessage answers a stored write. Null when it hasn't landed, or is
3489
+ * no longer in the store. */
3490
+ private landedRow;
3119
3491
  private editMessage;
3120
3492
  private removeMessage;
3121
3493
  private getMessage;
@@ -3234,6 +3606,44 @@ export declare class AgentFramework {
3234
3606
  * Fail-open: individual server connection failures don't prevent framework startup.
3235
3607
  */
3236
3608
  private initializeMcpl;
3609
+ /** The modern connections, created on first use, so a framework assembled
3610
+ * field by field (as unit tests do) needs no setup for them. */
3611
+ private modernServers;
3612
+ /** The restarts' claims (mcpRestartClaims), created on first use, as the
3613
+ * modern connections are. */
3614
+ private restartClaims;
3615
+ private legacyConnectAborts;
3616
+ /**
3617
+ * Hold a server id for one connect or disconnect, until `release()`.
3618
+ * Together with the live connections (the MCPL registry and the modern
3619
+ * map) this is what holds an id: from a connect's admission until its
3620
+ * teardown is done, one owner at a time. So a connect is refused while
3621
+ * anything holds its id, and a teardown never removes what a later
3622
+ * connection registered. A disconnect takes the hold over, and waits for
3623
+ * the operation it displaced (`previous`); a connect that finds itself
3624
+ * `displaced` ends rather than setting up a server being removed. Each
3625
+ * release frees only its own hold.
3626
+ */
3627
+ private holdMcpServerId;
3628
+ /** Whether a server's connection still serves it, or is reconnecting to
3629
+ * it. One the host is closing or has closed doesn't, and neither does one
3630
+ * lost for good, whose reconnecting has stopped. */
3631
+ private static mcpConnectionServes;
3632
+ /**
3633
+ * A connect whose failed launch couldn't be confirmed exited. Its
3634
+ * connection stays registered, closed, and holds the id, so nothing
3635
+ * launches beside a child that may still be running; a disconnect retries
3636
+ * the reap, and completes once the exit is confirmed. Once stop() has
3637
+ * begun it refuses a disconnect, and retries the reap itself, so no
3638
+ * disconnect is offered then.
3639
+ */
3640
+ private unreapedConnectFailure;
3641
+ /** Close every modern-MCP connection, including any connect still in
3642
+ * flight, so none outlives a failed startup or stop(). One whose close
3643
+ * fails or is still pending stays registered, holding its id, as a failed
3644
+ * connect's does, and every failure is thrown, as the MCPL registry's
3645
+ * closeAll does. `timeoutMs` bounds the wait without cancelling cleanup. */
3646
+ private closeModernMcpConnections;
3237
3647
  /**
3238
3648
  * Reconcile the durable ledger with Chronicle, then deliver every server's
3239
3649
  * work. A drain still running is waited for and then followed by a fresh
@@ -3252,6 +3662,10 @@ export declare class AgentFramework {
3252
3662
  * Deliver every due ref independently. Permanent Discord failures are kept
3253
3663
  * in the ledger for audit but do not block later operations; retryable
3254
3664
  * failures remain pending for the next reconnect/list-change attempt.
3665
+ * Once stop() has begun, a drain sends nothing more and records nothing
3666
+ * for what it hasn't sent: that stays pending, as it was, for the next
3667
+ * start's barrier. stop() closes the connections and then waits for each
3668
+ * drain still running to record the call it had in flight.
3255
3669
  */
3256
3670
  private drainDiscordAwarenessOutbox;
3257
3671
  private readDiscordAwarenessPending;
@@ -3267,7 +3681,9 @@ export declare class AgentFramework {
3267
3681
  * its last read of the ledger, missing a batch activated since. So a
3268
3682
  * generation that finds a drain running waits for it, then drains again
3269
3683
  * with a fresh pass that reads the ledger anew. Two generations waiting on
3270
- * the same drain share the fresh pass that follows it.
3684
+ * the same drain share the fresh pass that follows it. Once stop() has
3685
+ * begun, a drain sends nothing more, so a gate armed then completes with
3686
+ * what it found pending left for the next start.
3271
3687
  */
3272
3688
  private drainForGeneration;
3273
3689
  /**
@@ -3278,10 +3694,57 @@ export declare class AgentFramework {
3278
3694
  * meanwhile. Rejects as the generation it waited for rejects.
3279
3695
  */
3280
3696
  private awaitDiscordAwarenessBarriers;
3697
+ /**
3698
+ * Per connection, the transport epoch whose §5.3 policy exchange is in
3699
+ * flight: a connect's, from when the registry hands the connection back,
3700
+ * or a reconnect's, from its 'reconnect' event. Until the exchange settles,
3701
+ * that transport's traffic must wait for its grant, so a gate that opens
3702
+ * every connection's planes meanwhile (another server's list change, a
3703
+ * resume, an awareness sync, startup's staged open) passes it by
3704
+ * (mcplPlanesMayOpen), and the exchange's own caller opens them once it
3705
+ * settles (openMcplPlanesAfterExchange).
3706
+ */
3707
+ private readonly mcplPolicyExchanges;
3708
+ /** Whether a gate may open this connection's planes: no policy exchange is
3709
+ * in flight on its current transport (see mcplPolicyExchanges). */
3710
+ private mcplPlanesMayOpen;
3711
+ /**
3712
+ * Run a connection's §5.3 policy exchange with its planes held: it is
3713
+ * marked in flight for the transport it runs on, so no gate releases that
3714
+ * transport's traffic before its grant exists. With `epoch`, it runs only
3715
+ * on that transport: a runtime connect passes the one it registered, and
3716
+ * if that transport is gone, the exchange isn't run, since a reconnect
3717
+ * that adopts a next transport, if one comes, runs that one's. With
3718
+ * `closeOnFailure`, as for a runtime connect or restart, an exchange that
3719
+ * throws closes the connection for good, through the registry, before it
3720
+ * lifts the marker: left live, the connection would be half registered,
3721
+ * and unmarked before its close, a gate completing in between would let
3722
+ * out what it held with no grant. A close that fails keeps the connection
3723
+ * registered, but closed for good, it readies nothing but its own reports.
3724
+ * Without `closeOnFailure`, as for a reconnect or a staged startup connect,
3725
+ * an exchange that throws leaves the connection live with the grant it
3726
+ * reached, empty if the policy step itself threw, and the marker lifts.
3727
+ */
3728
+ private settlePolicyExchange;
3729
+ /**
3730
+ * Open a connection's planes once its exchange has settled, when the gate
3731
+ * its connect or reconnect installed was replaced during the exchange: the
3732
+ * newer gate passed the connection by, and if it has completed, nothing
3733
+ * else would open it. A connection whose transport dropped meanwhile is
3734
+ * readied as any gate readies one between transports, so its own reports
3735
+ * go on. How far follows what is held now: while a gate is up, only the
3736
+ * control plane opens, since a gate holds data planes alone, and its
3737
+ * completion, which no longer passes this connection by, opens the data
3738
+ * plane; a quiesced host (issue #122) likewise opens only control planes;
3739
+ * otherwise both open.
3740
+ */
3741
+ private openMcplPlanesAfterExchange;
3281
3742
  /**
3282
3743
  * Install one framework-global Discord-awareness generation before releasing
3283
3744
  * any inference-bearing event. With pending work or a drain still running,
3284
- * every MCPL data plane is paused while all control planes remain live.
3745
+ * every MCPL data plane is paused while the control planes remain live,
3746
+ * but for a connection whose policy exchange is in flight, which neither
3747
+ * the release nor the completion opens (mcplPlanesMayOpen).
3285
3748
  * With neither, ready() runs synchronously so request responders preserve
3286
3749
  * their historical same-stack behavior.
3287
3750
  */
@@ -3289,6 +3752,14 @@ export declare class AgentFramework {
3289
3752
  private releaseMcplDataPlaneGate;
3290
3753
  private completeMcplDataPlaneGate;
3291
3754
  private failMcplDataPlaneGate;
3755
+ /**
3756
+ * Close a legacy connection for good through the registry. A close that
3757
+ * can't confirm its child exited keeps the connection registered, holding
3758
+ * its id, and emits no 'close', so no close listener takes its tools off
3759
+ * the agents' list: this does, then rethrows the failure for the caller to
3760
+ * report. A server the host has given up on leaves the list either way.
3761
+ */
3762
+ private closeMcplServerForGood;
3292
3763
  /**
3293
3764
  * Close a runtime connect's new connection for good, through the registry,
3294
3765
  * after a step of that connect failed. A close that fails keeps the entry,
@@ -3303,11 +3774,23 @@ export declare class AgentFramework {
3303
3774
  * lifecycle API (connectMcplServer). Throws on connection failure.
3304
3775
  */
3305
3776
  private connectMcplServerInternal;
3306
- /** Per-connection §17.8 fetch limiter: at most one in-flight fetch, at
3307
- * most one queued behind it (announcements coalesce), floor between
3308
- * completed fetches. Exceeding the floor drops the excess with a
3309
- * diagnostic — the host bounds the rate; it never depends on server
3310
- * coalescing. */
3777
+ /** The part of a connect that runs holding its id, handing back the
3778
+ * connection it registered. `displaced`: whether a disconnect has taken
3779
+ * the id over and is waiting for this connect, which a legacy connect asks
3780
+ * once its handshake is done. */
3781
+ private connectHeldMcplServer;
3782
+ /** Per-connection §17.8 fetch limiter, keyed by the connection, so one
3783
+ * that replaces a removed server under its id starts its own: at most one
3784
+ * fetch in flight; announcements during it coalesce into one refresh run
3785
+ * after it, and announcements within the floor after a fetch into one run
3786
+ * once the floor has passed — the host bounds the rate; it never depends
3787
+ * on server coalescing. A queued refresh runs only on the transport of
3788
+ * the latest announcement it holds (`queuedEpoch`), and its receipt
3789
+ * reports the domains of the announcements coalesced into it there
3790
+ * (`queuedDomains`), since its fetch answers them all. The next
3791
+ * transport's initialize and policy exchange start afresh, and its own
3792
+ * announcements queue their own, so an earlier transport's hints are
3793
+ * dropped. */
3311
3794
  private manifestRefreshState;
3312
3795
  private static readonly MANIFEST_FETCH_FLOOR_MS;
3313
3796
  /**
@@ -3325,7 +3808,9 @@ export declare class AgentFramework {
3325
3808
  */
3326
3809
  private handleManifestChanged;
3327
3810
  /** A refresh handleManifestChanged schedules for itself, after the fetch
3328
- * floor or behind a fetch in flight. It's contained as the announcement's
3811
+ * floor or behind a fetch in flight. Its announcement is the domains of
3812
+ * the announcements it coalesced (`{ domains }`), the one field
3813
+ * handleManifestChanged reads of one. It's contained as the announcement's
3329
3814
  * own run is. Its fetch's own failure is logged by the fetch's catch, as
3330
3815
  * in 0.22.x; any other failure emits mcpl:request-handler-error and a
3331
3816
  * stderr line through the containment. */
@@ -3392,13 +3877,25 @@ export declare class AgentFramework {
3392
3877
  * false or null is refused, as `create()` refuses one.
3393
3878
  */
3394
3879
  connectMcplServer(config: import('./mcpl/types.js').McplServerConfig): Promise<void>;
3880
+ /** connectMcplServer's work, for a restart's connect too: the id and tool
3881
+ * prefix its `claim` holds are free to it, and refused to everyone else. */
3395
3882
  private runConnectMcplServer;
3396
3883
  /**
3397
- * Disconnect an MCPL server at runtime: close the connection, destroy its
3398
- * feature-set and checkpoint state (permanent removal — unlike a transient
3399
- * transport close, which preserves checkpoints for the reconnect), remove
3400
- * its channels from the registry, drop routing entries, and refresh tools.
3884
+ * Disconnect an MCPL server at runtime: remove its channels from the
3885
+ * registry (first, cancelling their queued lifecycle work), close the
3886
+ * connection, destroy its feature-set and checkpoint state (permanent
3887
+ * removal — unlike a transient transport close, which preserves checkpoints
3888
+ * for the reconnect), drop routing entries, and refresh tools.
3401
3889
  * No-op-ish if the server is not connected (still clears routing state).
3890
+ *
3891
+ * It holds the id throughout (holdMcpServerId): a connect for it is
3892
+ * refused meanwhile, and a connect already in flight is ended (closed, or
3893
+ * displaced when its legacy handshake finishes) and settles before
3894
+ * anything but the channel entries the disconnect found when it began is
3895
+ * removed. A close that can't confirm a child exited fails the disconnect
3896
+ * before anything but those entries is removed: the server stays
3897
+ * registered, closed, so nothing launches beside a child that may still be
3898
+ * running, and disconnecting again retries the reap.
3402
3899
  */
3403
3900
  disconnectMcplServer(id: string): Promise<void>;
3404
3901
  private runDisconnectMcplServer;
@@ -3407,11 +3904,63 @@ export declare class AgentFramework {
3407
3904
  * same (or an updated) config. This actually respawns a stdio child —
3408
3905
  * unlike `reconnect: true`, which only retries after transport-level
3409
3906
  * failures. Throws if the server has no stored config and none is given.
3410
- * A config `connectMcplServer` would refuse for its boolean settings is
3411
- * refused before the running server is disconnected, so it stays up.
3907
+ * What its connect would refuse is refused before the running server is
3908
+ * torn down. Until the connect, a connect or a restart started meanwhile
3909
+ * is refused the id and the tool prefix the restart moves the server to,
3910
+ * and a module added meanwhile is refused that tool prefix. The id and
3911
+ * prefix it moves the server from are free once its disconnect is done, as
3912
+ * after any disconnect.
3913
+ *
3914
+ * A disconnect of the id the restart moves the server to (its own id, for
3915
+ * a restart under the same id), or a `stop()`, that begins during the
3916
+ * restart ends it, as a disconnect ends a connect in flight: the restart
3917
+ * rejects, saying the server is down, and its holds are released, and the
3918
+ * server isn't left configured, as after any disconnect. A restart whose
3919
+ * connect fails on its own leaves the server down, configured under the
3920
+ * new configuration and its id, and holding its tool prefix, as any failed
3921
+ * connect does: `listMcplServers()` lists it as not connected, and a
3922
+ * restart or a disconnect under that id tries it again or removes it.
3923
+ *
3924
+ * Treat a submitted configuration and its nested values as immutable for
3925
+ * as long as the server uses them. Apply changed settings with
3926
+ * `restartMcplServer(id, newConfig)`; use `accessProvider` for a network
3927
+ * server's rotating credentials (a stdio server's changed `env` goes
3928
+ * through a restart with the new configuration).
3412
3929
  */
3413
3930
  restartMcplServer(id: string, newConfig?: import('./mcpl/types.js').McplServerConfig): Promise<void>;
3414
3931
  private runRestartMcplServer;
3932
+ /**
3933
+ * A redactor for text that may come from any configured server, with every
3934
+ * server's credentials replaced: those of the configurations present when
3935
+ * it's made, then those present when it's used. An operation makes it
3936
+ * before it waits, so a server removed meanwhile, and one connected
3937
+ * meanwhile, both have their credentials replaced. When one of them can't
3938
+ * be read, the text is withheld, as redactServerText withholds it.
3939
+ */
3940
+ private everyServerRedactor;
3941
+ /** Why a connect for this id is refused, or null when the id is free. One
3942
+ * owner per id, across both engines (holdMcpServerId): a connect or
3943
+ * disconnect in flight holds it, and so does a registered connection,
3944
+ * including one kept because its launch couldn't be confirmed exited, and
3945
+ * a restart moving its server to the id (mcpRestartClaims), except to
3946
+ * that restart's own connect (`claim`). That connect is still refused
3947
+ * while a disconnect of the id is in flight: the restart's own disconnect
3948
+ * has finished by then, so that one began during the restart, and it ends
3949
+ * the restart. `restarting` words the refusal for a restart of that
3950
+ * server checking the id it would move to. */
3951
+ private mcpServerIdTaken;
3952
+ /** The collision rules create() enforces at startup, as a runtime connect
3953
+ * (or a restart, before its disconnect) meets them, in either engine: the
3954
+ * tool prefix must not shadow a module name or another server's prefix,
3955
+ * or meet another server's (prefixesMeet, #265), and must neither be nor
3956
+ * meet one a restart is moving its server to (mcpRestartClaims). The
3957
+ * server's own prefix is free to keep, so is that of the server a restart
3958
+ * is about to disconnect (`leaving`), and so is the prefix a restart's own
3959
+ * connect holds (`claim`). */
3960
+ private mcplPrefixCollision;
3961
+ /** The protocol family a configuration resolves to, or a refusal naming
3962
+ * every problem with it, before anything is spawned, dialed or torn down. */
3963
+ private static usableConfigFamily;
3415
3964
  /**
3416
3965
  * List configured MCPL servers with live connection status and the number
3417
3966
  * of tools each currently contributes.
@@ -3420,10 +3969,22 @@ export declare class AgentFramework {
3420
3969
  id: string;
3421
3970
  connected: boolean;
3422
3971
  retrying: boolean;
3972
+ /** Which engine serves it: `legacy` (MCP 2024-11-05 + MCPL) or `modern`
3973
+ * (MCP 2026-07-28), as its configuration selects; null when the
3974
+ * configuration names no usable transport (see `refused`). */
3975
+ family: 'legacy' | 'modern' | null;
3976
+ /** The MCP revision its last handshake established; null until one has. */
3977
+ protocolVersion: string | null;
3978
+ /** How it is reached; null when the configuration names no usable transport. */
3979
+ transport: 'stdio' | 'websocket' | 'http' | null;
3980
+ /** Why connecting refuses this configuration, before anything is spawned
3981
+ * or dialed; null when it is usable. */
3982
+ refused: string | null;
3423
3983
  toolPrefix: string;
3424
3984
  toolCount: number;
3425
- /** True only after the §5.3 policy receipt activated the grant. */
3426
- policyEstablished: boolean;
3985
+ /** True only after the §5.3 policy receipt activated the grant. Null for
3986
+ * a modern server, which has no MCPL policy. */
3987
+ policyEstablished: boolean | null;
3427
3988
  /** Sole normative authorization allowlist for the connection (§5.4). */
3428
3989
  effectiveGrant: string[];
3429
3990
  /** Advertised paths removed by host enabled/disabled capability config. */
@@ -3462,6 +4023,10 @@ export declare class AgentFramework {
3462
4023
  lastNegotiatedAt: number | null;
3463
4024
  };
3464
4025
  command?: string;
4026
+ /** The url as {@link redactUrl} shows it, since a credential may ride in
4027
+ * it: no userinfo, each query parameter's value replaced, and any path
4028
+ * segment or query parameter name that looks like a credential
4029
+ * replaced. */
3465
4030
  url?: string;
3466
4031
  }>;
3467
4032
  /**
@@ -3470,6 +4035,9 @@ export declare class AgentFramework {
3470
4035
  * rejection at this boundary, including failures after channels/register or
3471
4036
  * channels/changed has acknowledged the request. It's invoked at once, so
3472
4037
  * control-plane replies keep their acknowledge-before-reconcile order.
4038
+ * Each text a failure records, in its trace and its log line, goes through
4039
+ * `recorded`, the server's redactor where the caller has one, since a
4040
+ * handler's error can quote what the server sent.
3473
4041
  */
3474
4042
  private containedInboundHandler;
3475
4043
  /**
@@ -3477,9 +4045,29 @@ export declare class AgentFramework {
3477
4045
  * Push events and inference requests are deferred to Steps 6/7.
3478
4046
  */
3479
4047
  private wireMcplEvents;
4048
+ /**
4049
+ * Wire a modern-MCP connection's events. There's less here than for MCPL
4050
+ * on purpose: no push, inference, channels or policy exist on this family.
4051
+ * A list change, including the relist after a reconnect, takes the legacy
4052
+ * engine's refresh path (`handleToolsListChanged`), so any wake it causes
4053
+ * goes through ordinary admission: it parks under quiesce or a hold like
4054
+ * every other wake. It installs no awareness gate, since that gate orders
4055
+ * MCPL data-plane traffic, which this family doesn't carry.
4056
+ */
4057
+ private wireModernEvents;
4058
+ /** An MCP tool call's trace: a result the server marked an error (or one
4059
+ * the reading couldn't complete) is tool:failed, as on the other tool
4060
+ * paths; anything else is tool:completed. */
4061
+ private traceMcpToolOutcome;
3480
4062
  /**
3481
4063
  * Discover tools from all connected MCPL servers and cache them.
3482
4064
  * Tools are namespaced as `{toolPrefix}--{toolName}` per server config.
4065
+ * No two servers' prefixes meet (prefixesMeet, refused at create(), a
4066
+ * runtime connect and a restart), so no two servers give one name. A
4067
+ * server that lists one name more than once keeps its first listing, and
4068
+ * an ops alert at each refresh that finds it says so (#265): a tool list
4069
+ * holding one name twice is refused by membrane's native formatter and by
4070
+ * Anthropic's API.
3483
4071
  */
3484
4072
  private refreshMcplTools;
3485
4073
  /**
@@ -3509,11 +4097,47 @@ export declare class AgentFramework {
3509
4097
  * Emit a trace event and push an external-message listing newly added tools.
3510
4098
  */
3511
4099
  private emitMcplToolDiff;
4100
+ /** The refusal of a tool call whose server isn't one to call yet, for the
4101
+ * model's call, a script's and an in-process one alike: none registered,
4102
+ * or a modern connection whose connect is still under way
4103
+ * (modernStillConnecting). It says "connecting" while a connect of its id
4104
+ * is in flight (mcpServerOps) or a restart holds the id, and "gone"
4105
+ * otherwise. */
4106
+ private mcplServerAbsentText;
4107
+ /** A modern connection a connect still under way registered: it registers
4108
+ * before it starts, so until its first session is up it's no server to
4109
+ * call, and a call reads the "connecting" text, as one during a legacy
4110
+ * connect's handshake does. Once that session is up, a call goes to it,
4111
+ * even while the connect lists its tools. */
4112
+ private modernStillConnecting;
3512
4113
  /**
3513
4114
  * Dispatch a tool call to an MCPL server.
3514
4115
  * Strips the configured toolPrefix and routes to the server.
3515
4116
  */
3516
4117
  private dispatchMcplToolCall;
4118
+ /**
4119
+ * The direct path's reading of an MCP tool result (executeToolCall,
4120
+ * ModuleContext.callTool). `data` stays the raw content array, as module
4121
+ * callers have always received it. Two things change: a tool error
4122
+ * (`isError: true`) is now a failure, where every answer used to count as
4123
+ * success, and `structured` is added by presence.
4124
+ */
4125
+ private static directToolResult;
4126
+ /**
4127
+ * The framework's ToolResult for an MCP tool result on the model path
4128
+ * (model dispatch, and scripts, which ride it). A standard peer, meaning an MCP-only legacy
4129
+ * server or any modern server, gets the standard reading
4130
+ * (tool-result-normalize.ts): typed content blocks, workspace-saved
4131
+ * payloads, and `structured` kept by presence. An MCPL peer keeps the
4132
+ * RFC-005 reading. Its content array passes through when it holds
4133
+ * non-text blocks, so references and images render natively. It gets
4134
+ * `structured` too, with the JSON view when its content is empty. The
4135
+ * direct path (a module's callTool, a channel's tool call) doesn't come
4136
+ * here: it returns the server's result itself (directToolResult), for
4137
+ * either engine, as does a script's call whose result is structured, with
4138
+ * an MCPL peer's blocks given RFC-005's treatment (referenceBlockForScript).
4139
+ */
4140
+ private mcpToolResult;
3517
4141
  /**
3518
4142
  * Build BeforeInferenceParams from agent state and trigger context.
3519
4143
  */