@animalabs/agent-framework 0.23.3 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. package/README.md +212 -42
  2. package/dist/src/agent.d.ts +65 -9
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +98 -12
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/server.d.ts.map +1 -1
  7. package/dist/src/api/server.js +14 -12
  8. package/dist/src/api/server.js.map +1 -1
  9. package/dist/src/clock-rows.d.ts +115 -0
  10. package/dist/src/clock-rows.d.ts.map +1 -0
  11. package/dist/src/clock-rows.js +222 -0
  12. package/dist/src/clock-rows.js.map +1 -0
  13. package/dist/src/code-execution/engine.d.ts +177 -0
  14. package/dist/src/code-execution/engine.d.ts.map +1 -0
  15. package/dist/src/code-execution/engine.js +494 -0
  16. package/dist/src/code-execution/engine.js.map +1 -0
  17. package/dist/src/code-execution/index.d.ts +5 -10
  18. package/dist/src/code-execution/index.d.ts.map +1 -1
  19. package/dist/src/code-execution/index.js +5 -9
  20. package/dist/src/code-execution/index.js.map +1 -1
  21. package/dist/src/code-execution/isolate/calls.d.ts +114 -0
  22. package/dist/src/code-execution/isolate/calls.d.ts.map +1 -0
  23. package/dist/src/code-execution/isolate/calls.js +145 -0
  24. package/dist/src/code-execution/isolate/calls.js.map +1 -0
  25. package/dist/src/code-execution/isolate/child-entry.d.ts +2 -0
  26. package/dist/src/code-execution/isolate/child-entry.d.ts.map +1 -0
  27. package/dist/src/code-execution/isolate/child-entry.js +5 -0
  28. package/dist/src/code-execution/isolate/child-entry.js.map +1 -0
  29. package/dist/src/code-execution/isolate/child-main.d.ts +26 -0
  30. package/dist/src/code-execution/isolate/child-main.d.ts.map +1 -0
  31. package/dist/src/code-execution/isolate/child-main.js +132 -0
  32. package/dist/src/code-execution/isolate/child-main.js.map +1 -0
  33. package/dist/src/code-execution/isolate/child.d.ts +44 -0
  34. package/dist/src/code-execution/isolate/child.d.ts.map +1 -0
  35. package/dist/src/code-execution/isolate/child.js +315 -0
  36. package/dist/src/code-execution/isolate/child.js.map +1 -0
  37. package/dist/src/code-execution/isolate/frames.d.ts +80 -0
  38. package/dist/src/code-execution/isolate/frames.d.ts.map +1 -0
  39. package/dist/src/code-execution/isolate/frames.js +210 -0
  40. package/dist/src/code-execution/isolate/frames.js.map +1 -0
  41. package/dist/src/code-execution/isolate/output.d.ts +68 -0
  42. package/dist/src/code-execution/isolate/output.d.ts.map +1 -0
  43. package/dist/src/code-execution/isolate/output.js +143 -0
  44. package/dist/src/code-execution/isolate/output.js.map +1 -0
  45. package/dist/src/code-execution/isolate/prelude.d.ts +109 -0
  46. package/dist/src/code-execution/isolate/prelude.d.ts.map +1 -0
  47. package/dist/src/code-execution/isolate/prelude.js +401 -0
  48. package/dist/src/code-execution/isolate/prelude.js.map +1 -0
  49. package/dist/src/code-execution/isolate/protocol.d.ts +151 -0
  50. package/dist/src/code-execution/isolate/protocol.d.ts.map +1 -0
  51. package/dist/src/code-execution/isolate/protocol.js +150 -0
  52. package/dist/src/code-execution/isolate/protocol.js.map +1 -0
  53. package/dist/src/code-execution/isolate/render.d.ts +57 -0
  54. package/dist/src/code-execution/isolate/render.d.ts.map +1 -0
  55. package/dist/src/code-execution/isolate/render.js +214 -0
  56. package/dist/src/code-execution/isolate/render.js.map +1 -0
  57. package/dist/src/code-execution/isolate/runner.d.ts +250 -0
  58. package/dist/src/code-execution/isolate/runner.d.ts.map +1 -0
  59. package/dist/src/code-execution/isolate/runner.js +854 -0
  60. package/dist/src/code-execution/isolate/runner.js.map +1 -0
  61. package/dist/src/code-execution/isolate/scripts.d.ts +175 -0
  62. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -0
  63. package/dist/src/code-execution/isolate/scripts.js +357 -0
  64. package/dist/src/code-execution/isolate/scripts.js.map +1 -0
  65. package/dist/src/code-execution/quickjs.wasm +0 -0
  66. package/dist/src/code-execution/tool-definition.d.ts +93 -43
  67. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  68. package/dist/src/code-execution/tool-definition.js +176 -142
  69. package/dist/src/code-execution/tool-definition.js.map +1 -1
  70. package/dist/src/deadline-timer.d.ts +3 -0
  71. package/dist/src/deadline-timer.d.ts.map +1 -1
  72. package/dist/src/deadline-timer.js +1 -1
  73. package/dist/src/deadline-timer.js.map +1 -1
  74. package/dist/src/framework.d.ts +1120 -457
  75. package/dist/src/framework.d.ts.map +1 -1
  76. package/dist/src/framework.js +4635 -2505
  77. package/dist/src/framework.js.map +1 -1
  78. package/dist/src/gate/event-gate.d.ts +28 -4
  79. package/dist/src/gate/event-gate.d.ts.map +1 -1
  80. package/dist/src/gate/event-gate.js +89 -13
  81. package/dist/src/gate/event-gate.js.map +1 -1
  82. package/dist/src/held-writes.d.ts +16 -3
  83. package/dist/src/held-writes.d.ts.map +1 -1
  84. package/dist/src/held-writes.js +16 -3
  85. package/dist/src/held-writes.js.map +1 -1
  86. package/dist/src/index.d.ts +8 -6
  87. package/dist/src/index.d.ts.map +1 -1
  88. package/dist/src/index.js +10 -8
  89. package/dist/src/index.js.map +1 -1
  90. package/dist/src/mcpl/channel-registry.d.ts +483 -115
  91. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  92. package/dist/src/mcpl/channel-registry.js +1735 -425
  93. package/dist/src/mcpl/channel-registry.js.map +1 -1
  94. package/dist/src/mcpl/conversation-router.d.ts +19 -39
  95. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  96. package/dist/src/mcpl/conversation-router.js +27 -59
  97. package/dist/src/mcpl/conversation-router.js.map +1 -1
  98. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  99. package/dist/src/mcpl/convert-block.js +9 -5
  100. package/dist/src/mcpl/convert-block.js.map +1 -1
  101. package/dist/src/mcpl/inbound-source.d.ts +50 -0
  102. package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
  103. package/dist/src/mcpl/inbound-source.js +130 -0
  104. package/dist/src/mcpl/inbound-source.js.map +1 -1
  105. package/dist/src/mcpl/index.d.ts +2 -1
  106. package/dist/src/mcpl/index.d.ts.map +1 -1
  107. package/dist/src/mcpl/index.js +3 -2
  108. package/dist/src/mcpl/index.js.map +1 -1
  109. package/dist/src/mcpl/log-value.d.ts +6 -0
  110. package/dist/src/mcpl/log-value.d.ts.map +1 -1
  111. package/dist/src/mcpl/log-value.js +8 -0
  112. package/dist/src/mcpl/log-value.js.map +1 -1
  113. package/dist/src/mcpl/modern-connection.d.ts +15 -6
  114. package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
  115. package/dist/src/mcpl/modern-connection.js +168 -53
  116. package/dist/src/mcpl/modern-connection.js.map +1 -1
  117. package/dist/src/mcpl/prose-grammar.d.ts +4 -5
  118. package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
  119. package/dist/src/mcpl/prose-grammar.js +4 -5
  120. package/dist/src/mcpl/prose-grammar.js.map +1 -1
  121. package/dist/src/mcpl/protocol-family.d.ts +97 -10
  122. package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
  123. package/dist/src/mcpl/protocol-family.js +174 -17
  124. package/dist/src/mcpl/protocol-family.js.map +1 -1
  125. package/dist/src/mcpl/push-coalescer.d.ts +132 -14
  126. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  127. package/dist/src/mcpl/push-coalescer.js +227 -50
  128. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  129. package/dist/src/mcpl/push-handler.d.ts +9 -20
  130. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  131. package/dist/src/mcpl/push-handler.js +7 -49
  132. package/dist/src/mcpl/push-handler.js.map +1 -1
  133. package/dist/src/mcpl/reference-fetcher.d.ts +2 -2
  134. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  135. package/dist/src/mcpl/reference-fetcher.js +4 -4
  136. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  137. package/dist/src/mcpl/server-connection.d.ts +50 -21
  138. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  139. package/dist/src/mcpl/server-connection.js +158 -74
  140. package/dist/src/mcpl/server-connection.js.map +1 -1
  141. package/dist/src/mcpl/tags.d.ts +24 -0
  142. package/dist/src/mcpl/tags.d.ts.map +1 -1
  143. package/dist/src/mcpl/tags.js +33 -0
  144. package/dist/src/mcpl/tags.js.map +1 -1
  145. package/dist/src/mcpl/tool-result-normalize.d.ts +2 -1
  146. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
  147. package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
  148. package/dist/src/mcpl/transport.d.ts +55 -7
  149. package/dist/src/mcpl/transport.d.ts.map +1 -1
  150. package/dist/src/mcpl/transport.js +163 -34
  151. package/dist/src/mcpl/transport.js.map +1 -1
  152. package/dist/src/mcpl/types.d.ts +22 -6
  153. package/dist/src/mcpl/types.d.ts.map +1 -1
  154. package/dist/src/mcpl/types.js.map +1 -1
  155. package/dist/src/module-registry.d.ts +4 -2
  156. package/dist/src/module-registry.d.ts.map +1 -1
  157. package/dist/src/module-registry.js +100 -13
  158. package/dist/src/module-registry.js.map +1 -1
  159. package/dist/src/modules/discord/index.d.ts.map +1 -1
  160. package/dist/src/modules/discord/index.js +8 -13
  161. package/dist/src/modules/discord/index.js.map +1 -1
  162. package/dist/src/modules/history/index.d.ts.map +1 -1
  163. package/dist/src/modules/history/index.js +27 -4
  164. package/dist/src/modules/history/index.js.map +1 -1
  165. package/dist/src/modules/history/semantic.d.ts +15 -3
  166. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  167. package/dist/src/modules/history/semantic.js +27 -5
  168. package/dist/src/modules/history/semantic.js.map +1 -1
  169. package/dist/src/modules/index.d.ts +1 -1
  170. package/dist/src/modules/index.d.ts.map +1 -1
  171. package/dist/src/modules/index.js.map +1 -1
  172. package/dist/src/modules/unknown-tool.d.ts +4 -1
  173. package/dist/src/modules/unknown-tool.d.ts.map +1 -1
  174. package/dist/src/modules/unknown-tool.js +4 -1
  175. package/dist/src/modules/unknown-tool.js.map +1 -1
  176. package/dist/src/modules/workspace/branch-intent.d.ts +3 -1
  177. package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
  178. package/dist/src/modules/workspace/branch-intent.js +3 -1
  179. package/dist/src/modules/workspace/branch-intent.js.map +1 -1
  180. package/dist/src/modules/workspace/edit-plan.d.ts +265 -0
  181. package/dist/src/modules/workspace/edit-plan.d.ts.map +1 -0
  182. package/dist/src/modules/workspace/edit-plan.js +794 -0
  183. package/dist/src/modules/workspace/edit-plan.js.map +1 -0
  184. package/dist/src/modules/workspace/edit-text.d.ts +117 -0
  185. package/dist/src/modules/workspace/edit-text.d.ts.map +1 -0
  186. package/dist/src/modules/workspace/edit-text.js +584 -0
  187. package/dist/src/modules/workspace/edit-text.js.map +1 -0
  188. package/dist/src/modules/workspace/effects.d.ts +5 -4
  189. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  190. package/dist/src/modules/workspace/effects.js +78 -25
  191. package/dist/src/modules/workspace/effects.js.map +1 -1
  192. package/dist/src/modules/workspace/index.d.ts +493 -62
  193. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  194. package/dist/src/modules/workspace/index.js +2216 -652
  195. package/dist/src/modules/workspace/index.js.map +1 -1
  196. package/dist/src/modules/workspace/observe.d.ts +213 -38
  197. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  198. package/dist/src/modules/workspace/observe.js +416 -36
  199. package/dist/src/modules/workspace/observe.js.map +1 -1
  200. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  201. package/dist/src/modules/workspace/reconcile.js +15 -8
  202. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  203. package/dist/src/modules/workspace/text.d.ts +32 -0
  204. package/dist/src/modules/workspace/text.d.ts.map +1 -0
  205. package/dist/src/modules/workspace/text.js +67 -0
  206. package/dist/src/modules/workspace/text.js.map +1 -0
  207. package/dist/src/modules/workspace/types.d.ts +210 -6
  208. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  209. package/dist/src/modules/workspace/types.js.map +1 -1
  210. package/dist/src/modules/workspace/watcher.d.ts +3 -2
  211. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  212. package/dist/src/modules/workspace/watcher.js.map +1 -1
  213. package/dist/src/operator-log.d.ts +52 -24
  214. package/dist/src/operator-log.d.ts.map +1 -1
  215. package/dist/src/operator-log.js.map +1 -1
  216. package/dist/src/recovery/discord-awareness-outbox.d.ts +496 -72
  217. package/dist/src/recovery/discord-awareness-outbox.d.ts.map +1 -1
  218. package/dist/src/recovery/discord-awareness-outbox.js +1196 -289
  219. package/dist/src/recovery/discord-awareness-outbox.js.map +1 -1
  220. package/dist/src/recovery/offline-branch.d.ts +26 -1
  221. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  222. package/dist/src/recovery/offline-branch.js +164 -39
  223. package/dist/src/recovery/offline-branch.js.map +1 -1
  224. package/dist/src/recovery/recover-cli.js +23 -4
  225. package/dist/src/recovery/recover-cli.js.map +1 -1
  226. package/dist/src/send-tools.d.ts +11 -0
  227. package/dist/src/send-tools.d.ts.map +1 -0
  228. package/dist/src/send-tools.js +11 -0
  229. package/dist/src/send-tools.js.map +1 -0
  230. package/dist/src/timezone.d.ts +4 -2
  231. package/dist/src/timezone.d.ts.map +1 -1
  232. package/dist/src/timezone.js +4 -2
  233. package/dist/src/timezone.js.map +1 -1
  234. package/dist/src/tool-image-ledger.d.ts +46 -7
  235. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  236. package/dist/src/tool-image-ledger.js +60 -7
  237. package/dist/src/tool-image-ledger.js.map +1 -1
  238. package/dist/src/tool-result-guard.d.ts +14 -11
  239. package/dist/src/tool-result-guard.d.ts.map +1 -1
  240. package/dist/src/tool-result-guard.js +17 -14
  241. package/dist/src/tool-result-guard.js.map +1 -1
  242. package/dist/src/tool-result-history.d.ts +3 -2
  243. package/dist/src/tool-result-history.d.ts.map +1 -1
  244. package/dist/src/tool-result-history.js +3 -2
  245. package/dist/src/tool-result-history.js.map +1 -1
  246. package/dist/src/tune-out/coordinator.d.ts +21 -0
  247. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  248. package/dist/src/tune-out/coordinator.js +142 -40
  249. package/dist/src/tune-out/coordinator.js.map +1 -1
  250. package/dist/src/types/agent.d.ts +31 -6
  251. package/dist/src/types/agent.d.ts.map +1 -1
  252. package/dist/src/types/events.d.ts +66 -9
  253. package/dist/src/types/events.d.ts.map +1 -1
  254. package/dist/src/types/framework.d.ts +69 -62
  255. package/dist/src/types/framework.d.ts.map +1 -1
  256. package/dist/src/types/module.d.ts +40 -8
  257. package/dist/src/types/module.d.ts.map +1 -1
  258. package/dist/src/types/trace.d.ts +111 -10
  259. package/dist/src/types/trace.d.ts.map +1 -1
  260. package/dist/src/usage/types.d.ts +16 -0
  261. package/dist/src/usage/types.d.ts.map +1 -1
  262. package/dist/src/usage/usage-tracker.d.ts +14 -1
  263. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  264. package/dist/src/usage/usage-tracker.js +81 -41
  265. package/dist/src/usage/usage-tracker.js.map +1 -1
  266. package/package.json +6 -8
  267. package/src/agent.ts +99 -12
  268. package/src/api/server.ts +14 -12
  269. package/src/clock-rows.ts +302 -0
  270. package/src/code-execution/engine.ts +595 -0
  271. package/src/code-execution/index.ts +5 -17
  272. package/src/code-execution/isolate/calls.ts +199 -0
  273. package/src/code-execution/isolate/child-entry.ts +5 -0
  274. package/src/code-execution/isolate/child-main.ts +132 -0
  275. package/src/code-execution/isolate/child.ts +327 -0
  276. package/src/code-execution/isolate/frames.ts +209 -0
  277. package/src/code-execution/isolate/output.ts +161 -0
  278. package/src/code-execution/isolate/prelude.ts +409 -0
  279. package/src/code-execution/isolate/protocol.ts +240 -0
  280. package/src/code-execution/isolate/render.ts +230 -0
  281. package/src/code-execution/isolate/runner.ts +911 -0
  282. package/src/code-execution/isolate/scripts.ts +479 -0
  283. package/src/code-execution/quickjs.wasm +0 -0
  284. package/src/code-execution/quickjs.wasm.LICENSES +149 -0
  285. package/src/code-execution/tool-definition.ts +236 -167
  286. package/src/deadline-timer.ts +1 -1
  287. package/src/framework.ts +4978 -2671
  288. package/src/gate/event-gate.ts +79 -17
  289. package/src/held-writes.ts +17 -3
  290. package/src/index.ts +32 -22
  291. package/src/mcpl/channel-registry.ts +1892 -471
  292. package/src/mcpl/conversation-router.ts +26 -72
  293. package/src/mcpl/convert-block.ts +9 -5
  294. package/src/mcpl/inbound-source.ts +131 -0
  295. package/src/mcpl/index.ts +4 -2
  296. package/src/mcpl/log-value.ts +9 -0
  297. package/src/mcpl/modern-connection.ts +162 -53
  298. package/src/mcpl/prose-grammar.ts +4 -5
  299. package/src/mcpl/protocol-family.ts +200 -22
  300. package/src/mcpl/push-coalescer.ts +306 -62
  301. package/src/mcpl/push-handler.ts +12 -54
  302. package/src/mcpl/reference-fetcher.ts +4 -4
  303. package/src/mcpl/server-connection.ts +180 -79
  304. package/src/mcpl/tags.ts +36 -0
  305. package/src/mcpl/tool-result-normalize.ts +2 -1
  306. package/src/mcpl/transport.ts +162 -33
  307. package/src/mcpl/types.ts +23 -6
  308. package/src/module-registry.ts +92 -13
  309. package/src/modules/discord/index.ts +8 -13
  310. package/src/modules/history/index.ts +25 -3
  311. package/src/modules/history/semantic.ts +29 -6
  312. package/src/modules/index.ts +1 -0
  313. package/src/modules/unknown-tool.ts +4 -1
  314. package/src/modules/workspace/branch-intent.ts +3 -1
  315. package/src/modules/workspace/edit-plan.ts +954 -0
  316. package/src/modules/workspace/edit-text.ts +618 -0
  317. package/src/modules/workspace/effects.ts +70 -19
  318. package/src/modules/workspace/index.ts +2293 -650
  319. package/src/modules/workspace/observe.ts +465 -56
  320. package/src/modules/workspace/reconcile.ts +14 -7
  321. package/src/modules/workspace/text.ts +68 -0
  322. package/src/modules/workspace/types.ts +211 -6
  323. package/src/modules/workspace/watcher.ts +3 -2
  324. package/src/operator-log.ts +47 -19
  325. package/src/recovery/discord-awareness-outbox.ts +1500 -356
  326. package/src/recovery/offline-branch.ts +189 -55
  327. package/src/recovery/recover-cli.ts +25 -4
  328. package/src/send-tools.ts +11 -0
  329. package/src/timezone.ts +4 -2
  330. package/src/tool-image-ledger.ts +62 -8
  331. package/src/tool-result-guard.ts +18 -15
  332. package/src/tool-result-history.ts +3 -2
  333. package/src/tune-out/coordinator.ts +166 -45
  334. package/src/types/agent.ts +32 -6
  335. package/src/types/events.ts +66 -9
  336. package/src/types/framework.ts +69 -62
  337. package/src/types/module.ts +40 -8
  338. package/src/types/trace.ts +121 -15
  339. package/src/usage/types.ts +16 -0
  340. package/src/usage/usage-tracker.ts +86 -43
  341. package/dist/src/code-execution/py-runner.d.ts +0 -191
  342. package/dist/src/code-execution/py-runner.d.ts.map +0 -1
  343. package/dist/src/code-execution/py-runner.js +0 -655
  344. package/dist/src/code-execution/py-runner.js.map +0 -1
  345. package/dist/src/code-execution/runtime-py.d.ts +0 -50
  346. package/dist/src/code-execution/runtime-py.d.ts.map +0 -1
  347. package/dist/src/code-execution/runtime-py.js +0 -677
  348. package/dist/src/code-execution/runtime-py.js.map +0 -1
  349. package/dist/src/mcpl/prose-stream-router.d.ts +0 -88
  350. package/dist/src/mcpl/prose-stream-router.d.ts.map +0 -1
  351. package/dist/src/mcpl/prose-stream-router.js +0 -291
  352. package/dist/src/mcpl/prose-stream-router.js.map +0 -1
  353. package/src/code-execution/py-runner.ts +0 -791
  354. package/src/code-execution/runtime-py.ts +0 -677
  355. package/src/mcpl/prose-stream-router.ts +0 -268
@@ -18,7 +18,7 @@ export const TOOL_RESULT_GUARD_AUDIT_STATE = 'framework/tool-result-guard';
18
18
  * left the stub's branch when the file would have been committed
19
19
  * (`branch_changed`), or because the batch used its deadline before this
20
20
  * write could start (`batch_deadline`); or null when no file is written at all (no writable
21
- * workspace, or the host is stopping). */
21
+ * mount without an include, or the host is stopping). */
22
22
  export type WithheldSpill =
23
23
  | { path: string; error?: string; unfinished?: true }
24
24
  | { skipped: 'workspace_unanswered' | 'branch_changed' | 'batch_deadline' }
@@ -86,10 +86,11 @@ export function withheldResultNotice(spill: WithheldSpill, auditDurable: boolean
86
86
  /**
87
87
  * What a withheld result's stub keeps of it: the fields of its data that its
88
88
  * tool marked as saying what it acted on (ToolResult.keepWhenWithheld), in
89
- * the order marked, serialized as a stored result is. Past the inline cap
90
- * they're cut, with a note that says so and where the whole result is, since
91
- * a later field can then be cut away entirely. Undefined when it keeps
92
- * nothing, as for a failed result (agent-framework #277). A write's file
89
+ * the order marked, serialized as a stored result is. Past `maxChars`, the
90
+ * inline cap or less (storeResults' keptCap), they're cut, with a note that
91
+ * says so and where the whole result is, since a later field can then be cut
92
+ * away entirely. Undefined when it keeps nothing, as for a failed result
93
+ * (agent-framework #277). A write's file
93
94
  * list is evidence of what it did, rarely what drew a refusal, and keeping it
94
95
  * is what shows a clobbered file when the receipt is withheld.
95
96
  */
@@ -109,7 +110,7 @@ export function keptWhenWithheld(result: CompletedToolCall['result'], maxChars:
109
110
  // Never half a surrogate pair: the stub goes to the provider as text, so
110
111
  // the cut can be one character short of the cap.
111
112
  const head = safeSlice(whole, 0, maxChars);
112
- return `${head} [cut at the inline cap: ${head.length} of these fields' ${whole.length} ` +
113
+ return `${head} [cut: ${head.length} of these fields' ${whole.length} ` +
113
114
  'characters; the whole result is where this note says]';
114
115
  }
115
116
 
@@ -313,15 +314,16 @@ export class ToolResultGuard {
313
314
  : JSON.parse(json);
314
315
  }
315
316
 
316
- /** `inlineCap` is the cap the caller stored `content` under; what a stub
317
- * keeps of a withheld result is cut at it too. */
317
+ /** `keptCap` is where what a stub keeps of a withheld result is cut: the
318
+ * cap the caller stored `content` under, less the stub's notice room where
319
+ * its message's bound sets it (toolResultShare, tool-image-ledger.ts). */
318
320
  storeResults(
319
321
  content: ContentBlock[],
320
322
  wireResults: ToolResult[],
321
323
  originals: CompletedToolCall[],
322
324
  metadata: MessageMetadata | undefined,
323
- /** The cap the results were stored under: what a stub keeps is cut there too. */
324
- inlineCap: number | undefined,
325
+ /** Where what a stub keeps is cut. */
326
+ keptCap: number | undefined,
325
327
  ): MessageId {
326
328
  if (!this.enabled) return this.cm.addMessage('user', content, metadata);
327
329
  if (this.pending) {
@@ -355,7 +357,7 @@ export class ToolResultGuard {
355
357
  const kept = new Map<number, string>();
356
358
  for (const [index, block] of content.entries()) {
357
359
  const result = block.type === 'tool_result' ? results.get(block.toolUseId) : undefined;
358
- const text = result ? keptWhenWithheld(result, inlineCap) : undefined;
360
+ const text = result ? keptWhenWithheld(result, keptCap) : undefined;
359
361
  if (text !== undefined) kept.set(index, text);
360
362
  }
361
363
  // Compression hold (CM >= 0.12): placed BEFORE onNewMessage fires, so no
@@ -497,8 +499,9 @@ export class ToolResultGuard {
497
499
 
498
500
  /**
499
501
  * Turn a withheld batch's notices into stubs that say where each original
500
- * is (agent-framework #277): written to the workspace when a writable mount
501
- * takes it, else kept in the audit record. A stub also keeps the file
502
+ * is (agent-framework #277): written to the workspace when it has a
503
+ * writable mount without an include to take it, else kept in the audit
504
+ * record. A stub also keeps the file
502
505
  * lists its tool marked. Its own unedited placeholder is
503
506
  * the only thing it edits: an operator's later edit, removal or rollback
504
507
  * stands.
@@ -596,8 +599,8 @@ export class ToolResultGuard {
596
599
  if (block.type !== 'tool_result') return [];
597
600
  if (thrown.has(index)) return [{ toolUseId: block.toolUseId, error: thrown.get(index)! }];
598
601
  const spill = spills.get(index) ?? null;
599
- // No file was written: no writable workspace, or the framework
600
- // was stopping.
602
+ // No file was written: no writable mount without an include,
603
+ // or the framework was stopping.
601
604
  if (spill === null) return [{ toolUseId: block.toolUseId, skipped: 'no_workspace_file' }];
602
605
  if ('skipped' in spill) return [{ toolUseId: block.toolUseId, skipped: spill.skipped }];
603
606
  return [{ toolUseId: block.toolUseId, path: spill.path,
@@ -49,7 +49,7 @@ export interface HistorySerializeOptions {
49
49
 
50
50
  /**
51
51
  * House-safe default inline cap (chars) for tool results, error results, and
52
- * background-script wake payloads (issue #89: a resident should never eat a
52
+ * script wake payloads (issue #89: a resident should never eat a
53
53
  * 42k blob they didn't ask for). Durable per-residence value comes from
54
54
  * `FrameworkConfig.toolResultInlineMaxChars`; a per-agent value from
55
55
  * agent_settings `tool_result_inline_max_chars`.
@@ -101,7 +101,8 @@ export function nonErrorResultText(
101
101
  }
102
102
 
103
103
  /** Bounded copy of an arbitrary string with the standard truncation notice,
104
- * cut at the cap as a tool result's stored copy is (toolResultCutAt), so it
104
+ * cut at the cap where a tool result's stored copy is cut at that cap
105
+ * (toolResultCutAt), so it
105
106
  * never ends in part of a slot. Each of its callers' readers sees this
106
107
  * very text, never the images behind a placeholder, so nothing past the cut
107
108
  * is kept for save_recent_image's index (#403). The framework stores a tool
@@ -37,6 +37,37 @@ const WAKE_COALESCE_MS = 2_000;
37
37
  /** How long after an end time whose cancel threw the cancel is tried again. */
38
38
  const EXPIRY_RETRY_MS = 60_000;
39
39
 
40
+ /** Ends a backlog line cut to fit in one of the residents' messages. */
41
+ const CUT_MARK = " [cut to fit; the whole message is in the channel's history]";
42
+
43
+ /**
44
+ * tune_out's refusal of a channel already tuned out: its settings, then what
45
+ * its transport is now where the repeat tried to open it again (`transport`,
46
+ * from the framework's dispatch), then how to change the settings.
47
+ */
48
+ export function alreadyTunedOutText(channelId: string, p: TuneOutParams, transport?: string): string {
49
+ // A duration so long its end is past what a Date can hold has no time to show.
50
+ const expires = p.expiresAtMs !== undefined ? new Date(p.expiresAtMs) : undefined;
51
+ const until = expires && Number.isFinite(expires.getTime())
52
+ ? `, until ${expires.toISOString().replace(/\.\d+Z$/, 'Z')}`
53
+ : '';
54
+ // A thrown error's words may end without a stop.
55
+ const now = transport === undefined ? '' : /[.!?]$/.test(transport) ? `${transport} ` : `${transport}. `;
56
+ return `Nothing changed${transport !== undefined ? ' in the tune-out' : ''}: ${JSON.stringify(channelId)} is already `
57
+ + `tuned out, with cadenceSeconds ${p.cadenceSeconds}, backlogCap ${p.backlogCap} and maxWakes ${p.maxWakes}${until}. `
58
+ + now
59
+ + 'To change those, cancel it first with tune_out mode "cancel", which delivers its backlog to you now, then call '
60
+ + 'tune_out again with the new settings.';
61
+ }
62
+
63
+ /** The first `length` code units of `text`, one fewer where the last would be
64
+ * the first half of a surrogate pair. */
65
+ function prefixOf(text: string, length: number): string {
66
+ const cut = text.slice(0, Math.max(0, length));
67
+ const last = cut.charCodeAt(cut.length - 1);
68
+ return last >= 0xd800 && last <= 0xdbff ? cut.slice(0, -1) : cut;
69
+ }
70
+
40
71
  /** What a thrown value says, without throwing: an Error's message, or the
41
72
  * value as text, which String() can't give for an object with no prototype. */
42
73
  function thrownReason(error: unknown): string {
@@ -63,6 +94,20 @@ export interface TuneOutFrameworkHooks {
63
94
  primaryName: () => string | null;
64
95
  /** Read the primary's stored messages (shared slot, unfiltered). */
65
96
  getStoredMessages: () => StoredMessage[];
97
+ /**
98
+ * Messages admitted for the primary but not yet in its store (deferred
99
+ * while a turn was alive), oldest first. A cancel reads them too, so a
100
+ * message held mid-turn is in the backlog it belongs to (#232 gap 7).
101
+ */
102
+ getDeferredMessages?: () => Array<Pick<StoredMessage, 'participant' | 'content' | 'metadata'>>;
103
+ /**
104
+ * The most characters of text every reader of the residents' shared slot
105
+ * is shown whole in one message: the smallest of their strategies'
106
+ * per-message caps (`maxMessageTokens`) at four characters a token, or
107
+ * undefined when none has such a cap. A cancel's dump is one message in
108
+ * that slot, so it holds as many backlog lines as fit.
109
+ */
110
+ residentMessageChars?: () => number | undefined;
66
111
  /** Current chronicle head sequence (window anchor for new epochs). */
67
112
  currentSequence: () => number;
68
113
  /** Move the subconscious window anchor (never forward past active epochs). */
@@ -178,7 +223,7 @@ export class TuneOutCoordinator {
178
223
  channelId: string,
179
224
  opts: { cadenceSeconds?: number; backlogCap?: number; maxWakes?: number; durationSeconds?: number },
180
225
  source: string,
181
- ): { ok: true; params: TuneOutParams } | { ok: false; error: string } {
226
+ ): { ok: true; params: TuneOutParams } | { ok: false; error: string; alreadyTunedOut?: true } {
182
227
  if (!this.hooks.subconsciousName()) {
183
228
  return {
184
229
  ok: false,
@@ -197,21 +242,7 @@ export class TuneOutCoordinator {
197
242
  };
198
243
  }
199
244
  const current = this.channelRegistry.getTuneOutState(serverId, channelId);
200
- if (current) {
201
- const p = current.params;
202
- // A duration so long its end is past what a Date can hold has no time to show.
203
- const expires = p.expiresAtMs !== undefined ? new Date(p.expiresAtMs) : undefined;
204
- const until = expires && Number.isFinite(expires.getTime())
205
- ? `, until ${expires.toISOString().replace(/\.\d+Z$/, 'Z')}`
206
- : '';
207
- return {
208
- ok: false,
209
- error: `Nothing changed: ${JSON.stringify(channelId)} is already tuned out, with cadenceSeconds `
210
- + `${p.cadenceSeconds}, backlogCap ${p.backlogCap} and maxWakes ${p.maxWakes}${until}. To change those, `
211
- + 'cancel it first with tune_out mode "cancel", which delivers its backlog to you now, then call tune_out '
212
- + 'again with the new settings.',
213
- };
214
- }
245
+ if (current) return { ok: false, error: alreadyTunedOutText(channelId, current.params), alreadyTunedOut: true };
215
246
  // A tool's input reaches here as the model wrote it, and the store can't
216
247
  // keep a NaN or an Infinity: its append would throw after the registry
217
248
  // had marked the channel tuned out. So each option is read as a number
@@ -259,9 +290,9 @@ export class TuneOutCoordinator {
259
290
  startedAtSequence: this.hooks.currentSequence(),
260
291
  ...(expiresAtMs !== undefined ? { expiresAtMs } : {}),
261
292
  };
262
- // A tune-out can end in the registry without cancel (a send into the
263
- // channel or a close sets its state there), leaving its timers armed:
264
- // clear them, so none of them acts on this one.
293
+ // A tune-out that ended in the registry without this coordinator's
294
+ // cancel would have left its timers armed: clear them, so none of them
295
+ // acts on this one.
265
296
  this.clearTimers(serverId, channelId);
266
297
  this.channelRegistry.enterTuneOut(serverId, channelId, params, source);
267
298
  this.armExpiry(serverId, channelId, params);
@@ -302,35 +333,106 @@ export class TuneOutCoordinator {
302
333
  this.clearTimers(serverId, channelId);
303
334
  this.divertedSinceInvocation.delete(this.key(serverId, channelId));
304
335
 
305
- // Backlog: every message stamped with this epoch, oldest first.
336
+ // Backlog: every message held under this epoch. The stored ones come in
337
+ // store order, then those still in the deferred queue (held mid-turn,
338
+ // not yet written), oldest first. That's arrival order, except when a
339
+ // held message stays queued while a later one is stored: a store write
340
+ // refused at a turn-start flush leaves it queued, and that turn's
341
+ // assembly can then store later coalesced messages directly.
306
342
  const epochId = ended.params.epochId;
307
- const backlog = this.hooks.getStoredMessages().filter(
308
- (m) => (m.metadata as { tuneOut?: { epochId?: string } } | undefined)?.tuneOut?.epochId === epochId,
309
- );
310
- const cap = ended.params.backlogCap;
311
- const shown = cap > 0 ? backlog.slice(-cap) : [];
312
- const truncated = backlog.length - shown.length;
313
-
314
- const lines = shown.map((m) => {
315
- const author = (m.metadata as { author?: { name?: string } } | undefined)?.author?.name ?? m.participant;
343
+ const ofEpoch = (m: Pick<StoredMessage, 'metadata'>): boolean =>
344
+ (m.metadata as { tuneOut?: { epochId?: string } } | undefined)?.tuneOut?.epochId === epochId;
345
+ const held = (this.hooks.getDeferredMessages?.() ?? []).filter(ofEpoch);
346
+ const backlog: Array<Pick<StoredMessage, 'participant' | 'content' | 'metadata'>> = [
347
+ ...this.hooks.getStoredMessages().filter(ofEpoch),
348
+ ...held,
349
+ ];
350
+ // Whole messages: a cap below one shows none, where slice(-0.5) would
351
+ // start at 0 and take them all.
352
+ const cap = Math.floor(ended.params.backlogCap);
353
+ const authorOf = (m: Pick<StoredMessage, 'participant' | 'metadata'>): string =>
354
+ (m.metadata as { author?: { name?: string } } | undefined)?.author?.name ?? m.participant;
355
+ const lineOf = (m: Pick<StoredMessage, 'participant' | 'content' | 'metadata'>): string => {
316
356
  const text = m.content
317
357
  .filter((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text')
318
358
  .map((b) => b.text)
319
359
  .join('\n');
320
- return `${author}: ${text}`;
321
- });
322
- const attrs = [
323
- `channel="${channelId}"`,
324
- `messages=${backlog.length}`,
325
- ...(truncated > 0 ? [`truncated=${truncated} (oldest; fetch_history covers the rest)`] : []),
360
+ return `${authorOf(m)}: ${text}`;
361
+ };
362
+ const lifecycle = `[Tune-out: ${channelId} cancelled — ${reason}, ${ended.wakeCount} wake${ended.wakeCount === 1 ? '' : 's'}]`;
363
+ const render = (lines: string[], truncated: number): string => {
364
+ const attrs = [
365
+ `channel="${channelId}"`,
366
+ `messages=${backlog.length}`,
367
+ ...(truncated > 0 ? [`truncated=${truncated} (oldest; fetch_history covers the rest)`] : []),
368
+ ];
369
+ return `${lifecycle}\n\n<tuned-out-backlog ${attrs.join(' ')}>\n${lines.join('\n')}\n</tuned-out-backlog>`;
370
+ };
371
+
372
+ // The dump is one message in the residents' shared slot, so each
373
+ // resident's strategy cuts it as it cuts any message past its per-message
374
+ // cap: context-manager's AutobiographicalStrategy (which connectome-host's
375
+ // FrontdeskStrategy extends) and WindowedPassthroughStrategy count a text
376
+ // block at a token per four characters, rounded up, and keep a message's
377
+ // first cap × 4 characters. Cut there, the dump would lose its newest
378
+ // lines, which would still count as shown. So it holds the newest lines
379
+ // that fit whole in the smallest such room, and those it leaves out, the
380
+ // oldest, count as truncated along with those above backlogCap, so the
381
+ // report is told of them. A newest message too long to fit alone is shown
382
+ // cut to the room left and marked, so the residents still get its head,
383
+ // as they would from a row of its own. When no line fits, whole or cut,
384
+ // the dump shows none; if the room is too small for even that, the
385
+ // residents' strategies cut its frame, the header and tags.
386
+ let shown = cap > 0 ? backlog.slice(-cap) : [];
387
+ let lines = shown.map(lineOf);
388
+ let newestCut = false;
389
+ const room = this.hooks.residentMessageChars?.();
390
+ if (room !== undefined && render(lines, backlog.length - shown.length).length > room) {
391
+ // The dump's length with no line, for the count that showing k lines
392
+ // leaves out: truncated= takes that count's digits, and goes at 0. With
393
+ // the newest k lines whole, the dump is that and the lines, with a
394
+ // newline between each two. While a message is left out, each line kept
395
+ // makes it longer, and they don't all fit, so the fit keeps lines from
396
+ // the newest while the next one fits.
397
+ const frameShowing = (k: number): number => render([], backlog.length - k).length;
398
+ let used = 0;
399
+ let kept = 0;
400
+ while (kept < lines.length) {
401
+ const next = used + lines[lines.length - 1 - kept]!.length + (kept > 0 ? 1 : 0);
402
+ if (frameShowing(kept + 1) + next > room) break;
403
+ used = next;
404
+ kept++;
405
+ }
406
+ // What of the newest line's text fits beside the frame and the mark. A
407
+ // cut shows some of it or none: a room under one character, or one
408
+ // that would split a surrogate pair, leaves the head empty.
409
+ const head = prefixOf(lines.at(-1) ?? '', room - frameShowing(1) - CUT_MARK.length);
410
+ if (kept === 0 && head !== '') {
411
+ lines = [head + CUT_MARK];
412
+ shown = shown.slice(-1);
413
+ newestCut = true;
414
+ } else {
415
+ lines = lines.slice(lines.length - kept);
416
+ shown = shown.slice(shown.length - kept);
417
+ }
418
+ }
419
+ const truncated = backlog.length - shown.length;
420
+ // The held messages the dump leaves out, the oldest of them. The
421
+ // subconscious's report covers what the dump leaves out, but its view
422
+ // holds stored rows only, so these go to it with its notice below. So does
423
+ // a held message the dump shows but can't give whole: one whose image or
424
+ // audio its text lines drop, or the newest, cut to fit.
425
+ const heldLeftOut = held.slice(0, Math.max(0, held.length - shown.length));
426
+ const heldForReport = [
427
+ ...heldLeftOut,
428
+ ...held.slice(heldLeftOut.length).filter((m) =>
429
+ m.content.some((b) => b.type !== 'text') || (newestCut && m === shown[0])),
326
430
  ];
327
- const dump =
328
- `<tuned-out-backlog ${attrs.join(' ')}>\n${lines.join('\n')}\n</tuned-out-backlog>`;
329
431
 
330
432
  // Host-framed system artifacts — never voiced as anyone.
331
433
  this.hooks.addMessage(
332
434
  'user',
333
- [{ type: 'text', text: `[Tune-out: ${channelId} cancelled — ${reason}, ${ended.wakeCount} wake${ended.wakeCount === 1 ? '' : 's'}]\n\n${dump}` }],
435
+ [{ type: 'text', text: render(lines, truncated) }],
334
436
  { system: true, kind: 'tune-out-cancel', channelId, epochId },
335
437
  );
336
438
  // The subconscious's own words, under its own name, verbatim.
@@ -346,20 +448,39 @@ export class TuneOutCoordinator {
346
448
  if (primary) {
347
449
  this.hooks.requestInference(primary, `tune-out cancelled (${reason})`, 'tune-out');
348
450
  }
349
- // The subconscious gets one cancel turn of its own (unless it initiated
350
- // the cancel — its note already carries its words): the issue's cancel
351
- // shape is dump + a report from the subconscious, and when the cap
352
- // truncated the raw dump, its summary is what covers the tail.
353
- if (source !== 'subconscious' && subName) {
451
+ // The subconscious gets one cancel turn of its own: the issue's cancel
452
+ // shape is dump + a report from the subconscious, and when the dump left
453
+ // messages out, its summary is what covers them. The held messages the
454
+ // dump can't give the resident whole follow the notice, each a row of its
455
+ // own with its own content, as its view can't have them until they're
456
+ // stored. A cancel the subconscious makes itself gets the notice and the
457
+ // turn when some do, since it wrote its note before it could see them,
458
+ // and when it wrote no note and the dump left any message out, since
459
+ // nothing else then covers it. Otherwise its note speaks for what it saw.
460
+ const selfCancel = source === 'subconscious';
461
+ if (subName && (!selfCancel || heldForReport.length > 0 || (!subconsciousNote && truncated > 0))) {
354
462
  const truncNote = truncated > 0
355
- ? ` ${truncated} of ${backlog.length} diverted messages were above the cap and not delivered raw.`
463
+ ? ` ${truncated} of ${backlog.length} diverted messages, the oldest, weren't delivered raw to the resident.`
464
+ : '';
465
+ const heldNote = heldForReport.length > 0
466
+ ? ` Still held at the cancel, so your view may not have them yet: the ${heldForReport.length} message${heldForReport.length === 1 ? '' : 's'} after this one.`
356
467
  : '';
357
468
  this.hooks.addMessage(
358
469
  'user',
359
- [{ type: 'text', text: `[Tune-out cancelled: ${channelId} — ${reason}.${truncNote}]` }],
470
+ [{ type: 'text', text: `[Tune-out cancelled: ${channelId} — ${reason}.${truncNote}${heldNote}]` }],
360
471
  { system: true, kind: 'tune-out-cancel-report', channelId },
361
472
  subName,
362
473
  );
474
+ // Each held message is a row of its own, so a per-message cap cuts each
475
+ // one on its own, its author's label included, not the batch's newest.
476
+ for (const m of heldForReport) {
477
+ this.hooks.addMessage(
478
+ 'user',
479
+ [{ type: 'text', text: `${authorOf(m)}:` }, ...m.content],
480
+ { system: true, kind: 'tune-out-cancel-held', channelId },
481
+ subName,
482
+ );
483
+ }
363
484
  this.hooks.requestInference(subName, `tune-out cancelled: ${channelId}`, 'tune-out');
364
485
  }
365
486
  this.hooks.emitTrace({ type: 'tune-out:cancelled', serverId, channelId, epochId, reason, wakeCount: ended.wakeCount });
@@ -218,6 +218,34 @@ export interface AgentConfig {
218
218
  * content-free system receipt is stored instead. Default false.
219
219
  */
220
220
  toolWrapperProseGuard?: boolean;
221
+
222
+ /**
223
+ * Clock rows (off when left out): when a boundary of this interval on the
224
+ * agent's local wall clock (counted from local midnight, in the
225
+ * framework's `timeZone`) has passed since the last clock row, the window
226
+ * takes one more — `Clock: 10:50 Europe/Berlin (UTC+02:00)`, with the date
227
+ * when the day has changed — before the next thing it takes: an incoming
228
+ * event, a turn's start, or, mid-turn, after a tool round's results, where
229
+ * it also joins the live stream. Only the latest boundary is written, and
230
+ * a window that holds no clock row yet takes one at the next of these. A
231
+ * clock reads the boundary's time where its tick would have stood, else
232
+ * the time it is written (after a tool's results, say), so it never
233
+ * reads earlier than a row before it. A clock wakes no one. See
234
+ * src/clock-rows.ts.
235
+ *
236
+ * - A stream that can't carry injected messages (membrane's XML prefill
237
+ * path) takes no mid-turn clock: the next event or turn start writes it.
238
+ * - On some models a clock after a tool's results drops the loop's
239
+ * earlier thinking from the request, as any message heard while acting
240
+ * does there: at most once an interval.
241
+ * - Residents share one window, so every resident reads the clock rows any
242
+ * of them writes, a resident with no `clock` of its own included. Each
243
+ * writes one when a boundary of its own interval has passed since the
244
+ * window's last clock, whichever resident wrote that. The subconscious
245
+ * takes none of its own: it reads the residents' through its merged
246
+ * view.
247
+ */
248
+ clock?: import('../clock-rows.js').ClockConfig;
221
249
  }
222
250
 
223
251
  /** The intentionally small set of agent settings that may change in-process. */
@@ -371,6 +399,10 @@ export interface InferenceRequest {
371
399
  * all of them without materializing content, the turn has no cause left
372
400
  * and does not run. */
373
401
  coalescingBatchSubjects?: string[];
402
+ /** Host-owned: this wake's sole cause is reading this deferred write, a
403
+ * coalesced render placed in its agent's queue (wakeToRead). Once the
404
+ * agent has read the write, or it was removed, the wake starts no turn. */
405
+ readsDeferredWrite?: string;
374
406
  agentName: string;
375
407
  reason: string;
376
408
  source: string;
@@ -386,12 +418,6 @@ export interface InferenceRequest {
386
418
  * ephemeral run's (a subagent's), which takes no global fallback.
387
419
  */
388
420
  channelId?: string;
389
- /**
390
- * Host-owned: the MCPL server whose message set `channelId`. A turn's
391
- * replies to that channel go out through this server only. Unset where the
392
- * wake's channel came from no one server's message.
393
- */
394
- serverId?: string;
395
421
  /**
396
422
  * True when the triggering message explicitly addressed the agent (mention,
397
423
  * reply-to-bot, DM — `chat:addressed` in MCPL RFC-001 terms). When a wake
@@ -193,11 +193,32 @@ export interface ToolResult {
193
193
  /** Whether this was an error (for LLM) */
194
194
  isError?: boolean;
195
195
  /**
196
- * An MCP tool's `structuredContent`: its machine-readable result, beside
197
- * `data`, which is the content view. Present exactly when the server sent
198
- * one, so `false`, `0` and `null` are values here, not absence. Programs
199
- * read it (scripts get it in their result object); the model sees content,
200
- * and sees this rendered as JSON only when the content carries no text.
196
+ * The dispatcher refused the call before it opened, RFC-007's refusal, so
197
+ * nothing ran and the call certainly had no effect. Only the dispatcher's
198
+ * path, the model's calls and a script's, sets it: the caller may not use
199
+ * the tool (allowedTools), a `skip_reply` wake was refused, the call's MCPL
200
+ * server is absent or still connecting, the host's policy for that server
201
+ * denies the tool, or a fork reached outside its channel. RFC-007's
202
+ * lifecycle refuses the call from the same place, so observers see no
203
+ * events for it. A check made after the call opened, such as a module's
204
+ * answer for a name it has no tool for or arguments that don't fit a
205
+ * schema, is a failure whose text says nothing was done, as is an MCPL
206
+ * request that couldn't be written. A programmatic call (`executeToolCall`,
207
+ * `ModuleContext.callTool`) opens no RFC-007 call, so it's never refused:
208
+ * the allowedTools check answers it with a failure saying the call didn't
209
+ * run, and the other checks it gets, such as the module registry's, answer
210
+ * as they do once a call has opened. A module's result never carries it. A script's call refused so reaches the script as an
211
+ * Error, where any other failure is a result with `isError`.
212
+ */
213
+ refused?: boolean;
214
+ /**
215
+ * The result's machine-readable form, beside `data`. Programs read it: a
216
+ * script gets it in its result object, and a host in the tool result
217
+ * event. The model never reads this field: it sees the value only where an
218
+ * MCP conversion put it in the content as JSON, which happens only when the
219
+ * server sent no text. An MCP tool's is its `structuredContent`, present
220
+ * exactly when the server sent one, so `false`, `0` and `null` are values
221
+ * here, not absence. Any other tool can set one too.
201
222
  */
202
223
  structured?: unknown;
203
224
  /**
@@ -237,15 +258,51 @@ export interface ToolCall {
237
258
  * The turn the call was made in, the same id as that turn's
238
259
  * `SpeechContext.turnId`. Set by the framework dispatch layer, beside
239
260
  * `callerAgentName`: a model's call carries the turn whose stream made it, a
240
- * foreground script's inner call the turn of the code_execution call whose
241
- * script is running when the call arrives, and a utility run its `utils`
242
- * call's. A background script runs apart from any turn, so its calls carry
243
- * none. Ids increase, so a later turn's is larger, and they're unique only
261
+ * script's call the turn of the code_execution call watching the script when
262
+ * the call reaches the host, and a utility run its `utils` call's. A script left
263
+ * running, which no call watches, runs apart from any turn, so its calls
264
+ * carry none. Ids increase, so a later turn's is larger, and they're unique only
244
265
  * within one run of the process. A call made with `executeToolCall` or a
245
266
  * module's `callTool` skips that layer and carries whatever turn its caller
246
267
  * put on the call, usually none.
247
268
  */
248
269
  callerTurnId?: number;
270
+ /**
271
+ * How long this call's result may be and still reach its caller whole,
272
+ * measured as the framework stores it: an error's text, or a success's data
273
+ * as JSON (a content array as its text). For a model's call it is the
274
+ * agent's inline cap, past which the framework spills the result to a
275
+ * workspace file and sends a preview. A result that long is stored whole
276
+ * unless its round's results together pass the readers' per-message bound,
277
+ * or a save_recent_image in the round counted it by its share: then each
278
+ * result of the round takes an equal share of that bound, and one past its
279
+ * share spills. For a call from a running
280
+ * code_execution script it is the longest result that reaches the script
281
+ * whole whatever its characters, from the script's memory: the script
282
+ * takes an answer of up to a third of its memoryMaxBytes, in which a
283
+ * character of the result takes at most six bytes (a control character,
284
+ * escaped as `\u0000`), so the figure is that third, less the 80 bytes of a
285
+ * text answer's envelope, over six: 7,456,527 at the default 128 MiB. On
286
+ * Node, past about 1.5 GiB, the host's longest string bounds it instead.
287
+ * A result with a `structured` value reaches a script as its content's
288
+ * blocks as they are, beside that value as JSON, and the figure counts
289
+ * neither the value nor what the blocks carry past their text, such as each
290
+ * further block's keys or an image's data, so a tool that returns one
291
+ * leaves room for them; and a result nested past 2,000 levels, or whose parse
292
+ * needs more of the script's memory than is free, doesn't reach the script
293
+ * whatever its length. A result that doesn't reach it is answered
294
+ * ResultTooLarge, the call having taken effect. Nothing is cut or saved to
295
+ * a file. A tool that pages or budgets its answer can stay under it. The
296
+ * framework sets it on a model's, a script's and the operator's puppet's
297
+ * call to a module, a utility run through `utils` included. A call
298
+ * made in process with ModuleContext.callTool or executeToolCall carries
299
+ * what its caller put on it, as the puppet's does; the framework's own
300
+ * read_image tool passes a model's call to the workspace without it; and a
301
+ * utility run through `utils` carries what its `utils` call carried.
302
+ * Without it, a module holds its answer to a bound of its own, if any: the
303
+ * workspace's is the house default, 24,000.
304
+ */
305
+ resultMaxChars?: number;
249
306
  }
250
307
 
251
308
  // ============================================================================