@animalabs/agent-framework 0.22.1 → 0.22.2

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 (359) hide show
  1. package/README.md +1 -1
  2. package/dist/src/agent.d.ts +192 -19
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +404 -65
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/mcp-server-core.d.ts +41 -0
  7. package/dist/src/api/mcp-server-core.d.ts.map +1 -0
  8. package/dist/src/api/mcp-server-core.js +593 -0
  9. package/dist/src/api/mcp-server-core.js.map +1 -0
  10. package/dist/src/api/mcp-server.d.ts +2 -36
  11. package/dist/src/api/mcp-server.d.ts.map +1 -1
  12. package/dist/src/api/mcp-server.js +15 -595
  13. package/dist/src/api/mcp-server.js.map +1 -1
  14. package/dist/src/api/server.d.ts +9 -1
  15. package/dist/src/api/server.d.ts.map +1 -1
  16. package/dist/src/api/server.js +50 -26
  17. package/dist/src/api/server.js.map +1 -1
  18. package/dist/src/api/types.d.ts +9 -1
  19. package/dist/src/api/types.d.ts.map +1 -1
  20. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  21. package/dist/src/code-execution/py-runner.js.map +1 -1
  22. package/dist/src/framework.d.ts +393 -113
  23. package/dist/src/framework.d.ts.map +1 -1
  24. package/dist/src/framework.js +1748 -582
  25. package/dist/src/framework.js.map +1 -1
  26. package/dist/src/gate/event-gate.d.ts.map +1 -1
  27. package/dist/src/gate/gate-script.d.ts.map +1 -1
  28. package/dist/src/gate/gate-script.js.map +1 -1
  29. package/dist/src/index.d.ts +2 -2
  30. package/dist/src/index.d.ts.map +1 -1
  31. package/dist/src/index.js +2 -2
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/mcpl/capability-grant.d.ts.map +1 -1
  34. package/dist/src/mcpl/channel-registry.d.ts +26 -7
  35. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  36. package/dist/src/mcpl/channel-registry.js +102 -22
  37. package/dist/src/mcpl/channel-registry.js.map +1 -1
  38. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  39. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  40. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  41. package/dist/src/mcpl/errors.js.map +1 -1
  42. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
  43. package/dist/src/mcpl/feature-set-manager.js +4 -3
  44. package/dist/src/mcpl/feature-set-manager.js.map +1 -1
  45. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  46. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  47. package/dist/src/mcpl/log-value.d.ts +54 -0
  48. package/dist/src/mcpl/log-value.d.ts.map +1 -0
  49. package/dist/src/mcpl/log-value.js +109 -0
  50. package/dist/src/mcpl/log-value.js.map +1 -0
  51. package/dist/src/mcpl/prose-stream-router.d.ts +6 -6
  52. package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
  53. package/dist/src/mcpl/prose-stream-router.js +4 -4
  54. package/dist/src/mcpl/prose-stream-router.js.map +1 -1
  55. package/dist/src/mcpl/push-coalescer.d.ts +38 -3
  56. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  57. package/dist/src/mcpl/push-coalescer.js +81 -16
  58. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  59. package/dist/src/mcpl/push-handler.d.ts +26 -3
  60. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  61. package/dist/src/mcpl/push-handler.js +87 -23
  62. package/dist/src/mcpl/push-handler.js.map +1 -1
  63. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  64. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  65. package/dist/src/mcpl/references.d.ts +2 -2
  66. package/dist/src/mcpl/references.d.ts.map +1 -1
  67. package/dist/src/mcpl/references.js.map +1 -1
  68. package/dist/src/mcpl/refusal-lines.d.ts +37 -0
  69. package/dist/src/mcpl/refusal-lines.d.ts.map +1 -0
  70. package/dist/src/mcpl/refusal-lines.js +61 -0
  71. package/dist/src/mcpl/refusal-lines.js.map +1 -0
  72. package/dist/src/mcpl/scope-manager.d.ts.map +1 -1
  73. package/dist/src/mcpl/server-connection.d.ts +18 -2
  74. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  75. package/dist/src/mcpl/server-connection.js +66 -11
  76. package/dist/src/mcpl/server-connection.js.map +1 -1
  77. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  78. package/dist/src/mcpl/tool-classes.d.ts +1 -1
  79. package/dist/src/mcpl/tool-classes.d.ts.map +1 -1
  80. package/dist/src/mcpl/tool-classes.js +3 -2
  81. package/dist/src/mcpl/tool-classes.js.map +1 -1
  82. package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
  83. package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
  84. package/dist/src/mcpl/transport.d.ts +23 -5
  85. package/dist/src/mcpl/transport.d.ts.map +1 -1
  86. package/dist/src/mcpl/transport.js +103 -10
  87. package/dist/src/mcpl/transport.js.map +1 -1
  88. package/dist/src/mcpl/types.d.ts +30 -26
  89. package/dist/src/mcpl/types.d.ts.map +1 -1
  90. package/dist/src/mcpl/types.js.map +1 -1
  91. package/dist/src/module-registry.d.ts.map +1 -1
  92. package/dist/src/module-registry.js.map +1 -1
  93. package/dist/src/modules/api/index.d.ts.map +1 -1
  94. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
  95. package/dist/src/modules/discord/discord-js-client.js.map +1 -1
  96. package/dist/src/modules/discord/index.d.ts.map +1 -1
  97. package/dist/src/modules/discord/index.js +4 -2
  98. package/dist/src/modules/discord/index.js.map +1 -1
  99. package/dist/src/modules/health/index.d.ts.map +1 -1
  100. package/dist/src/modules/health/index.js.map +1 -1
  101. package/dist/src/modules/history/index.d.ts.map +1 -1
  102. package/dist/src/modules/history/index.js.map +1 -1
  103. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  104. package/dist/src/modules/history/semantic.js.map +1 -1
  105. package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
  106. package/dist/src/modules/workspace/branch-intent.js.map +1 -1
  107. package/dist/src/modules/workspace/disk-agreement.d.ts +22 -4
  108. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
  109. package/dist/src/modules/workspace/disk-agreement.js +63 -25
  110. package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
  111. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  112. package/dist/src/modules/workspace/index.d.ts +8 -4
  113. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  114. package/dist/src/modules/workspace/index.js +39 -12
  115. package/dist/src/modules/workspace/index.js.map +1 -1
  116. package/dist/src/modules/workspace/observe.d.ts +43 -8
  117. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  118. package/dist/src/modules/workspace/observe.js +264 -39
  119. package/dist/src/modules/workspace/observe.js.map +1 -1
  120. package/dist/src/modules/workspace/reconcile.d.ts +17 -1
  121. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  122. package/dist/src/modules/workspace/reconcile.js +57 -14
  123. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  124. package/dist/src/modules/workspace/types.d.ts +27 -2
  125. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  126. package/dist/src/modules/workspace/types.js.map +1 -1
  127. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  128. package/dist/src/modules/workspace/watcher.js.map +1 -1
  129. package/dist/src/operator-log.d.ts.map +1 -1
  130. package/dist/src/operator-log.js.map +1 -1
  131. package/dist/src/provider-waits.d.ts.map +1 -1
  132. package/dist/src/provider-waits.js.map +1 -1
  133. package/dist/src/queue.d.ts.map +1 -1
  134. package/dist/src/record-journal.d.ts.map +1 -1
  135. package/dist/src/record-journal.js.map +1 -1
  136. package/dist/src/recovery/discord-awareness-outbox.d.ts.map +1 -1
  137. package/dist/src/recovery/discord-awareness-outbox.js.map +1 -1
  138. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  139. package/dist/src/recovery/offline-branch.js +29 -24
  140. package/dist/src/recovery/offline-branch.js.map +1 -1
  141. package/dist/src/recovery/recover-cli.js.map +1 -1
  142. package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -1
  143. package/dist/src/runtime/liveness-watchdog.js.map +1 -1
  144. package/dist/src/shutdown.d.ts.map +1 -1
  145. package/dist/src/shutdown.js.map +1 -1
  146. package/dist/src/stored-removal.d.ts +73 -0
  147. package/dist/src/stored-removal.d.ts.map +1 -0
  148. package/dist/src/stored-removal.js +100 -0
  149. package/dist/src/stored-removal.js.map +1 -0
  150. package/dist/src/thinking-token-stamp.js.map +1 -1
  151. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  152. package/dist/src/tool-image-ledger.js.map +1 -1
  153. package/dist/src/tool-presentation.d.ts.map +1 -1
  154. package/dist/src/tool-presentation.js.map +1 -1
  155. package/dist/src/tool-result-guard.d.ts.map +1 -1
  156. package/dist/src/tool-result-guard.js.map +1 -1
  157. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  158. package/dist/src/tune-out/coordinator.js.map +1 -1
  159. package/dist/src/tune-out/tools.d.ts +1 -1
  160. package/dist/src/tune-out/tools.d.ts.map +1 -1
  161. package/dist/src/types/agent.d.ts +42 -6
  162. package/dist/src/types/agent.d.ts.map +1 -1
  163. package/dist/src/types/framework.d.ts +10 -0
  164. package/dist/src/types/framework.d.ts.map +1 -1
  165. package/dist/src/types/module.d.ts +8 -4
  166. package/dist/src/types/module.d.ts.map +1 -1
  167. package/dist/src/types/trace.d.ts +61 -10
  168. package/dist/src/types/trace.d.ts.map +1 -1
  169. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  170. package/dist/test/agent-runtime-settings.test.js +1139 -3
  171. package/dist/test/agent-runtime-settings.test.js.map +1 -1
  172. package/dist/test/batched-wake-locus.test.js.map +1 -1
  173. package/dist/test/channel-label-history.test.js.map +1 -1
  174. package/dist/test/code-execution.test.js.map +1 -1
  175. package/dist/test/conversation-fork-injection-scoping.test.js.map +1 -1
  176. package/dist/test/conversation-router.test.js.map +1 -1
  177. package/dist/test/conversation-routing.test.js +240 -6
  178. package/dist/test/conversation-routing.test.js.map +1 -1
  179. package/dist/test/discord-awareness-outbox.test.js +104 -0
  180. package/dist/test/discord-awareness-outbox.test.js.map +1 -1
  181. package/dist/test/discord-typing-keepalive.test.d.ts +2 -0
  182. package/dist/test/discord-typing-keepalive.test.d.ts.map +1 -0
  183. package/dist/test/discord-typing-keepalive.test.js +77 -0
  184. package/dist/test/discord-typing-keepalive.test.js.map +1 -0
  185. package/dist/test/ephemeral-settle.test.js.map +1 -1
  186. package/dist/test/event-gate-passthrough.test.js.map +1 -1
  187. package/dist/test/event-gate-policy-mutation.test.js.map +1 -1
  188. package/dist/test/event-gate-rate-limit.test.js.map +1 -1
  189. package/dist/test/event-gate-script.test.js.map +1 -1
  190. package/dist/test/event-gate-selfwake.test.js.map +1 -1
  191. package/dist/test/event-gate-tags.test.js.map +1 -1
  192. package/dist/test/event-gate.test.js.map +1 -1
  193. package/dist/test/explicit-prose-routing.test.js +363 -42
  194. package/dist/test/explicit-prose-routing.test.js.map +1 -1
  195. package/dist/test/framework.test.js.map +1 -1
  196. package/dist/test/helpers/coalescing-fixture.d.ts +2 -0
  197. package/dist/test/helpers/coalescing-fixture.d.ts.map +1 -1
  198. package/dist/test/helpers/coalescing-fixture.js +8 -2
  199. package/dist/test/helpers/coalescing-fixture.js.map +1 -1
  200. package/dist/test/helpers/mock-membrane.d.ts.map +1 -1
  201. package/dist/test/helpers/mock-membrane.js.map +1 -1
  202. package/dist/test/hide-then-turn.test.d.ts +2 -0
  203. package/dist/test/hide-then-turn.test.d.ts.map +1 -0
  204. package/dist/test/hide-then-turn.test.js +101 -0
  205. package/dist/test/hide-then-turn.test.js.map +1 -0
  206. package/dist/test/history-module-overview.test.js.map +1 -1
  207. package/dist/test/history-module-semantic.test.js.map +1 -1
  208. package/dist/test/history-module.test.js.map +1 -1
  209. package/dist/test/history-semantic-review-findings.test.js.map +1 -1
  210. package/dist/test/host-quiesce.test.js +125 -0
  211. package/dist/test/host-quiesce.test.js.map +1 -1
  212. package/dist/test/inference-exhaustion-rewind.test.js +81 -3
  213. package/dist/test/inference-exhaustion-rewind.test.js.map +1 -1
  214. package/dist/test/inference-failure-bounds.test.js +1 -0
  215. package/dist/test/inference-failure-bounds.test.js.map +1 -1
  216. package/dist/test/kv-unified-open-flight-retry.test.js.map +1 -1
  217. package/dist/test/liveness-watchdog.test.js.map +1 -1
  218. package/dist/test/maintenance-timer.test.js +1564 -2
  219. package/dist/test/maintenance-timer.test.js.map +1 -1
  220. package/dist/test/mcp-server-bin.test.d.ts +2 -0
  221. package/dist/test/mcp-server-bin.test.d.ts.map +1 -0
  222. package/dist/test/mcp-server-bin.test.js +142 -0
  223. package/dist/test/mcp-server-bin.test.js.map +1 -0
  224. package/dist/test/mcpl-awareness-barrier.test.js.map +1 -1
  225. package/dist/test/mcpl-child-env.test.js +1 -1
  226. package/dist/test/mcpl-child-env.test.js.map +1 -1
  227. package/dist/test/mcpl-coalesced-content-parity.test.d.ts +2 -0
  228. package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +1 -0
  229. package/dist/test/mcpl-coalesced-content-parity.test.js +532 -0
  230. package/dist/test/mcpl-coalesced-content-parity.test.js.map +1 -0
  231. package/dist/test/mcpl-coalescing.test.js.map +1 -1
  232. package/dist/test/mcpl-config-diagnostics.test.js.map +1 -1
  233. package/dist/test/mcpl-gate-roundtrip.test.js.map +1 -1
  234. package/dist/test/mcpl-manifest-changed.test.js.map +1 -1
  235. package/dist/test/mcpl-reconnect-events.test.js.map +1 -1
  236. package/dist/test/mcpl-reconnect-reregistration.test.js +2 -0
  237. package/dist/test/mcpl-reconnect-reregistration.test.js.map +1 -1
  238. package/dist/test/mcpl-reference-fetcher.test.js.map +1 -1
  239. package/dist/test/mcpl-reference-fields.test.js.map +1 -1
  240. package/dist/test/mcpl-rejection-log-lines.test.d.ts +2 -0
  241. package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +1 -0
  242. package/dist/test/mcpl-rejection-log-lines.test.js +349 -0
  243. package/dist/test/mcpl-rejection-log-lines.test.js.map +1 -0
  244. package/dist/test/mcpl-stderr-tail.test.d.ts +2 -0
  245. package/dist/test/mcpl-stderr-tail.test.d.ts.map +1 -0
  246. package/dist/test/mcpl-stderr-tail.test.js +210 -0
  247. package/dist/test/mcpl-stderr-tail.test.js.map +1 -0
  248. package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +1 -1
  249. package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +1 -1
  250. package/dist/test/mcpl-tool-lifecycle-script.test.js.map +1 -1
  251. package/dist/test/mcpl-tool-lifecycle.test.js.map +1 -1
  252. package/dist/test/mcpl-transport-close.test.d.ts +2 -0
  253. package/dist/test/mcpl-transport-close.test.d.ts.map +1 -0
  254. package/dist/test/mcpl-transport-close.test.js +373 -0
  255. package/dist/test/mcpl-transport-close.test.js.map +1 -0
  256. package/dist/test/mcpl-transport-spawn.test.d.ts +2 -0
  257. package/dist/test/mcpl-transport-spawn.test.d.ts.map +1 -0
  258. package/dist/test/mcpl-transport-spawn.test.js +58 -0
  259. package/dist/test/mcpl-transport-spawn.test.js.map +1 -0
  260. package/dist/test/mcpl-ws-transport.test.js +1 -0
  261. package/dist/test/mcpl-ws-transport.test.js.map +1 -1
  262. package/dist/test/offline-recovery-branch.test.js +344 -0
  263. package/dist/test/offline-recovery-branch.test.js.map +1 -1
  264. package/dist/test/operator-surgery.test.js +197 -0
  265. package/dist/test/operator-surgery.test.js.map +1 -1
  266. package/dist/test/overbudget-drain-breaker.test.js +1 -0
  267. package/dist/test/overbudget-drain-breaker.test.js.map +1 -1
  268. package/dist/test/pack-installs-alone.test.d.ts +2 -0
  269. package/dist/test/pack-installs-alone.test.d.ts.map +1 -0
  270. package/dist/test/pack-installs-alone.test.js +284 -0
  271. package/dist/test/pack-installs-alone.test.js.map +1 -0
  272. package/dist/test/physical-window-restart.test.d.ts +6 -4
  273. package/dist/test/physical-window-restart.test.d.ts.map +1 -1
  274. package/dist/test/physical-window-restart.test.js +275 -10
  275. package/dist/test/physical-window-restart.test.js.map +1 -1
  276. package/dist/test/placed-reaction-baseline.test.js.map +1 -1
  277. package/dist/test/present-while-acting.test.js +949 -6
  278. package/dist/test/present-while-acting.test.js.map +1 -1
  279. package/dist/test/provider-acceleration-cooldown.test.js.map +1 -1
  280. package/dist/test/provider-waits.test.js +67 -4
  281. package/dist/test/provider-waits.test.js.map +1 -1
  282. package/dist/test/puppet-tool-call.test.js +2 -0
  283. package/dist/test/puppet-tool-call.test.js.map +1 -1
  284. package/dist/test/reaction-baseline-platform.test.js.map +1 -1
  285. package/dist/test/record-journal.test.js.map +1 -1
  286. package/dist/test/refusal-rewind.test.js +9 -0
  287. package/dist/test/refusal-rewind.test.js.map +1 -1
  288. package/dist/test/refusal-single-call.test.js.map +1 -1
  289. package/dist/test/release-bump.test.d.ts +2 -0
  290. package/dist/test/release-bump.test.d.ts.map +1 -0
  291. package/dist/test/release-bump.test.js +248 -0
  292. package/dist/test/release-bump.test.js.map +1 -0
  293. package/dist/test/release-changelog.test.js.map +1 -1
  294. package/dist/test/release-holds.test.d.ts +2 -0
  295. package/dist/test/release-holds.test.d.ts.map +1 -0
  296. package/dist/test/release-holds.test.js +456 -0
  297. package/dist/test/release-holds.test.js.map +1 -0
  298. package/dist/test/same-round-think-policy.test.js.map +1 -1
  299. package/dist/test/shutdown-errors.test.js.map +1 -1
  300. package/dist/test/silent-heartbeat-rows.test.js.map +1 -1
  301. package/dist/test/stop-with-host-operations.test.d.ts +20 -0
  302. package/dist/test/stop-with-host-operations.test.d.ts.map +1 -0
  303. package/dist/test/stop-with-host-operations.test.js +1174 -0
  304. package/dist/test/stop-with-host-operations.test.js.map +1 -0
  305. package/dist/test/stop-with-work-in-flight.test.js +162 -50
  306. package/dist/test/stop-with-work-in-flight.test.js.map +1 -1
  307. package/dist/test/surgery-marks-receipt.test.js.map +1 -1
  308. package/dist/test/tool-batch-hook.test.js.map +1 -1
  309. package/dist/test/tool-result-guard.test.js +131 -9
  310. package/dist/test/tool-result-guard.test.js.map +1 -1
  311. package/dist/test/tool-result-spill.test.js.map +1 -1
  312. package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +1 -1
  313. package/dist/test/trunk-channel-routing.test.js +330 -7
  314. package/dist/test/trunk-channel-routing.test.js.map +1 -1
  315. package/dist/test/tune-out-e2e.test.js.map +1 -1
  316. package/dist/test/unconvertible-tool-result.test.js.map +1 -1
  317. package/dist/test/unsent-compiles.test.d.ts +2 -0
  318. package/dist/test/unsent-compiles.test.d.ts.map +1 -0
  319. package/dist/test/unsent-compiles.test.js +139 -0
  320. package/dist/test/unsent-compiles.test.js.map +1 -0
  321. package/dist/test/workspace-branch-continuation.test.js.map +1 -1
  322. package/dist/test/workspace-materialize-freshness.test.js.map +1 -1
  323. package/dist/test/workspace-read-text.test.js.map +1 -1
  324. package/dist/test/workspace-three-way.test.js +907 -7
  325. package/dist/test/workspace-three-way.test.js.map +1 -1
  326. package/dist/test/workspace-watcher.test.js.map +1 -1
  327. package/dist/test/xml-round-persist.test.js.map +1 -1
  328. package/dist/tsconfig.tsbuildinfo +1 -1
  329. package/package.json +8 -6
  330. package/src/agent.ts +411 -66
  331. package/src/api/mcp-server-core.ts +681 -0
  332. package/src/api/mcp-server.ts +16 -685
  333. package/src/api/server.ts +51 -29
  334. package/src/api/types.ts +9 -1
  335. package/src/framework.ts +1899 -641
  336. package/src/index.ts +2 -2
  337. package/src/mcpl/channel-registry.ts +126 -31
  338. package/src/mcpl/feature-set-manager.ts +4 -3
  339. package/src/mcpl/log-value.ts +116 -0
  340. package/src/mcpl/prose-stream-router.ts +6 -6
  341. package/src/mcpl/push-coalescer.ts +76 -12
  342. package/src/mcpl/push-handler.ts +95 -23
  343. package/src/mcpl/refusal-lines.ts +59 -0
  344. package/src/mcpl/server-connection.ts +70 -11
  345. package/src/mcpl/tool-classes.ts +3 -2
  346. package/src/mcpl/transport.ts +105 -14
  347. package/src/mcpl/types.ts +5 -1
  348. package/src/modules/discord/index.ts +4 -2
  349. package/src/modules/workspace/disk-agreement.ts +74 -18
  350. package/src/modules/workspace/index.ts +41 -11
  351. package/src/modules/workspace/observe.ts +265 -39
  352. package/src/modules/workspace/reconcile.ts +71 -14
  353. package/src/modules/workspace/types.ts +26 -1
  354. package/src/recovery/offline-branch.ts +32 -28
  355. package/src/stored-removal.ts +126 -0
  356. package/src/types/agent.ts +42 -6
  357. package/src/types/framework.ts +10 -0
  358. package/src/types/module.ts +8 -4
  359. package/src/types/trace.ts +62 -11
@@ -1,5 +1,6 @@
1
1
  import { JsStore } from '@animalabs/chronicle';
2
- import { ContextManager } from '@animalabs/context-manager';
2
+ import { ContextManager, continuesBody, type BodyShard } from '@animalabs/context-manager';
3
+ import { removeStoredInterval } from '../stored-removal.js';
3
4
  import {
4
5
  DEFAULT_DISCORD_AWARENESS_EMOJI,
5
6
  DiscordAwarenessOutbox,
@@ -202,11 +203,12 @@ export async function createOfflineRecoveryBranch(
202
203
  const createdBranch = contextManager.branchAt(target.id, targetBranch);
203
204
  try {
204
205
  await contextManager.switchBranch(createdBranch);
205
- // Work from newest to oldest so earlier redactions never perturb the
206
- // live positions used by later range endpoints.
206
+ // Each interval is removed by its messages' ids, so the order doesn't
207
+ // change the result. removeStoredInterval takes a single message outside
208
+ // any body through removeMessage, and a shard or a longer interval as a
209
+ // range, which refuses one that splits a body.
207
210
  for (const interval of [...suppression.intervals].reverse()) {
208
- if (interval.fromId === interval.toId) contextManager.removeMessage(interval.fromId);
209
- else contextManager.removeMessages(interval.fromId, interval.toId);
211
+ removeStoredInterval(contextManager, interval.fromId, interval.toId);
210
212
  }
211
213
  if (batch) outbox.activate(batch.id);
212
214
  } catch (error) {
@@ -238,9 +240,7 @@ interface SuppressionInterval {
238
240
 
239
241
  interface MessageLocation {
240
242
  first: number;
241
- last: number;
242
243
  firstId: StoredWindowMessage['id'];
243
- lastId: StoredWindowMessage['id'];
244
244
  /** Usually one run; multiple runs tolerate duplicate external metadata. */
245
245
  runs: SuppressionInterval[];
246
246
  }
@@ -299,14 +299,15 @@ function validateAnchorBoundary(
299
299
  total: number,
300
300
  target: StoredWindowMessage,
301
301
  ): void {
302
- const groupId = (target as StoredWindowMessage & { bodyGroupId?: string }).bodyGroupId;
303
- if (!groupId || targetIndex + 1 >= total) return;
302
+ if (targetIndex + 1 >= total) return;
304
303
  const next = contextManager.getMessageWindow(targetIndex + 1, 1, {
305
304
  resolveBlobs: false,
306
- }).messages[0] as (StoredWindowMessage & { bodyGroupId?: string }) | undefined;
307
- if (next?.bodyGroupId === groupId) {
305
+ }).messages[0];
306
+ // A body continues only into a later shard of the same ingestion: a
307
+ // reposted document shares its group id but starts a body of its own.
308
+ if (continuesBody(target as BodyShard, next as BodyShard | undefined)) {
308
309
  throw new Error(
309
- `Recovery anchor ${String(target.id)} splits body group ${groupId}; choose its final shard.`,
310
+ `Recovery anchor ${String(target.id)} splits body group ${(target as BodyShard).bodyGroupId}; choose its final shard.`,
310
311
  );
311
312
  }
312
313
  }
@@ -321,7 +322,9 @@ function validateRemovalIntegrity(
321
322
  index >= interval.start && index <= interval.end);
322
323
  const toolUses = new Map<string, boolean>();
323
324
  const toolResults = new Map<string, boolean>();
324
- const groupMembership = new Map<string, boolean>();
325
+ // The message before, in store order: a selection splits a body when a
326
+ // message continuing the one before it lands on the other side.
327
+ let previous: { message: BodyShard; removed: boolean } | null = null;
325
328
 
326
329
  for (let offset = 0; offset < total; offset += RECOVERY_SCAN_WINDOW) {
327
330
  const messages = contextManager.getMessageWindow(
@@ -332,16 +335,14 @@ function validateRemovalIntegrity(
332
335
  for (let i = 0; i < messages.length; i++) {
333
336
  const index = offset + i;
334
337
  const isRemoved = removed(index);
335
- const message = messages[i] as StoredWindowMessage & { bodyGroupId?: string };
336
- if (message.bodyGroupId) {
337
- const prior = groupMembership.get(message.bodyGroupId);
338
- if (prior !== undefined && prior !== isRemoved) {
339
- throw new Error(
340
- `Recovery selection splits body group ${message.bodyGroupId}; adjust the anchor or suppression boundary.`,
341
- );
342
- }
343
- groupMembership.set(message.bodyGroupId, isRemoved);
338
+ const message = messages[i];
339
+ const shard = message as BodyShard;
340
+ if (previous && previous.removed !== isRemoved && continuesBody(previous.message, shard)) {
341
+ throw new Error(
342
+ `Recovery selection splits body group ${shard.bodyGroupId}; adjust the anchor or suppression boundary.`,
343
+ );
344
344
  }
345
+ previous = { message: shard, removed: isRemoved };
345
346
  for (const rawBlock of message.content) {
346
347
  const block = rawBlock as { type?: unknown; id?: unknown; toolUseId?: unknown };
347
348
  if (block.type === 'tool_use' && typeof block.id === 'string') {
@@ -402,9 +403,7 @@ function buildSuppressionPlan(
402
403
  };
403
404
  locations.set(externalId, {
404
405
  first: index,
405
- last: index,
406
406
  firstId: messages[i].id,
407
- lastId: messages[i].id,
408
407
  runs: [run],
409
408
  });
410
409
  } else {
@@ -420,8 +419,6 @@ function buildSuppressionPlan(
420
419
  toId: messages[i].id,
421
420
  });
422
421
  }
423
- location.last = index;
424
- location.lastId = messages[i].id;
425
422
  }
426
423
  }
427
424
  }
@@ -444,12 +441,19 @@ function buildSuppressionPlan(
444
441
  const to = locations.get(range.toMessageId)!;
445
442
  const earlier = from.first <= to.first ? from : to;
446
443
  const later = from.first <= to.first ? to : from;
444
+ // What's between the two messages, after the earlier one began and
445
+ // before the later one began: from the earlier one's first stored copy
446
+ // through the later one's first. Each end is every copy of it besides,
447
+ // as a single suppression is, so a copy stored outside that span (a
448
+ // write cut short and retried later) is taken on its own, and nothing
449
+ // else stored after the later one began is taken.
447
450
  intervals.push({
448
451
  start: earlier.first,
449
- end: later.last,
452
+ end: later.first,
450
453
  fromId: earlier.firstId,
451
- toId: later.lastId,
454
+ toId: later.firstId,
452
455
  });
456
+ for (const end of [from, to]) intervals.push(...end.runs.map((run) => ({ ...run })));
453
457
  }
454
458
 
455
459
  intervals.sort((a, b) => a.start - b.start || a.end - b.end);
@@ -0,0 +1,126 @@
1
+ import { bodyBoundsIn, type BodyShard, type ContextManager, type MessageId } from '@animalabs/context-manager';
2
+
3
+ type StoredLike = BodyShard & { id: MessageId };
4
+ type Removals = Pick<ContextManager, 'removeMessage' | 'removeMessages'>;
5
+
6
+ /** Minimal read surface the live-surgery helpers need from a ContextManager. */
7
+ export interface SurgeryReadableCm {
8
+ getMessageCount(): number;
9
+ getMessageWindow(
10
+ offset: number,
11
+ limit: number,
12
+ opts?: { resolveBlobs?: boolean; alignToBodyGroups?: boolean },
13
+ ): { messages: Array<{ id: unknown; bodyGroupId?: string }>; startIndex: number };
14
+ }
15
+
16
+ const SURGERY_SCAN_WINDOW = 500;
17
+
18
+ /**
19
+ * Slot index of `messageId` on the active branch, or -1. Scans from the tail
20
+ * in blob-free windows: operator targets are almost always recent, and a
21
+ * full `getAllMessages()` re-inflates every attachment on the branch.
22
+ */
23
+ export function locateMessageIndex(cm: SurgeryReadableCm, messageId: string): number {
24
+ const hit = locateMessageIndices(cm, new Set([messageId])).get(messageId);
25
+ return hit ? hit.index : -1;
26
+ }
27
+
28
+ /** Tail-first scan resolving many ids at once; stops as soon as all are found. */
29
+ export function locateMessageIndices(
30
+ cm: SurgeryReadableCm,
31
+ ids: Set<string>,
32
+ ): Map<string, { index: number; bodyGroupId?: string }> {
33
+ const found = new Map<string, { index: number; bodyGroupId?: string }>();
34
+ const total = cm.getMessageCount();
35
+ for (let end = total; end > 0 && found.size < ids.size; end -= SURGERY_SCAN_WINDOW) {
36
+ const start = Math.max(0, end - SURGERY_SCAN_WINDOW);
37
+ const { messages, startIndex } = cm.getMessageWindow(start, end - start, { resolveBlobs: false });
38
+ for (let i = messages.length - 1; i >= 0; i--) {
39
+ const id = String(messages[i].id);
40
+ if (ids.has(id) && !found.has(id)) {
41
+ found.set(id, {
42
+ index: startIndex + i,
43
+ ...(messages[i].bodyGroupId ? { bodyGroupId: messages[i].bodyGroupId } : {}),
44
+ });
45
+ }
46
+ }
47
+ }
48
+ return found;
49
+ }
50
+
51
+ /**
52
+ * Inclusive slot range of the body-group run containing `index` (the slot
53
+ * itself when it is not sharded). Delegates to the store's own
54
+ * `alignToBodyGroups` edge walk — one contiguity assumption, not two.
55
+ */
56
+ export function bodyGroupRun(
57
+ cm: SurgeryReadableCm,
58
+ index: number,
59
+ ): { from: number; to: number; fromId: string; toId: string; group: boolean } {
60
+ const win = cm.getMessageWindow(index, 1, { alignToBodyGroups: true, resolveBlobs: false });
61
+ const from = win.startIndex;
62
+ const to = win.startIndex + win.messages.length - 1;
63
+ return {
64
+ from,
65
+ to,
66
+ fromId: String(win.messages[0].id),
67
+ toId: String(win.messages[win.messages.length - 1].id),
68
+ group: win.messages[0].bodyGroupId !== undefined,
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Remove the stored messages `first`..`last` (inclusive, in store order).
74
+ *
75
+ * A single message outside any body goes through `removeMessage`.
76
+ * context-manager's `removeMessage` refuses any message that carries a body
77
+ * group id, a one-shard body's included, so a shard, and any longer interval,
78
+ * goes through `removeMessages`, which refuses an interval that splits a
79
+ * body.
80
+ */
81
+ export function removeStoredMessages(cm: Removals, first: StoredLike, last: StoredLike): void {
82
+ if (first.id === last.id && !first.bodyGroupId) {
83
+ cm.removeMessage(first.id);
84
+ return;
85
+ }
86
+ cm.removeMessages(first.id, last.id);
87
+ }
88
+
89
+ /**
90
+ * Remove one stored message by its id, with the rest of its body when it's
91
+ * in one: a module or a coalescer removes a message it added by the id
92
+ * `addMessage` returned, which for a long message stored in shards is its
93
+ * first shard's.
94
+ */
95
+ export function removeStoredMessage(cm: ContextManager, id: MessageId): void {
96
+ const message = cm.getMessage(id) as StoredLike | null;
97
+ if (!message?.bodyGroupId) {
98
+ cm.removeMessage(id);
99
+ return;
100
+ }
101
+ // Found in blob-free windows: a full read of the branch would inflate every
102
+ // attachment on it, once for each message a sweep removes.
103
+ const index = locateMessageIndex(cm, String(id));
104
+ if (index < 0) {
105
+ cm.removeMessage(id);
106
+ return;
107
+ }
108
+ const run = bodyGroupRun(cm, index);
109
+ cm.removeMessages(run.fromId as MessageId, run.toId as MessageId);
110
+ }
111
+
112
+ /** removeStoredMessages for an interval known by its ids. */
113
+ export function removeStoredInterval(cm: ContextManager, fromId: MessageId, toId: MessageId): void {
114
+ const first = cm.getMessage(fromId) as StoredLike | null;
115
+ removeStoredMessages(cm, first ?? { id: fromId }, { id: toId });
116
+ }
117
+
118
+ /**
119
+ * The first and last positions (inclusive) of the body that holds
120
+ * `messages[index]`, read the way context-manager 0.14.0 reads bodies
121
+ * (`continuesBody`, pairwise in store order). A message outside any body is a
122
+ * body of its own.
123
+ */
124
+ export function bodyRangeIn(messages: readonly unknown[], index: number): { from: number; to: number } {
125
+ return bodyBoundsIn(messages as readonly (BodyShard | null | undefined)[], index);
126
+ }
@@ -53,11 +53,14 @@ export interface AgentConfig {
53
53
  /**
54
54
  * The model's PHYSICAL context window (provider hard cap, e.g. 200000).
55
55
  * When set, the framework projects each continuation round's real size
56
- * (cache-inclusive input of the prior round + the blocks about to be
57
- * appended + reserve for response) and restarts the stream through a fresh
58
- * compile instead of dispatching a request the provider will 400
59
- * (issue #92: a legal compile can walk past the physical window mid-turn).
60
- * Unset → no projection; only the maxStreamTokens restart applies.
56
+ * (cache-inclusive input of the latest call that reported its prompt +
57
+ * that call's output + what the stream has grown by since, if later calls
58
+ * reported none + the blocks about to be appended + reserve for response)
59
+ * and restarts the stream through a fresh compile instead of dispatching a
60
+ * request the provider will 400 (issue #92: a legal compile can walk past
61
+ * the physical window mid-turn). The projection runs once a call on the
62
+ * stream has reported its prompt. Unset → no projection; only the
63
+ * maxStreamTokens restart applies.
61
64
  */
62
65
  physicalWindowTokens?: number;
63
66
 
@@ -223,7 +226,20 @@ export interface AgentRuntimeSettingsPatch {
223
226
  }
224
227
 
225
228
  export interface AgentRuntimeSettingsSnapshot {
229
+ /**
230
+ * The budget the agent is set to: while a lower one converges, its target,
231
+ * and while a persisted one the agent couldn't apply is kept, that one.
232
+ */
226
233
  contextBudgetTokens: number;
234
+ /**
235
+ * The budget compiles plan at now: the live budget, or the default when the
236
+ * recipe sets none (the context manager's own default is the same). It
237
+ * equals `contextBudgetTokens` except while a lower budget converges, when
238
+ * it is still the budget the descent started from (context-manager #162),
239
+ * and while a budget the agent couldn't apply is kept, when it is the
240
+ * budget the agent runs at instead.
241
+ */
242
+ effectiveContextBudgetTokens: number;
227
243
  tailTokens?: number;
228
244
  transitionPaceTokens?: number;
229
245
  sameRoundThinkTextPolicy: SameRoundThinkTextPolicy;
@@ -233,7 +249,27 @@ export interface AgentRuntimeSettingsSnapshot {
233
249
  }
234
250
 
235
251
  /** Persisted values differ from recipe defaults; omitted keys inherit config. */
236
- export type AgentRuntimeSettingsOverrides = AgentRuntimeSettingsPatch;
252
+ export type AgentRuntimeSettingsOverrides = AgentRuntimeSettingsPatch & {
253
+ /**
254
+ * The record's own `contextBudgetTokens`, written when a compile at the
255
+ * agent's live budget fits at it, with nothing about the budget changed
256
+ * while it ran (a paced descent's settle included): the store fit that
257
+ * budget. A compile the context manager refuses at it as over budget
258
+ * withdraws it, unless a compile at it that started later has already
259
+ * ended.
260
+ * When it equals `contextBudgetTokens`, a restore applies that budget at
261
+ * once, rather than pacing a descent again from the recipe's budget
262
+ * (context-manager #162), if a preview at boot says it still fits; if not,
263
+ * a budget below the recipe's restores paced, and if a compile at one that
264
+ * the boot applied at once is refused, the agent restores it paced then.
265
+ * Since the store fit it when the field was written, the planner can fold
266
+ * to it at once even if its carried frontier was lost. Any other value is
267
+ * stale (the budget changed since, or an earlier release carried the field
268
+ * forward), and a budget below the recipe's restores paced, as it does
269
+ * without the field. Never part of a patch.
270
+ */
271
+ contextBudgetReachedTokens?: number;
272
+ };
237
273
 
238
274
  /**
239
275
  * Result of running inference.
@@ -302,6 +302,10 @@ export interface ContextMaintenanceAgentRun {
302
302
  pendingAfter?: string;
303
303
  progressBefore?: Record<string, unknown>;
304
304
  progressAfter?: Record<string, unknown>;
305
+ /**
306
+ * The agent's failure in this pass: a tick that threw, or its readiness
307
+ * that couldn't be read, before the ticks or after them.
308
+ */
305
309
  error?: string;
306
310
  }
307
311
 
@@ -323,6 +327,12 @@ export interface ContextMaintenanceSnapshot {
323
327
  agentName: string;
324
328
  ready: boolean;
325
329
  pending?: string;
330
+ /**
331
+ * Why the agent's readiness can't be read: the context manager refuses
332
+ * while its strategy's initialization is pending or has failed, and a
333
+ * strategy can refuse on its own. `ready` is false then.
334
+ */
335
+ error?: string;
326
336
  progress?: Record<string, unknown>;
327
337
  }>;
328
338
  }
@@ -321,10 +321,14 @@ export interface ModuleContext {
321
321
  *
322
322
  * Modules use this to react to agent lifecycle without polling — e.g. an
323
323
  * adapter that wants to show a "typing" indicator can bracket it between
324
- * `inference:started` and the terminal events (`inference:turn_ended`,
325
- * `inference:aborted`, `inference:failed`, `inference:exhausted`), all of
326
- * which carry `agentName`. The terminal events fire once per turn, after any
327
- * tool-call cycles, so a single start/stop bracket spans the whole turn.
324
+ * `inference:started` and the terminal events: `inference:completed` (the
325
+ * normal end), `inference:turn_ended` (a tool result ended the turn),
326
+ * `inference:aborted`, `inference:failed` and `inference:exhausted`, all of
327
+ * which carry `agentName`. A turn's tool-call cycles stay inside its
328
+ * stream, so one bracket spans them. A context-budget restart emits
329
+ * `inference:started` again with no terminal event for the stream it
330
+ * replaced, and a failing turn can emit `inference:failed` and then
331
+ * `inference:exhausted`, so a bracket should allow a repeated start or end.
328
332
  *
329
333
  * Returns an unsubscribe function. Call it in the module's `stop()` to avoid
330
334
  * leaking listeners across teardown/recreate (e.g. session switch).
@@ -41,11 +41,14 @@ export type TraceEvent =
41
41
  type: 'inference:started';
42
42
  agentName: string;
43
43
  /**
44
- * The turn-frozen outbound locus (see turnLocusPins) — the channel this
45
- * turn's plain prose will be routed to. Omitted for turns with no locus
46
- * (heartbeats with no default channel). Observability only, like every
47
- * trace field; lets external taps (e.g. a TTS relay) tag the whole
48
- * activation with its channel without re-deriving routing.
44
+ * The turn's locus as this stream starts (locus and hybrid mode): where
45
+ * its unprefixed plain prose is published, unless a hybrid `>>>`
46
+ * destination takes it, or a re-pin or a `channel_open` moves the locus
47
+ * later in the turn. Omitted for a turn with no locus: explicit and
48
+ * disabled mode, a silent turn, and a heartbeat with no default
49
+ * channel. Observability only, like every trace field; lets external
50
+ * taps (e.g. a TTS relay) tag the whole activation with its channel
51
+ * without re-deriving routing.
49
52
  */
50
53
  channelId?: string;
51
54
  /** True for a control-plane wake that must not surface typing/prose. */
@@ -60,7 +63,15 @@ export type TraceEvent =
60
63
  | (TraceEventBase & {
61
64
  type: 'inference:aborted';
62
65
  agentName: string;
66
+ /** How long this start of the turn ran; after a context-budget restart, from the restart. */
63
67
  durationMs: number;
68
+ /**
69
+ * Why the turn ended unfinished: the reason an operator gave
70
+ * `abortInference`, `'quiesce_abandoned'` for a turn an operator quiesce
71
+ * abandoned, or, for a turn dropped before it sent anything (its agent
72
+ * replaced or removed, or the framework stopping or restarted), a
73
+ * sentence saying which.
74
+ */
64
75
  reason?: string;
65
76
  })
66
77
  | (TraceEventBase & {
@@ -104,7 +115,9 @@ export type TraceEvent =
104
115
  blockType: 'text' | 'thinking' | 'tool_call' | 'tool_result';
105
116
  /** 0-indexed block position in the current assistant turn. */
106
117
  blockIndex: number;
107
- /** Turn-frozen outbound locus for this turn (see inference:started). */
118
+ /** The channel this stream shows typing in: the turn's locus as the
119
+ * stream started (see inference:started), or in explicit mode the
120
+ * channel the start's wake came from. */
108
121
  channelId?: string;
109
122
  })
110
123
  | (TraceEventBase & {
@@ -119,7 +132,9 @@ export type TraceEvent =
119
132
  phase: 'block_start' | 'block_complete';
120
133
  blockType: 'text' | 'thinking' | 'tool_call' | 'tool_result';
121
134
  blockIndex: number;
122
- /** Turn-frozen outbound locus for this turn (see inference:started). */
135
+ /** The channel this stream shows typing in: the turn's locus as the
136
+ * stream started (see inference:started), or in explicit mode the
137
+ * channel the start's wake came from. */
123
138
  channelId?: string;
124
139
  })
125
140
  | (TraceEventBase & {
@@ -236,22 +251,47 @@ export type TraceEvent =
236
251
  messageId: string;
237
252
  source: string;
238
253
  })
239
- /** A push/event refused at the MCPL boundary (today: visibly-empty content). */
254
+ /** A push/event refused at the MCPL boundary: visibly-empty content
255
+ * (`empty-content`, answered -32602, with no `field` or `detail`), or a
256
+ * coalesced occurrence the host refused. A refused occurrence's reason
257
+ * follows the code the producer was answered with: `coalesce-invalid` when
258
+ * the occurrence was the producer's to fix (-32602 malformed; -32017 or
259
+ * -32023 for a channel not permitted or not registered), `coalesce-failed`
260
+ * when the host itself failed (-32000, -32603). Its trace carries the
261
+ * error's message in `detail`, and the failing `field` when the error
262
+ * names one, and `channelId` when the push is refused for its channel
263
+ * (-32017, -32023), so each refusal names its channel even while repeats
264
+ * of its line are held back. An `eventId` that is missing or isn't a
265
+ * string is traced as `''`. A push refused for its feature set, or as a
266
+ * duplicate, gets a `[push-event-rejected]` line and no trace. */
240
267
  | (TraceEventBase & {
241
268
  type: 'mcpl:push-event-rejected';
242
269
  serverId: string;
243
270
  eventId: string;
244
271
  featureSet: string;
245
272
  reason: string;
246
- })
247
- /** A channels/incoming message refused at the MCPL boundary: unregistered
248
- * channel (§14.5), or visibly-empty content (then `messageId` is set). */
273
+ field?: string;
274
+ channelId?: string;
275
+ detail?: string;
276
+ })
277
+ /** A channels/incoming message refused at the MCPL boundary: an
278
+ * unregistered channel (`unknown-channel`, §14.5), visibly-empty content
279
+ * (`empty-content`), or a coalesced item the host refused
280
+ * (`coalesce-invalid` / `coalesce-failed`, with `detail`, and `field` when
281
+ * the error names one, as for a push). A coalesced item refused with
282
+ * -32602 (malformed) is answered `coalesce_invalid`, and one refused
283
+ * otherwise (-32000, or an internal error) with the error's own message.
284
+ * `messageId` is set whenever the message carries one. An uncoalesced
285
+ * message without a channelId or messageId is answered `invalid
286
+ * channel/message identity`, with no line and no trace. */
249
287
  | (TraceEventBase & {
250
288
  type: 'mcpl:channel-incoming-rejected';
251
289
  serverId: string;
252
290
  channelId: string;
253
291
  messageId?: string;
254
292
  reason: string;
293
+ field?: string;
294
+ detail?: string;
255
295
  })
256
296
  /** MCPL content with nothing model-visible reached the store/wake site
257
297
  * (module-emitted or coalescer-delivered) and was dropped: no row, no wake. */
@@ -441,6 +481,12 @@ export type TraceEvent =
441
481
  type: 'mcpl:server-connect-failed';
442
482
  serverId: string;
443
483
  error: string;
484
+ /** The last of the server's stderr, when its handshake failed by its
485
+ * transport closing, a transport error or its timeout and it had
486
+ * written any: the server's own text, which `error` never carries. A
487
+ * transport that failed its handshake was never wired, so its lines
488
+ * never reached `mcpl:server-stderr`. */
489
+ stderrTail?: string;
444
490
  /** Ordinal of the failed attempt: 0 is the initial connect, N is the Nth background retry. */
445
491
  attempt: number;
446
492
  /** True when a background reconnect loop will keep retrying. */
@@ -468,6 +514,11 @@ export type TraceEvent =
468
514
  code: number | null;
469
515
  /** Signal that terminated the child, if any. */
470
516
  signal: string | null;
517
+ /** Why the transport closed, in its own words, such as `child process
518
+ * exited, and its pipe failed with write EPIPE` or `closed by host`;
519
+ * null when it gave none, or when its reason is a WebSocket server's
520
+ * own close text, which stays off the trace. */
521
+ reason: string | null;
471
522
  /** True when a background reconnect loop will revive the connection. */
472
523
  willReconnect: boolean;
473
524
  })