@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
package/dist/src/agent.js CHANGED
@@ -65,15 +65,38 @@ export class Agent {
65
65
  * closure) so the next activation can close whatever its predecessor left
66
66
  * open — see failOpenKvSubmissions. */
67
67
  kvOpenQueue = null;
68
+ /** The current stream's fresh input so far, as membrane's usage event
69
+ * reports it (cumulative across the tool loop): what the maxStreamTokens
70
+ * budget checks. */
68
71
  lastStreamInputTokens = 0;
69
- /** Real prefix size of the last usage event: fresh + cache creation +
70
- * cache read. THE window-shaped number — `lastStreamInputTokens` alone
71
- * omits cached tokens, which are most of a warm stream's window. */
72
+ /** Real prompt size of the stream's latest provider call that reported a
73
+ * prompt, which is what the provider billed it: fresh + cache creation +
74
+ * cache read. A call that reports none leaves it as it was (see
75
+ * lastStreamUnreportedTokens). Membrane's usage events are cumulative, so
76
+ * this is the call's share of its event (cumulativeDelta): the whole event
77
+ * for a stream's first call, and the event's own value where a counter
78
+ * went back. THE window-shaped number — `lastStreamInputTokens` alone
79
+ * omits cached tokens, which are most of a warm stream's window. 0 until a
80
+ * call on this stream reports a prompt, and the physical-window
81
+ * projection runs only once one has. */
72
82
  lastStreamRealInputTokens = 0;
73
- /** Output tokens of the last usage event — the prior round's generated
74
- * thinking/text/tool_use, which becomes part of the NEXT round's input
75
- * and so belongs in the physical-window projection. */
83
+ /** Output tokens of the same call as lastStreamRealInputTokens, or
84
+ * maxTokens, the bound on them, when it reported none (a call that ends at
85
+ * a tool boundary wrote a tool_use, so a 0 is a count left unreported) —
86
+ * the prior round's generated thinking/text/tool_use, which becomes part
87
+ * of the NEXT round's input and so belongs in the physical-window
88
+ * projection. */
76
89
  lastStreamOutputTokens = 0;
90
+ /** What the stream has grown by since the call lastStreamRealInputTokens
91
+ * describes, for the physical-window projection: the blocks appended at
92
+ * each boundary the stream continued past (counted only for an agent with
93
+ * physicalWindowTokens, whose projection is this field's one reader), and
94
+ * the output of each call that reported no prompt (its reported output,
95
+ * or maxTokens, the bound on it, when it reported none either). After
96
+ * calls that report no prompt, the next request holds the prompt and
97
+ * output of the call lastStreamRealInputTokens describes, plus this. 0 at
98
+ * stream start and whenever a call reports a prompt. */
99
+ lastStreamUnreportedTokens = 0;
77
100
  maxStreamTokens;
78
101
  /** Provider hard context cap (see AgentConfig.physicalWindowTokens). */
79
102
  physicalWindowTokens;
@@ -87,6 +110,51 @@ export class Agent {
87
110
  configuredSameRoundThinkTextPolicy;
88
111
  contextBudgetTargetTokens;
89
112
  runtimeSettingsOverrides = {};
113
+ /**
114
+ * Counts changes of the live budget (an update, a reset, a restore, a
115
+ * settle). A compile speaks for the budget it planned at only if the live
116
+ * budget didn't change while it ran, and what it showed stands only until
117
+ * the next change.
118
+ */
119
+ liveBudgetEpoch = 0;
120
+ /**
121
+ * Counts changes of the descent (each arm of one, the same target included,
122
+ * a reset, a cancel, a settle). A settle proves its target only to a
123
+ * compile that began under that same descent.
124
+ */
125
+ descentEpoch = 0;
126
+ /** The live budget the last compile planned at, whether it fit, and the live budget's epoch then. */
127
+ lastCompiledBudget;
128
+ /** Counts compiles at the live budget as they start. */
129
+ compilesStarted = 0;
130
+ /** The start numbers of the compiles at the live budget still running. */
131
+ compilesRunning = new Set();
132
+ /**
133
+ * For each budget, the start of the latest-started compile that has ended
134
+ * there with a fit or a refusal, whether or not what it showed was
135
+ * credited. Of two compiles at one budget, the later-started one planned
136
+ * at the newer store, so an earlier-started one that ends after it is
137
+ * ignored. That rests on the context manager selecting at the start of
138
+ * `compile`, before anything it awaits, so start order is selection order;
139
+ * a context manager that awaits before its select breaks it. (Injections
140
+ * are added after the select, so compiles with and without them plan
141
+ * alike.) An entry is
142
+ * kept only while a compile that started before it is still running, the
143
+ * only kind it can turn away, so the map is empty whenever no compile
144
+ * runs.
145
+ */
146
+ latestEndedStart = new Map();
147
+ /** The last outcome the listener recorded; a failed record is told again, and so is any after a change. */
148
+ recordedCompiledBudget;
149
+ /**
150
+ * A boot restore applied a proven budget below the live one at once. Until
151
+ * the budget is next written (an update that carries one, or a reset of
152
+ * it) or a compile at it fits, a refusal of a compile at it restores it
153
+ * paced instead.
154
+ */
155
+ restoredAtOnce = false;
156
+ /** Told when that changes (the framework records it beside the persisted budget). */
157
+ compiledBudgetListener;
90
158
  contextManager;
91
159
  membrane;
92
160
  constructor(config, contextManager, membrane) {
@@ -208,9 +276,15 @@ export class Agent {
208
276
  getRuntimeSettings() {
209
277
  const hot = this.getHotContextSettings();
210
278
  return {
211
- contextBudgetTokens: this.contextBudgetTargetTokens ??
279
+ // The budget it's set to: the override, where there is one. That is
280
+ // the target of a descent toward it, or else the live budget, except
281
+ // while it's kept unapplied: at boot (keepRuntimeSettings), or after a
282
+ // refused at-once budget couldn't be restored paced (compileAtLiveBudget).
283
+ contextBudgetTokens: this.runtimeSettingsOverrides.contextBudgetTokens ??
284
+ this.contextBudgetTargetTokens ??
212
285
  this.contextBudgetTokens ??
213
286
  DEFAULT_CONTEXT_BUDGET_TOKENS,
287
+ effectiveContextBudgetTokens: this.contextBudgetTokens ?? DEFAULT_CONTEXT_BUDGET_TOKENS,
214
288
  ...(hot ? { tailTokens: hot.tailTokens } : {}),
215
289
  ...(hot?.transitionPaceTokens !== undefined
216
290
  ? { transitionPaceTokens: hot.transitionPaceTokens }
@@ -232,6 +306,179 @@ export class Agent {
232
306
  getRuntimeSettingsOverrides() {
233
307
  return { ...this.runtimeSettingsOverrides };
234
308
  }
309
+ /**
310
+ * Set (or clear) the one listener told when a compile at the live budget
311
+ * comes out differently from the last: it fit at a budget the last one
312
+ * didn't plan at (after a settle, the target), or the context manager
313
+ * refused a budget the last one fit at.
314
+ */
315
+ onContextBudgetCompiled(listener) {
316
+ this.compiledBudgetListener = listener;
317
+ }
318
+ /**
319
+ * The live budget the last compile planned at and fit, if it fit and the
320
+ * live budget hasn't changed since.
321
+ */
322
+ getProvenContextBudgetTokens() {
323
+ const last = this.lastCompiledBudget;
324
+ return last?.fit && last.epoch === this.liveBudgetEpoch ? last.budget : undefined;
325
+ }
326
+ /**
327
+ * Set the live budget and the descent target, counting a change of each.
328
+ * `armed` counts a descent armed here as a change even when its target is
329
+ * the one already set: a compile that began under the earlier arm isn't
330
+ * credited with this one's settle.
331
+ */
332
+ setContextBudget(budget, target, armed = false) {
333
+ if (budget !== this.contextBudgetTokens)
334
+ this.liveBudgetEpoch++;
335
+ if (armed || target !== this.contextBudgetTargetTokens)
336
+ this.descentEpoch++;
337
+ this.contextBudgetTokens = budget;
338
+ this.contextBudgetTargetTokens = target;
339
+ }
340
+ /**
341
+ * Claim the place of the latest compile to end at `budget` for the one that
342
+ * started as the `started`th, and say whether it got it: a compile that
343
+ * started before one that has already ended there is ignored, whichever
344
+ * finishes last.
345
+ */
346
+ claimLatestAt(budget, started) {
347
+ if (started < (this.latestEndedStart.get(budget) ?? 0))
348
+ return false;
349
+ this.latestEndedStart.set(budget, started);
350
+ return true;
351
+ }
352
+ /**
353
+ * Note what a compile at `budget`, planned at the live budget's `epoch`,
354
+ * showed. It speaks for the live budget only if that hasn't changed since;
355
+ * the listener is told either way, so a refusal withdraws the record's
356
+ * marker for the budget it planned at whatever changed meanwhile.
357
+ */
358
+ noteCompiledBudget(budget, fit, epoch) {
359
+ if (epoch === this.liveBudgetEpoch)
360
+ this.lastCompiledBudget = { budget, fit, epoch };
361
+ const recorded = this.recordedCompiledBudget;
362
+ if (recorded?.budget === budget && recorded.fit === fit && recorded.epoch === epoch)
363
+ return;
364
+ // A listener that couldn't record it returns false: nothing is assumed
365
+ // recorded then, so the next compile tells it again whatever it shows.
366
+ this.recordedCompiledBudget = this.compiledBudgetListener?.(budget, fit) !== false ? { budget, fit, epoch } : undefined;
367
+ }
368
+ /** Compile at the live budget (no explicit one), noting whether it fit. */
369
+ async compileAtLiveBudget(compile) {
370
+ const started = ++this.compilesStarted;
371
+ this.compilesRunning.add(started);
372
+ try {
373
+ return await this.compileAtLiveBudgetNoting(compile, started);
374
+ }
375
+ finally {
376
+ this.compilesRunning.delete(started);
377
+ const earliest = Math.min(...this.compilesRunning);
378
+ for (const [budget, start] of this.latestEndedStart) {
379
+ if (start < earliest)
380
+ this.latestEndedStart.delete(budget);
381
+ }
382
+ }
383
+ }
384
+ /** The compile at the live budget that started as the `started`th, and what it showed. */
385
+ async compileAtLiveBudgetNoting(compile, started) {
386
+ // The budget this compile plans at, and the descent under way when it
387
+ // started: a change of either while it runs is no part of what it shows.
388
+ const live = this.contextBudgetTokens ?? DEFAULT_CONTEXT_BUDGET_TOKENS;
389
+ const epoch = this.liveBudgetEpoch;
390
+ const descent = this.descentEpoch;
391
+ let result;
392
+ try {
393
+ result = await compile();
394
+ }
395
+ catch (error) {
396
+ if (error?.name !== 'OverBudgetError')
397
+ throw error;
398
+ // A refusal shows the budget it planned at doesn't fit, whatever
399
+ // changed while it ran: the record's marker for it is withdrawn, and
400
+ // nothing is credited, unless a compile that started later has already
401
+ // ended at that budget.
402
+ if (this.claimLatestAt(live, started))
403
+ this.noteCompiledBudget(live, false, epoch);
404
+ // A budget a boot restore applied at once, refused at its first test,
405
+ // before anything wrote the budget: restore it paced instead, as the
406
+ // boot would have without its proof, and compile again from the
407
+ // recipe's budget. (While restoredAtOnce holds, the live budget is the
408
+ // one the boot applied: every write of the budget clears it, and no
409
+ // compile runs before the boot's restore.)
410
+ if (!this.restoredAtOnce)
411
+ throw error;
412
+ this.restoredAtOnce = false;
413
+ // The persisted budget stays in the overrides, so if it can't be
414
+ // restored paced, no later write erases it.
415
+ this.setContextBudget(this.configuredContextBudgetTokens, undefined);
416
+ let outcome;
417
+ try {
418
+ this.updateRuntimeSettings({ contextBudgetTokens: live });
419
+ outcome = 'restored it as a paced descent from the recipe\'s budget';
420
+ }
421
+ catch (fallback) {
422
+ outcome = `couldn't restore it paced (${fallback instanceof Error ? fallback.message : String(fallback)}), ` +
423
+ `so it compiles at the recipe's budget, with contextBudgetTokens=${live} kept set`;
424
+ }
425
+ console.error(`[agent-settings] ${this.name}: the context manager refused contextBudgetTokens=${live}, which the boot ` +
426
+ `applied at once; ${outcome}`);
427
+ return this.compileAtLiveBudget(compile);
428
+ }
429
+ this.restoredAtOnce = false;
430
+ // A settle, here, of the descent this compile began under proves its
431
+ // target: the frontier fits the window prepared for it. Without a settle,
432
+ // the compile proves the budget it planned at. Either only if the live
433
+ // budget didn't change while it ran; a settle of a descent armed while it
434
+ // ran proves nothing (the next compile at the target does).
435
+ // A compile that ends here with a fit takes its place in the start order
436
+ // at the budget it planned at, credited or not; a settle credits its
437
+ // target only from the latest compile to end there.
438
+ const latest = this.claimLatestAt(live, started);
439
+ const sameLive = this.liveBudgetEpoch === epoch;
440
+ const sameDescent = this.descentEpoch === descent;
441
+ const settled = this.settleRuntimeSettingsTransition();
442
+ if (latest && sameLive && settled && sameDescent && this.claimLatestAt(this.contextBudgetTokens, started)) {
443
+ this.noteCompiledBudget(this.contextBudgetTokens, true, this.liveBudgetEpoch);
444
+ }
445
+ else if (latest && sameLive && !settled)
446
+ this.noteCompiledBudget(live, true, epoch);
447
+ return result;
448
+ }
449
+ /**
450
+ * The budget compiles plan at, the path a patch's budget takes from it, and
451
+ * whether a descent is in flight, from the agent's own state alone: what a
452
+ * runtime-settings preview can still say while the context manager refuses
453
+ * its queries.
454
+ * @internal For the framework's runtime-settings preview; not a host API.
455
+ */
456
+ runtimeBudgetPath(patch) {
457
+ const live = this.contextBudgetTokens ?? DEFAULT_CONTEXT_BUDGET_TOKENS;
458
+ const budget = patch?.contextBudgetTokens;
459
+ return {
460
+ liveBudgetTokens: live,
461
+ path: budget === undefined ? 'none' : budget >= live || patch?.immediate ? 'immediate' : 'paced',
462
+ transition: this.contextBudgetTargetTokens === undefined ? 'stable' : 'converging',
463
+ };
464
+ }
465
+ /**
466
+ * Whether the context manager answers its queries, as its readiness says.
467
+ * It refuses them while its strategy's initialization is pending or has
468
+ * failed, until a switchBranch or setStrategy succeeds, and a strategy can
469
+ * refuse its readiness on its own, as the autobiographical one does for a
470
+ * branch it hasn't reloaded.
471
+ * @internal For the framework's runtime-settings preview; not a host API.
472
+ */
473
+ contextManagerAnswers() {
474
+ try {
475
+ this.contextManager.isReady();
476
+ return true;
477
+ }
478
+ catch {
479
+ return false;
480
+ }
481
+ }
235
482
  /**
236
483
  * Read-only mirror of `updateRuntimeSettings`' budget semantics: derive what
237
484
  * the next compile would actually plan at if `patch` were applied, WITHOUT
@@ -248,41 +495,48 @@ export class Agent {
248
495
  * - `path: 'none'` — patch absent or budget untouched.
249
496
  *
250
497
  * `overrides` uses strategy-config names (`recentWindowTokens`,
251
- * `kvStableReachTokens`) suitable for `previewContext`. `preparedWindowTokens`
252
- * is deliberately never mapped: it is strategy instance state, not config,
253
- * and cannot be simulated by a preview. A `kvStableReachTokens` override is
254
- * omitted while a prepared window is in flight — the live runtime pace
255
- * shadows the config value there, so the override would not be observed.
498
+ * `kvStableReachTokens`) suitable for `previewContext`. The reach is the one
499
+ * the next compile plans at (context-manager's kv-stable picker): a
500
+ * descent's pace while a window is prepared for it, one this patch arms (a
501
+ * paced decrease) or one in flight that it leaves alone (a patch without a
502
+ * budget), and the configured reach otherwise. Config is a preview's only
503
+ * lever: `preparedWindowTokens` and the live runtime pace are strategy
504
+ * instance state, which a preview can't set. So a preview taken during a
505
+ * descent plans under that descent's prepared window, and at its live pace
506
+ * whenever the strategy carries one, which the picker reads before the
507
+ * configured reach. A boot restore's preflight never meets this: a new
508
+ * strategy has no window.
256
509
  */
257
510
  planRuntimeSettings(patch) {
258
511
  if (patch && Object.keys(patch).length > 0) {
259
512
  this.validateRuntimeSettingsPatch(patch);
260
513
  }
261
514
  const hot = this.getHotContextSettings();
262
- const live = this.contextBudgetTokens ?? DEFAULT_CONTEXT_BUDGET_TOKENS;
515
+ const { liveBudgetTokens: live, path } = this.runtimeBudgetPath(patch);
516
+ const budget = patch?.contextBudgetTokens;
263
517
  const overrides = {};
264
518
  if (patch?.tailTokens !== undefined) {
265
519
  overrides.recentWindowTokens = patch.tailTokens;
266
520
  }
267
- if (patch?.transitionPaceTokens !== undefined &&
268
- hot?.preparedWindowTokens === undefined) {
269
- overrides.kvStableReachTokens = patch.transitionPaceTokens;
521
+ // A window is prepared at the next compile when this patch arms a
522
+ // descent, or leaves one in flight alone; an immediate change clears it.
523
+ const prepared = hot !== null && (path === 'paced' || (path === 'none' && hot.preparedWindowTokens !== undefined));
524
+ const reach = prepared
525
+ ? patch?.transitionPaceTokens ?? hot.transitionPaceTokens ?? DEFAULT_TRANSITION_PACE_TOKENS
526
+ : this.configuredTransitionPaceTokens;
527
+ if (reach !== undefined && reach !== this.configuredTransitionPaceTokens) {
528
+ overrides.kvStableReachTokens = reach;
270
529
  }
271
- if (patch?.contextBudgetTokens === undefined) {
530
+ if (budget === undefined) {
272
531
  return { effectiveBudgetTokens: live, liveBudgetTokens: live, path: 'none', overrides };
273
532
  }
274
- if (patch.contextBudgetTokens >= live || patch.immediate) {
275
- return {
276
- effectiveBudgetTokens: patch.contextBudgetTokens,
277
- liveBudgetTokens: live,
278
- path: 'immediate',
279
- overrides,
280
- };
533
+ if (path === 'immediate') {
534
+ return { effectiveBudgetTokens: budget, liveBudgetTokens: live, path, overrides };
281
535
  }
282
536
  return {
283
537
  effectiveBudgetTokens: live,
284
538
  liveBudgetTokens: live,
285
- advisoryTargetTokens: patch.contextBudgetTokens,
539
+ advisoryTargetTokens: budget,
286
540
  path: 'paced',
287
541
  overrides,
288
542
  };
@@ -306,6 +560,8 @@ export class Agent {
306
560
  let nextContextBudget = this.contextBudgetTokens;
307
561
  let nextContextTarget = this.contextBudgetTargetTokens;
308
562
  let appliedDefaultPace = false;
563
+ // A paced decrease arms a descent, even to the target already set.
564
+ let armed = false;
309
565
  const hotPatch = {};
310
566
  if (patch.tailTokens !== undefined)
311
567
  hotPatch.tailTokens = patch.tailTokens;
@@ -335,23 +591,25 @@ export class Agent {
335
591
  }
336
592
  hotPatch.preparedWindowTokens = patch.contextBudgetTokens - this.maxTokens;
337
593
  nextContextTarget = patch.contextBudgetTokens;
594
+ armed = true;
338
595
  }
339
596
  }
340
597
  if (Object.keys(hotPatch).length > 0) {
341
598
  this.updateHotContextSettings(hotPatch);
342
599
  }
343
- this.contextBudgetTokens = nextContextBudget;
344
- this.contextBudgetTargetTokens = nextContextTarget;
600
+ this.setContextBudget(nextContextBudget, nextContextTarget, armed);
601
+ // A budget written here is a decision about it, whatever its value: the
602
+ // boot's at-once fallback (restoredAtOnce) no longer speaks for it.
603
+ if (patch.contextBudgetTokens !== undefined)
604
+ this.restoredAtOnce = false;
345
605
  if (appliedDefaultPace) {
346
606
  this.runtimeSettingsOverrides.transitionPaceTokens = DEFAULT_TRANSITION_PACE_TOKENS;
347
607
  }
348
- for (const [key, value] of Object.entries(patch)) {
349
- if (key === 'sameRoundThinkTextPolicy')
350
- continue;
351
- if (key === 'immediate')
352
- continue; // a mode for this apply, never a persisted setting
353
- if (value !== undefined)
354
- this.runtimeSettingsOverrides[key] = value;
608
+ // Only settings reach the override map: `immediate` is a mode for this
609
+ // apply, and nothing else a caller passes is a setting.
610
+ for (const key of ['contextBudgetTokens', 'tailTokens', 'transitionPaceTokens']) {
611
+ if (patch[key] !== undefined)
612
+ this.runtimeSettingsOverrides[key] = patch[key];
355
613
  }
356
614
  if (patch.sameRoundThinkTextPolicy !== undefined) {
357
615
  this.runtimeSettingsOverrides.sameRoundThinkTextPolicy = patch.sameRoundThinkTextPolicy;
@@ -362,6 +620,8 @@ export class Agent {
362
620
  const reset = new Set(keys ?? ['contextBudgetTokens', 'tailTokens', 'transitionPaceTokens', 'sameRoundThinkTextPolicy']);
363
621
  let nextContextBudget = this.contextBudgetTokens;
364
622
  let nextContextTarget = this.contextBudgetTargetTokens;
623
+ // A reset to a recipe budget below the live one arms a descent to it.
624
+ let armed = false;
365
625
  const hotPatch = {};
366
626
  if (reset.has('contextBudgetTokens')) {
367
627
  const configured = this.configuredContextBudgetTokens ?? DEFAULT_CONTEXT_BUDGET_TOKENS;
@@ -375,6 +635,7 @@ export class Agent {
375
635
  hotPatch.transitionPaceTokens = DEFAULT_TRANSITION_PACE_TOKENS;
376
636
  }
377
637
  nextContextTarget = configured;
638
+ armed = true;
378
639
  }
379
640
  else {
380
641
  nextContextBudget = this.configuredContextBudgetTokens;
@@ -395,10 +656,11 @@ export class Agent {
395
656
  }
396
657
  if (Object.keys(hotPatch).length > 0)
397
658
  this.updateHotContextSettings(hotPatch);
398
- this.contextBudgetTokens = nextContextBudget;
399
- this.contextBudgetTargetTokens = nextContextTarget;
400
- if (reset.has('contextBudgetTokens'))
659
+ this.setContextBudget(nextContextBudget, nextContextTarget, armed);
660
+ if (reset.has('contextBudgetTokens')) {
401
661
  delete this.runtimeSettingsOverrides.contextBudgetTokens;
662
+ this.restoredAtOnce = false;
663
+ }
402
664
  if (reset.has('tailTokens'))
403
665
  delete this.runtimeSettingsOverrides.tailTokens;
404
666
  if (reset.has('transitionPaceTokens')) {
@@ -412,7 +674,7 @@ export class Agent {
412
674
  cancelRuntimeSettingsTransition() {
413
675
  if (this.contextBudgetTargetTokens !== undefined) {
414
676
  this.updateHotContextSettings({ preparedWindowTokens: null });
415
- this.contextBudgetTargetTokens = undefined;
677
+ this.setContextBudget(this.contextBudgetTokens, undefined);
416
678
  const live = this.contextBudgetTokens ?? DEFAULT_CONTEXT_BUDGET_TOKENS;
417
679
  if (live === (this.configuredContextBudgetTokens ?? DEFAULT_CONTEXT_BUDGET_TOKENS)) {
418
680
  delete this.runtimeSettingsOverrides.contextBudgetTokens;
@@ -423,10 +685,52 @@ export class Agent {
423
685
  }
424
686
  return this.getRuntimeSettings();
425
687
  }
426
- restoreRuntimeSettings(overrides) {
427
- return this.updateRuntimeSettings(overrides);
688
+ /**
689
+ * Apply persisted settings at construction. `provenBudget` is a budget the
690
+ * caller has shown fits (the framework, from the record and a preflight),
691
+ * applied at once: it stands as the last compile's until a compile here
692
+ * says otherwise. When it's below the live budget, where the proof is what
693
+ * took the restore off the paced path, a refusal of a compile at it
694
+ * restores it paced instead, until the budget is next written or a compile
695
+ * at it fits. At or above the live budget, the restore is at once either
696
+ * way, and a refusal has nothing to fall back to.
697
+ */
698
+ restoreRuntimeSettings(settings, opts) {
699
+ if (Object.keys(settings).length === 0)
700
+ return this.getRuntimeSettings();
701
+ const live = this.contextBudgetTokens ?? DEFAULT_CONTEXT_BUDGET_TOKENS;
702
+ const snapshot = this.updateRuntimeSettings(settings);
703
+ if (opts?.provenBudget !== undefined) {
704
+ this.lastCompiledBudget = { budget: opts.provenBudget, fit: true, epoch: this.liveBudgetEpoch };
705
+ this.restoredAtOnce = opts.provenBudget < live;
706
+ }
707
+ return snapshot;
708
+ }
709
+ /**
710
+ * Keep a persisted budget, tail or pace that couldn't be applied at
711
+ * construction: it stays in the override map, unapplied, so a later
712
+ * persisting write keeps it in the record. A budget is reported as the one
713
+ * set (`contextBudgetTokens`) beside the one compiles plan at
714
+ * (`effectiveContextBudgetTokens`); a tail or pace stays the strategy's.
715
+ */
716
+ keepRuntimeSettings(settings) {
717
+ for (const key of ['contextBudgetTokens', 'tailTokens', 'transitionPaceTokens']) {
718
+ if (settings[key] !== undefined)
719
+ this.runtimeSettingsOverrides[key] = settings[key];
720
+ }
428
721
  }
429
722
  validateRuntimeSettingsPatch(patch) {
723
+ this.checkRuntimeSettingsPatch(patch);
724
+ if ((patch.tailTokens !== undefined || patch.transitionPaceTokens !== undefined) &&
725
+ !this.getHotContextSettings()) {
726
+ throw new Error('The active context strategy does not support live tail/transition settings');
727
+ }
728
+ }
729
+ /**
730
+ * The checks of a runtime-settings patch that need nothing from the context manager.
731
+ * @internal For the framework's runtime-settings preview; not a host API.
732
+ */
733
+ checkRuntimeSettingsPatch(patch) {
430
734
  if (Object.keys(patch).length === 0)
431
735
  throw new Error('At least one setting is required');
432
736
  if (patch.contextBudgetTokens !== undefined) {
@@ -447,20 +751,17 @@ export class Agent {
447
751
  patch.sameRoundThinkTextPolicy !== 'private') {
448
752
  throw new Error("sameRoundThinkTextPolicy must be 'public' or 'private'");
449
753
  }
450
- if ((patch.tailTokens !== undefined || patch.transitionPaceTokens !== undefined) &&
451
- !this.getHotContextSettings()) {
452
- throw new Error('The active context strategy does not support live tail/transition settings');
453
- }
454
754
  }
755
+ /** Settle a converging budget once the window is prepared; true if it settled. */
455
756
  settleRuntimeSettingsTransition() {
456
757
  if (this.contextBudgetTargetTokens === undefined)
457
- return;
758
+ return false;
458
759
  const hot = this.getHotContextSettings();
459
760
  if (!hot?.prepared)
460
- return;
461
- this.contextBudgetTokens = this.contextBudgetTargetTokens;
462
- this.contextBudgetTargetTokens = undefined;
761
+ return false;
762
+ this.setContextBudget(this.contextBudgetTargetTokens, undefined);
463
763
  this.updateHotContextSettings({ preparedWindowTokens: null });
764
+ return true;
464
765
  }
465
766
  /**
466
767
  * RFC-006 consumed watermark: the newest stored message at the last compile
@@ -498,29 +799,56 @@ export class Agent {
498
799
  return null;
499
800
  }
500
801
  }
802
+ /** A committing compile, as for a request that is sent: the context
803
+ * manager commits what it commits for one, the consumed watermark moves,
804
+ * and a prepared budget change settles. To inspect what the agent would
805
+ * see, use the framework's `previewActivation`, which compiles as a dry
806
+ * run. */
501
807
  async compileContext(budget) {
502
808
  // RFC-006 §3.3: consumption is determined at assembly — a compile that
503
809
  // fails assembled nothing, so the watermark moves only on success.
504
810
  const watermark = this.pendingWatermark();
505
- const result = await this.contextManager.compile(this.compileBudget(budget));
811
+ const compile = () => this.contextManager.compile(this.compileBudget(budget));
812
+ const result = budget ? await compile() : await this.compileAtLiveBudget(compile);
506
813
  if (watermark)
507
814
  this.consumedWatermark = watermark;
508
- if (!budget)
509
- this.settleRuntimeSettingsTransition();
510
815
  return result;
511
816
  }
512
817
  /**
513
- * Compile the context with injections.
514
- * Same as compileContext but forwards injections (e.g. from MCPL servers)
515
- * to the context manager so they are merged into the compiled messages.
818
+ * Compile the context with injections (e.g. from MCPL servers), which the
819
+ * context manager merges into the compiled messages. Without
820
+ * `opts.dryRun` it commits as `compileContext` does: the consumed
821
+ * watermark moves, and a compile at the live window settles a prepared
822
+ * budget change. With `opts.dryRun`, for a compile that will never be
823
+ * sent, it does neither (see below).
516
824
  */
517
825
  async compileWithInjections(budget, injections, opts) {
518
- const watermark = this.pendingWatermark();
519
- const result = await this.contextManager.compile(this.compileBudget(budget), injections, opts);
826
+ // A dry run is a compile that will never be sent. The context manager
827
+ // persists no fold resolutions for it and queues none of the compression
828
+ // or merge work its fold plan calls for. Its thinking binding
829
+ // (context-manager#160, not yet released) will commit stamps and add a
830
+ // compile awaiting acceptance for a committing compile only. Here nothing
831
+ // moves either: the agent never read what a dry run assembled, so the
832
+ // consumed watermark stays. A dry run proves nothing about the budget
833
+ // either, so it goes straight to the compile, around
834
+ // compileAtLiveBudget: it settles no descent (a prepared budget change
835
+ // waits for the compile that is sent), notes no compiled budget, and
836
+ // restores nothing the boot applied at once, whether it fits or is
837
+ // refused.
838
+ //
839
+ // Context-manager 0.14 leaves what a select leaves behind as the last
840
+ // committing compile left it, on every path out of a dry run, throws
841
+ // included: no chunk record and no compression queued for a chunk the
842
+ // dry run closes, no calibration, and none of the bookkeeping the next
843
+ // compile or an observer reads (its `SelectOptions.dryRun` gives the
844
+ // contract). The one thing a dry run can still leave is the release of a
845
+ // compression hold whose deadline has passed, which `compile` does for
846
+ // any compile before it selects.
847
+ const watermark = opts?.dryRun ? null : this.pendingWatermark();
848
+ const compile = () => this.contextManager.compile(this.compileBudget(budget), injections, opts);
849
+ const result = budget || opts?.dryRun ? await compile() : await this.compileAtLiveBudget(compile);
520
850
  if (watermark)
521
851
  this.consumedWatermark = watermark;
522
- if (!budget)
523
- this.settleRuntimeSettingsTransition();
524
852
  return result;
525
853
  }
526
854
  // ==========================================================================
@@ -673,10 +1001,13 @@ export class Agent {
673
1001
  *
674
1002
  * This is the single source of truth for request assembly, shared by the
675
1003
  * real activation path (`startStreamWithInjections`) and debug/preview
676
- * tooling (`Framework.previewActivation`). It is pure and non-mutating:
677
- * it does not touch agent state and does not call the membrane, and
678
- * `ContextManager.compile` is itself side-effect-free (compression runs in
679
- * the background). Safe to call regardless of the agent's current status.
1004
+ * tooling (`Framework.previewActivation`). It does not call the membrane.
1005
+ * A compile that is sent commits what the context manager commits for it
1006
+ * (fold resolutions, the compression its fold plan calls for, the consumed
1007
+ * watermark); a preview passes `options.dryRun`, so its compile commits
1008
+ * none of that (see `compileWithInjections` for what a dry run still
1009
+ * leaves). Either way the context manager is handed the compression tool
1010
+ * definitions. Safe to call regardless of the agent's current status.
680
1011
  * It doesn't wait for the tool-result guard's annotations, so a preview
681
1012
  * never waits on a workspace write (rollback, undo and redo report one), and
682
1013
  * may show a withheld stub before its place is added; the activation waits
@@ -704,7 +1035,12 @@ export class Agent {
704
1035
  system: this.buildSystemPrompt(prospectiveSystemInjections),
705
1036
  })
706
1037
  : undefined;
707
- let { messages, systemInjections } = await this.compileWithInjections(budget, injections, immutablePrefixHash ? { kvUnifiedImmutablePrefixHash: immutablePrefixHash } : undefined);
1038
+ let { messages, systemInjections } = await this.compileWithInjections(budget, injections, immutablePrefixHash || options.dryRun
1039
+ ? {
1040
+ ...(immutablePrefixHash ? { kvUnifiedImmutablePrefixHash: immutablePrefixHash } : {}),
1041
+ ...(options.dryRun ? { dryRun: true } : {}),
1042
+ }
1043
+ : undefined);
708
1044
  // Sanitize: strip empty/whitespace text blocks and drop messages left with
709
1045
  // no content. The Anthropic API rejects empty text blocks with 400
710
1046
  // "messages: text content blocks must be non-empty"; when such a block
@@ -817,10 +1153,13 @@ export class Agent {
817
1153
  // the prior stream's window size — on the physical-window restart path
818
1154
  // that inherited value IS the oversized number that caused the restart,
819
1155
  // and projecting on it before this stream's first usage event would
820
- // restart again (loop). The `> 0` guard downstream means "no usage
821
- // observed on THIS stream yet" only because of this reset.
1156
+ // restart again (loop). The `> 0` guard downstream means "no call on
1157
+ // THIS stream has reported a prompt yet" only because of this reset (a
1158
+ // call that reports none leaves these as they were, and adds to
1159
+ // lastStreamUnreportedTokens instead).
822
1160
  this.lastStreamRealInputTokens = 0;
823
1161
  this.lastStreamOutputTokens = 0;
1162
+ this.lastStreamUnreportedTokens = 0;
824
1163
  // Compiled only once each withheld stub's annotation has finished,
825
1164
  // including one that began during the compile, and after a stub edit that
826
1165
  // failed has been retried (see afterAnnotations).