@animalabs/agent-framework 0.23.2 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/README.md +212 -42
  2. package/dist/src/agent.d.ts +60 -9
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +92 -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/code-execution/engine.d.ts +177 -0
  10. package/dist/src/code-execution/engine.d.ts.map +1 -0
  11. package/dist/src/code-execution/engine.js +494 -0
  12. package/dist/src/code-execution/engine.js.map +1 -0
  13. package/dist/src/code-execution/index.d.ts +5 -10
  14. package/dist/src/code-execution/index.d.ts.map +1 -1
  15. package/dist/src/code-execution/index.js +5 -9
  16. package/dist/src/code-execution/index.js.map +1 -1
  17. package/dist/src/code-execution/isolate/calls.d.ts +114 -0
  18. package/dist/src/code-execution/isolate/calls.d.ts.map +1 -0
  19. package/dist/src/code-execution/isolate/calls.js +145 -0
  20. package/dist/src/code-execution/isolate/calls.js.map +1 -0
  21. package/dist/src/code-execution/isolate/child-entry.d.ts +2 -0
  22. package/dist/src/code-execution/isolate/child-entry.d.ts.map +1 -0
  23. package/dist/src/code-execution/isolate/child-entry.js +5 -0
  24. package/dist/src/code-execution/isolate/child-entry.js.map +1 -0
  25. package/dist/src/code-execution/isolate/child-main.d.ts +26 -0
  26. package/dist/src/code-execution/isolate/child-main.d.ts.map +1 -0
  27. package/dist/src/code-execution/isolate/child-main.js +132 -0
  28. package/dist/src/code-execution/isolate/child-main.js.map +1 -0
  29. package/dist/src/code-execution/isolate/child.d.ts +44 -0
  30. package/dist/src/code-execution/isolate/child.d.ts.map +1 -0
  31. package/dist/src/code-execution/isolate/child.js +315 -0
  32. package/dist/src/code-execution/isolate/child.js.map +1 -0
  33. package/dist/src/code-execution/isolate/frames.d.ts +80 -0
  34. package/dist/src/code-execution/isolate/frames.d.ts.map +1 -0
  35. package/dist/src/code-execution/isolate/frames.js +210 -0
  36. package/dist/src/code-execution/isolate/frames.js.map +1 -0
  37. package/dist/src/code-execution/isolate/output.d.ts +68 -0
  38. package/dist/src/code-execution/isolate/output.d.ts.map +1 -0
  39. package/dist/src/code-execution/isolate/output.js +143 -0
  40. package/dist/src/code-execution/isolate/output.js.map +1 -0
  41. package/dist/src/code-execution/isolate/prelude.d.ts +109 -0
  42. package/dist/src/code-execution/isolate/prelude.d.ts.map +1 -0
  43. package/dist/src/code-execution/isolate/prelude.js +401 -0
  44. package/dist/src/code-execution/isolate/prelude.js.map +1 -0
  45. package/dist/src/code-execution/isolate/protocol.d.ts +151 -0
  46. package/dist/src/code-execution/isolate/protocol.d.ts.map +1 -0
  47. package/dist/src/code-execution/isolate/protocol.js +150 -0
  48. package/dist/src/code-execution/isolate/protocol.js.map +1 -0
  49. package/dist/src/code-execution/isolate/render.d.ts +57 -0
  50. package/dist/src/code-execution/isolate/render.d.ts.map +1 -0
  51. package/dist/src/code-execution/isolate/render.js +214 -0
  52. package/dist/src/code-execution/isolate/render.js.map +1 -0
  53. package/dist/src/code-execution/isolate/runner.d.ts +250 -0
  54. package/dist/src/code-execution/isolate/runner.d.ts.map +1 -0
  55. package/dist/src/code-execution/isolate/runner.js +854 -0
  56. package/dist/src/code-execution/isolate/runner.js.map +1 -0
  57. package/dist/src/code-execution/isolate/scripts.d.ts +175 -0
  58. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -0
  59. package/dist/src/code-execution/isolate/scripts.js +357 -0
  60. package/dist/src/code-execution/isolate/scripts.js.map +1 -0
  61. package/dist/src/code-execution/quickjs.wasm +0 -0
  62. package/dist/src/code-execution/tool-definition.d.ts +93 -43
  63. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  64. package/dist/src/code-execution/tool-definition.js +176 -142
  65. package/dist/src/code-execution/tool-definition.js.map +1 -1
  66. package/dist/src/deadline-timer.d.ts +3 -0
  67. package/dist/src/deadline-timer.d.ts.map +1 -1
  68. package/dist/src/deadline-timer.js +1 -1
  69. package/dist/src/deadline-timer.js.map +1 -1
  70. package/dist/src/framework.d.ts +1027 -440
  71. package/dist/src/framework.d.ts.map +1 -1
  72. package/dist/src/framework.js +4093 -2238
  73. package/dist/src/framework.js.map +1 -1
  74. package/dist/src/gate/event-gate.d.ts +28 -1
  75. package/dist/src/gate/event-gate.d.ts.map +1 -1
  76. package/dist/src/gate/event-gate.js +88 -10
  77. package/dist/src/gate/event-gate.js.map +1 -1
  78. package/dist/src/held-writes.d.ts +16 -3
  79. package/dist/src/held-writes.d.ts.map +1 -1
  80. package/dist/src/held-writes.js +16 -3
  81. package/dist/src/held-writes.js.map +1 -1
  82. package/dist/src/index.d.ts +6 -6
  83. package/dist/src/index.d.ts.map +1 -1
  84. package/dist/src/index.js +9 -8
  85. package/dist/src/index.js.map +1 -1
  86. package/dist/src/mcpl/channel-registry.d.ts +471 -75
  87. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  88. package/dist/src/mcpl/channel-registry.js +1673 -337
  89. package/dist/src/mcpl/channel-registry.js.map +1 -1
  90. package/dist/src/mcpl/conversation-router.d.ts +12 -7
  91. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  92. package/dist/src/mcpl/conversation-router.js +12 -7
  93. package/dist/src/mcpl/conversation-router.js.map +1 -1
  94. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  95. package/dist/src/mcpl/convert-block.js +9 -5
  96. package/dist/src/mcpl/convert-block.js.map +1 -1
  97. package/dist/src/mcpl/inbound-source.d.ts +50 -0
  98. package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
  99. package/dist/src/mcpl/inbound-source.js +130 -0
  100. package/dist/src/mcpl/inbound-source.js.map +1 -1
  101. package/dist/src/mcpl/index.d.ts +2 -1
  102. package/dist/src/mcpl/index.d.ts.map +1 -1
  103. package/dist/src/mcpl/index.js +3 -2
  104. package/dist/src/mcpl/index.js.map +1 -1
  105. package/dist/src/mcpl/log-value.d.ts +6 -0
  106. package/dist/src/mcpl/log-value.d.ts.map +1 -1
  107. package/dist/src/mcpl/log-value.js +8 -0
  108. package/dist/src/mcpl/log-value.js.map +1 -1
  109. package/dist/src/mcpl/modern-connection.d.ts +15 -6
  110. package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
  111. package/dist/src/mcpl/modern-connection.js +168 -53
  112. package/dist/src/mcpl/modern-connection.js.map +1 -1
  113. package/dist/src/mcpl/prose-grammar.d.ts +4 -5
  114. package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
  115. package/dist/src/mcpl/prose-grammar.js +4 -5
  116. package/dist/src/mcpl/prose-grammar.js.map +1 -1
  117. package/dist/src/mcpl/protocol-family.d.ts +97 -10
  118. package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
  119. package/dist/src/mcpl/protocol-family.js +173 -16
  120. package/dist/src/mcpl/protocol-family.js.map +1 -1
  121. package/dist/src/mcpl/push-coalescer.d.ts +132 -14
  122. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  123. package/dist/src/mcpl/push-coalescer.js +227 -50
  124. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  125. package/dist/src/mcpl/push-handler.d.ts +6 -1
  126. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  127. package/dist/src/mcpl/push-handler.js +4 -1
  128. package/dist/src/mcpl/push-handler.js.map +1 -1
  129. package/dist/src/mcpl/reference-fetcher.d.ts +2 -2
  130. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  131. package/dist/src/mcpl/reference-fetcher.js +4 -4
  132. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  133. package/dist/src/mcpl/server-connection.d.ts +50 -21
  134. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  135. package/dist/src/mcpl/server-connection.js +158 -74
  136. package/dist/src/mcpl/server-connection.js.map +1 -1
  137. package/dist/src/mcpl/tags.d.ts +24 -0
  138. package/dist/src/mcpl/tags.d.ts.map +1 -1
  139. package/dist/src/mcpl/tags.js +33 -0
  140. package/dist/src/mcpl/tags.js.map +1 -1
  141. package/dist/src/mcpl/tool-result-normalize.d.ts +2 -1
  142. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
  143. package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
  144. package/dist/src/mcpl/transport.d.ts +55 -7
  145. package/dist/src/mcpl/transport.d.ts.map +1 -1
  146. package/dist/src/mcpl/transport.js +163 -34
  147. package/dist/src/mcpl/transport.js.map +1 -1
  148. package/dist/src/mcpl/types.d.ts +22 -6
  149. package/dist/src/mcpl/types.d.ts.map +1 -1
  150. package/dist/src/mcpl/types.js.map +1 -1
  151. package/dist/src/module-registry.d.ts +4 -2
  152. package/dist/src/module-registry.d.ts.map +1 -1
  153. package/dist/src/module-registry.js +100 -13
  154. package/dist/src/module-registry.js.map +1 -1
  155. package/dist/src/modules/discord/index.d.ts.map +1 -1
  156. package/dist/src/modules/discord/index.js +8 -13
  157. package/dist/src/modules/discord/index.js.map +1 -1
  158. package/dist/src/modules/history/index.d.ts.map +1 -1
  159. package/dist/src/modules/history/index.js +27 -4
  160. package/dist/src/modules/history/index.js.map +1 -1
  161. package/dist/src/modules/history/semantic.d.ts +15 -3
  162. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  163. package/dist/src/modules/history/semantic.js +27 -5
  164. package/dist/src/modules/history/semantic.js.map +1 -1
  165. package/dist/src/modules/index.d.ts +1 -1
  166. package/dist/src/modules/index.d.ts.map +1 -1
  167. package/dist/src/modules/index.js.map +1 -1
  168. package/dist/src/modules/unknown-tool.d.ts +4 -1
  169. package/dist/src/modules/unknown-tool.d.ts.map +1 -1
  170. package/dist/src/modules/unknown-tool.js +4 -1
  171. package/dist/src/modules/unknown-tool.js.map +1 -1
  172. package/dist/src/modules/workspace/branch-intent.d.ts +3 -1
  173. package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
  174. package/dist/src/modules/workspace/branch-intent.js +3 -1
  175. package/dist/src/modules/workspace/branch-intent.js.map +1 -1
  176. package/dist/src/modules/workspace/edit-plan.d.ts +265 -0
  177. package/dist/src/modules/workspace/edit-plan.d.ts.map +1 -0
  178. package/dist/src/modules/workspace/edit-plan.js +794 -0
  179. package/dist/src/modules/workspace/edit-plan.js.map +1 -0
  180. package/dist/src/modules/workspace/edit-text.d.ts +117 -0
  181. package/dist/src/modules/workspace/edit-text.d.ts.map +1 -0
  182. package/dist/src/modules/workspace/edit-text.js +584 -0
  183. package/dist/src/modules/workspace/edit-text.js.map +1 -0
  184. package/dist/src/modules/workspace/effects.d.ts +5 -4
  185. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  186. package/dist/src/modules/workspace/effects.js +78 -25
  187. package/dist/src/modules/workspace/effects.js.map +1 -1
  188. package/dist/src/modules/workspace/index.d.ts +484 -61
  189. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  190. package/dist/src/modules/workspace/index.js +2173 -645
  191. package/dist/src/modules/workspace/index.js.map +1 -1
  192. package/dist/src/modules/workspace/observe.d.ts +213 -38
  193. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  194. package/dist/src/modules/workspace/observe.js +416 -36
  195. package/dist/src/modules/workspace/observe.js.map +1 -1
  196. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  197. package/dist/src/modules/workspace/reconcile.js +15 -8
  198. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  199. package/dist/src/modules/workspace/text.d.ts +32 -0
  200. package/dist/src/modules/workspace/text.d.ts.map +1 -0
  201. package/dist/src/modules/workspace/text.js +67 -0
  202. package/dist/src/modules/workspace/text.js.map +1 -0
  203. package/dist/src/modules/workspace/types.d.ts +210 -6
  204. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  205. package/dist/src/modules/workspace/types.js.map +1 -1
  206. package/dist/src/modules/workspace/watcher.d.ts +3 -2
  207. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  208. package/dist/src/modules/workspace/watcher.js.map +1 -1
  209. package/dist/src/operator-log.d.ts +52 -24
  210. package/dist/src/operator-log.d.ts.map +1 -1
  211. package/dist/src/operator-log.js.map +1 -1
  212. package/dist/src/recovery/discord-awareness-outbox.d.ts +496 -72
  213. package/dist/src/recovery/discord-awareness-outbox.d.ts.map +1 -1
  214. package/dist/src/recovery/discord-awareness-outbox.js +1196 -289
  215. package/dist/src/recovery/discord-awareness-outbox.js.map +1 -1
  216. package/dist/src/recovery/offline-branch.d.ts +26 -1
  217. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  218. package/dist/src/recovery/offline-branch.js +164 -39
  219. package/dist/src/recovery/offline-branch.js.map +1 -1
  220. package/dist/src/recovery/recover-cli.js +23 -4
  221. package/dist/src/recovery/recover-cli.js.map +1 -1
  222. package/dist/src/send-tools.d.ts +11 -0
  223. package/dist/src/send-tools.d.ts.map +1 -0
  224. package/dist/src/send-tools.js +11 -0
  225. package/dist/src/send-tools.js.map +1 -0
  226. package/dist/src/tool-result-guard.d.ts +4 -3
  227. package/dist/src/tool-result-guard.d.ts.map +1 -1
  228. package/dist/src/tool-result-guard.js +5 -4
  229. package/dist/src/tool-result-guard.js.map +1 -1
  230. package/dist/src/tool-result-history.d.ts +1 -1
  231. package/dist/src/tool-result-history.js +1 -1
  232. package/dist/src/tune-out/coordinator.d.ts +21 -0
  233. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  234. package/dist/src/tune-out/coordinator.js +139 -37
  235. package/dist/src/tune-out/coordinator.js.map +1 -1
  236. package/dist/src/types/agent.d.ts +4 -0
  237. package/dist/src/types/agent.d.ts.map +1 -1
  238. package/dist/src/types/events.d.ts +62 -9
  239. package/dist/src/types/events.d.ts.map +1 -1
  240. package/dist/src/types/framework.d.ts +62 -58
  241. package/dist/src/types/framework.d.ts.map +1 -1
  242. package/dist/src/types/module.d.ts +40 -8
  243. package/dist/src/types/module.d.ts.map +1 -1
  244. package/dist/src/types/trace.d.ts +111 -10
  245. package/dist/src/types/trace.d.ts.map +1 -1
  246. package/dist/src/usage/types.d.ts +16 -0
  247. package/dist/src/usage/types.d.ts.map +1 -1
  248. package/dist/src/usage/usage-tracker.d.ts +14 -1
  249. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  250. package/dist/src/usage/usage-tracker.js +81 -41
  251. package/dist/src/usage/usage-tracker.js.map +1 -1
  252. package/package.json +6 -5
  253. package/src/agent.ts +93 -12
  254. package/src/api/server.ts +14 -12
  255. package/src/code-execution/engine.ts +595 -0
  256. package/src/code-execution/index.ts +5 -17
  257. package/src/code-execution/isolate/calls.ts +199 -0
  258. package/src/code-execution/isolate/child-entry.ts +5 -0
  259. package/src/code-execution/isolate/child-main.ts +132 -0
  260. package/src/code-execution/isolate/child.ts +327 -0
  261. package/src/code-execution/isolate/frames.ts +209 -0
  262. package/src/code-execution/isolate/output.ts +161 -0
  263. package/src/code-execution/isolate/prelude.ts +409 -0
  264. package/src/code-execution/isolate/protocol.ts +240 -0
  265. package/src/code-execution/isolate/render.ts +230 -0
  266. package/src/code-execution/isolate/runner.ts +911 -0
  267. package/src/code-execution/isolate/scripts.ts +479 -0
  268. package/src/code-execution/quickjs.wasm +0 -0
  269. package/src/code-execution/quickjs.wasm.LICENSES +149 -0
  270. package/src/code-execution/tool-definition.ts +236 -167
  271. package/src/deadline-timer.ts +1 -1
  272. package/src/framework.ts +4468 -2418
  273. package/src/gate/event-gate.ts +78 -11
  274. package/src/held-writes.ts +17 -3
  275. package/src/index.ts +30 -22
  276. package/src/mcpl/channel-registry.ts +1825 -368
  277. package/src/mcpl/conversation-router.ts +12 -7
  278. package/src/mcpl/convert-block.ts +9 -5
  279. package/src/mcpl/inbound-source.ts +131 -0
  280. package/src/mcpl/index.ts +4 -2
  281. package/src/mcpl/log-value.ts +9 -0
  282. package/src/mcpl/modern-connection.ts +162 -53
  283. package/src/mcpl/prose-grammar.ts +4 -5
  284. package/src/mcpl/protocol-family.ts +199 -21
  285. package/src/mcpl/push-coalescer.ts +306 -62
  286. package/src/mcpl/push-handler.ts +10 -2
  287. package/src/mcpl/reference-fetcher.ts +4 -4
  288. package/src/mcpl/server-connection.ts +180 -79
  289. package/src/mcpl/tags.ts +36 -0
  290. package/src/mcpl/tool-result-normalize.ts +2 -1
  291. package/src/mcpl/transport.ts +162 -33
  292. package/src/mcpl/types.ts +23 -6
  293. package/src/module-registry.ts +92 -13
  294. package/src/modules/discord/index.ts +8 -13
  295. package/src/modules/history/index.ts +25 -3
  296. package/src/modules/history/semantic.ts +29 -6
  297. package/src/modules/index.ts +1 -0
  298. package/src/modules/unknown-tool.ts +4 -1
  299. package/src/modules/workspace/branch-intent.ts +3 -1
  300. package/src/modules/workspace/edit-plan.ts +954 -0
  301. package/src/modules/workspace/edit-text.ts +618 -0
  302. package/src/modules/workspace/effects.ts +70 -19
  303. package/src/modules/workspace/index.ts +2247 -642
  304. package/src/modules/workspace/observe.ts +465 -56
  305. package/src/modules/workspace/reconcile.ts +14 -7
  306. package/src/modules/workspace/text.ts +68 -0
  307. package/src/modules/workspace/types.ts +211 -6
  308. package/src/modules/workspace/watcher.ts +3 -2
  309. package/src/operator-log.ts +47 -19
  310. package/src/recovery/discord-awareness-outbox.ts +1500 -356
  311. package/src/recovery/offline-branch.ts +189 -55
  312. package/src/recovery/recover-cli.ts +25 -4
  313. package/src/send-tools.ts +11 -0
  314. package/src/tool-result-guard.ts +6 -5
  315. package/src/tool-result-history.ts +1 -1
  316. package/src/tune-out/coordinator.ts +163 -42
  317. package/src/types/agent.ts +4 -0
  318. package/src/types/events.ts +62 -9
  319. package/src/types/framework.ts +62 -58
  320. package/src/types/module.ts +40 -8
  321. package/src/types/trace.ts +121 -15
  322. package/src/usage/types.ts +16 -0
  323. package/src/usage/usage-tracker.ts +86 -43
  324. package/dist/src/code-execution/py-runner.d.ts +0 -191
  325. package/dist/src/code-execution/py-runner.d.ts.map +0 -1
  326. package/dist/src/code-execution/py-runner.js +0 -655
  327. package/dist/src/code-execution/py-runner.js.map +0 -1
  328. package/dist/src/code-execution/runtime-py.d.ts +0 -50
  329. package/dist/src/code-execution/runtime-py.d.ts.map +0 -1
  330. package/dist/src/code-execution/runtime-py.js +0 -677
  331. package/dist/src/code-execution/runtime-py.js.map +0 -1
  332. package/dist/src/mcpl/prose-stream-router.d.ts +0 -88
  333. package/dist/src/mcpl/prose-stream-router.d.ts.map +0 -1
  334. package/dist/src/mcpl/prose-stream-router.js +0 -291
  335. package/dist/src/mcpl/prose-stream-router.js.map +0 -1
  336. package/src/code-execution/py-runner.ts +0 -791
  337. package/src/code-execution/runtime-py.ts +0 -677
  338. package/src/mcpl/prose-stream-router.ts +0 -268
@@ -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
@@ -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 });
@@ -371,6 +371,10 @@ export interface InferenceRequest {
371
371
  * all of them without materializing content, the turn has no cause left
372
372
  * and does not run. */
373
373
  coalescingBatchSubjects?: string[];
374
+ /** Host-owned: this wake's sole cause is reading this deferred write, a
375
+ * coalesced render placed in its agent's queue (wakeToRead). Once the
376
+ * agent has read the write, or it was removed, the wake starts no turn. */
377
+ readsDeferredWrite?: string;
374
378
  agentName: string;
375
379
  reason: string;
376
380
  source: string;
@@ -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,47 @@ 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. For a call from a running
276
+ * code_execution script it is the longest result that reaches the script
277
+ * whole whatever its characters, from the script's memory: the script
278
+ * takes an answer of up to a third of its memoryMaxBytes, in which a
279
+ * character of the result takes at most six bytes (a control character,
280
+ * escaped as `\u0000`), so the figure is that third, less the 80 bytes of a
281
+ * text answer's envelope, over six: 7,456,527 at the default 128 MiB. On
282
+ * Node, past about 1.5 GiB, the host's longest string bounds it instead.
283
+ * A result with a `structured` value reaches a script as its content's
284
+ * blocks as they are, beside that value as JSON, and the figure counts
285
+ * neither the value nor what the blocks carry past their text, such as each
286
+ * further block's keys or an image's data, so a tool that returns one
287
+ * leaves room for them; and a result nested past 2,000 levels, or whose parse
288
+ * needs more of the script's memory than is free, doesn't reach the script
289
+ * whatever its length. A result that doesn't reach it is answered
290
+ * ResultTooLarge, the call having taken effect. Nothing is cut or saved to
291
+ * a file. A tool that pages or budgets its answer can stay under it. The
292
+ * framework sets it on a model's, a script's and the operator's puppet's
293
+ * call to a module, a utility run through `utils` included. A call
294
+ * made in process with ModuleContext.callTool or executeToolCall carries
295
+ * what its caller put on it, as the puppet's does; the framework's own
296
+ * read_image tool passes a model's call to the workspace without it; and a
297
+ * utility run through `utils` carries what its `utils` call carried.
298
+ * Without it, a module holds its answer to a bound of its own, if any: the
299
+ * workspace's is the house default, 24,000.
300
+ */
301
+ resultMaxChars?: number;
249
302
  }
250
303
 
251
304
  // ============================================================================
@@ -31,66 +31,66 @@ export interface ProcessLoggingConfig {
31
31
  */
32
32
  /**
33
33
  * Client-side programmatic tool calling: a synthesized `code_execution` tool
34
- * that runs model-authored Python on the host, with every framework tool
35
- * injected as an async function. Mirrors the trained surface of Anthropic's
36
- * server-side PTC; inner tool results never enter the model context.
34
+ * that runs model-authored JavaScript in QuickJS compiled to WebAssembly, in a
35
+ * child process of its own per script, with the agent's tools as async
36
+ * functions. Mirrors the trained surface of Anthropic's server-side PTC;
37
+ * inner tool results never enter the model context.
37
38
  *
38
- * Robustness boundary, not a security sandbox (same doctrine as gate.js —
39
- * the agent already has broader host access through its tools).
39
+ * A script adds nothing to what its agent's tools allow, provided the host
40
+ * runtime's WebAssembly engine and the glue around it are right. It claims no
41
+ * containment beyond that: the child runs as the host's user, with an empty
42
+ * environment and no operating-system sandbox around it.
40
43
  */
41
44
  export interface CodeExecutionConfig {
42
45
  /** Master switch — only `true` synthesizes the tool. `create()` refuses a
43
46
  * value that isn't a boolean, a quoted "false" included; `null` or leaving
44
47
  * it out counts as off. */
45
48
  enabled: boolean;
46
- /** Python interpreter to spawn (default: 'python3' from PATH). */
47
- pythonPath?: string;
48
- /**
49
- * Per-inner-tool-call timeout; exceeding it raises TimeoutError inside the
50
- * script (default 270_000 ms, mirroring the managed runtime's message).
51
- */
49
+ /** How long a run or a wait waits for its script before it yields, unless
50
+ * the agent asks otherwise with `yield_time_ms` (default 30_000 ms). */
51
+ yieldMs?: number;
52
+ /** The most an agent may ask a run or a wait to wait (default 600_000 ms).
53
+ * At least `yieldMs`. */
54
+ maxYieldMs?: number;
55
+ /** A script's limit: a script still running this long after it started is
56
+ * stopped, as "stopped at its limit" (default 86_400_000 ms, a day). */
57
+ scriptLifetimeMs?: number;
58
+ /** Scripts one agent may have running at once, yielded ones included
59
+ * (default 3). */
60
+ maxRunningScripts?: number;
61
+ /** Script processes the host runs at once, every agent's together
62
+ * (default 16). A run past it is refused at once, and nothing runs. */
63
+ maxScriptChildren?: number;
64
+ /** How long one of a script's tool calls may go unanswered before the
65
+ * script is told it timed out; the tool may still take effect
66
+ * (default 270_000 ms). */
52
67
  toolCallTimeoutMs?: number;
53
- /** Whole-script deadline: cancel → grace → SIGKILL (default 600_000 ms). */
54
- scriptTimeoutMs?: number;
55
- /**
56
- * Longest deadline an agent may ask for on one call with `time_limit_ms`
57
- * (default: `scriptTimeoutMs`, so agents can only shorten it until this is
58
- * raised). Longer requests are capped, and the result says so.
59
- */
60
- maxScriptTimeoutMs?: number;
61
- /**
62
- * Idle interpreter reclaim — script globals are lost after this much
63
- * inactivity (default 300_000 ms, mirroring ~5-minute container reclaim).
64
- * 0 disables reclaim.
65
- */
66
- idleReclaimMs?: number;
67
- /** Max concurrent background (daemon) scripts per agent (default 3). */
68
- maxBackgroundScripts?: number;
69
- /** Background script lifetime ceiling, ms (default 86_400_000 = 24h). */
70
- backgroundMaxLifetimeMs?: number;
71
68
  /** Minimum interval between wake_agent() deliveries, ms (default 60_000).
72
- * Early wakes are delayed, not dropped (backpressure inside the script). */
69
+ * Early wakes are delayed, not dropped. */
73
70
  wakeMinIntervalMs?: number;
74
- /** Max wake_agent() calls per background script (default 100); further
75
- * calls raise RuntimeError inside the script. */
71
+ /** Max wake_agent() calls per script (default 100); further calls are
72
+ * refused in the script. */
76
73
  maxWakesPerScript?: number;
77
- /** Variables for the python interpreter beyond the operating allowlist it
78
- * inherits (see CHILD_ENV_ALLOWLIST), such as PYTHONPATH. A recipe can
79
- * `${VAR}`-substitute them from .env. Other host variables aren't
80
- * inherited, which limits what model-authored code inherits, not what a
81
- * process running as the same user can read. The runner sets
82
- * PYTHONUNBUFFERED and PYTHONIOENCODING last, over any value given here,
83
- * since its protocol needs both. The interpreter's working directory is
84
- * its own runtime directory, so a relative path given here, as in
85
- * PYTHONPATH, resolves there: give absolute paths. */
86
- env?: Record<string, string>;
87
- /** The interpreter inherits the host's whole environment instead of the
88
- * allowlist: every host variable, provider keys included, reaches
89
- * model-authored code and every subprocess a script starts. Listing what
90
- * scripts need in `env` is the safer route. With code execution enabled,
91
- * `create()` refuses any value but true, false or null, which counts as
92
- * not set. */
93
- inheritEnv?: boolean;
74
+ /**
75
+ * The WebAssembly memory's maximum for each script, its budget: a whole
76
+ * number of 64 KiB pages from 4 MiB to 2 GiB (default 128 MiB). A script's
77
+ * process stays within about its runtime's base plus 2.7 times this, plus
78
+ * what its calls take as they go out: up to about 200 MiB under Node and
79
+ * 410 MiB under Bun. The host holds at most about 6 times this, plus 653 MiB,
80
+ * plus 128 KiB for each wake maxWakesPerScript allows, for a script: about
81
+ * 1.4 GiB at the defaults.
82
+ * That's its 16 calls out at once, each with at most 4 MiB of JSON
83
+ * arguments, timed-out ones whose tools still run included, up to 16 more
84
+ * waiting as their text for those to end, up to 16 answers until the child
85
+ * takes them, each at most a third of this, the frame from the child being
86
+ * read, and its waiting wakes. A tool's
87
+ * own result, and the one serialization the bridge makes of it to check its
88
+ * size, are the tool's, beside this. Size a host by both. A call past the 16
89
+ * the script waits on waits in the script, in this memory. A tool result too large for
90
+ * it reaches the script as ResultTooLarge; how much fits depends on the
91
+ * result's shape (see `IsolateSettings.memoryMaxBytes`).
92
+ */
93
+ memoryMaxBytes?: number;
94
94
  }
95
95
 
96
96
  /** See `FrameworkConfig.providerHold`. */
@@ -127,9 +127,10 @@ export interface FrameworkConfig {
127
127
  storePath?: string;
128
128
 
129
129
  /**
130
- * Branch-independent Discord awareness-marker outbox. Defaults to
130
+ * Branch-independent Discord awareness ledger. Defaults to
131
131
  * `<storePath>/recovery/discord-awareness-outbox.json`. Set explicitly when
132
- * providing an app-owned `store` without `storePath`; omit to disable.
132
+ * providing an app-owned `store` without `storePath`; with neither, the
133
+ * framework has no ledger and refuses a marks choice.
133
134
  */
134
135
  discordAwarenessOutboxPath?: string;
135
136
 
@@ -227,7 +228,7 @@ export interface FrameworkConfig {
227
228
 
228
229
  /**
229
230
  * Client-side programmatic tool calling (`code_execution` tool). Omitted or
230
- * `enabled: false` → tool absent, no python is ever spawned. At runtime,
231
+ * `enabled: false` → tool absent, and no script's process is ever started. At runtime,
231
232
  * untyped configs may also use `null` or `false` to omit this setting.
232
233
  * Other values that aren't objects are refused at `create()`.
233
234
  */
@@ -235,12 +236,12 @@ export interface FrameworkConfig {
235
236
 
236
237
  /**
237
238
  * Durable inline cap (chars) for tool results — successes AND errors — and
238
- * background-script wake payloads. Content over the cap is written to a
239
+ * script wake payloads (wake_agent()). Content over the cap is written to a
239
240
  * workspace file under `tool-results/` (deterministic `<date>-<callId>.txt`
240
241
  * name, overwritten on collision, retained until the workspace owner
241
242
  * deletes it — never auto-GC'd) and replaced inline by a bounded preview
242
- * plus the file reference; with no writable workspace the fallback is
243
- * explicit plain truncation. Default 24000 (house-safe; issue #89). Must be
243
+ * plus the file reference; with no writable mount without an include, the
244
+ * fallback is explicit plain truncation. Default 24000 (house-safe; issue #89). Must be
244
245
  * >= 1000. A resident's own agent_settings value
245
246
  * `tool_result_inline_max_chars` (durable, persisted in framework state)
246
247
  * takes precedence over this for that agent; the EFFECTIVE cap for every
@@ -460,13 +461,16 @@ export interface InferenceLogEntry {
460
461
  }
461
462
 
462
463
  /**
463
- * Entry in the process log - records a processed event with all module responses.
464
+ * Entry in the process log - records a processed event with the module
465
+ * responses that applied.
464
466
  */
465
467
  export interface ProcessLogEntry {
466
468
  timestamp: number;
467
469
  /** The process event that was handled */
468
470
  processEvent: ProcessEvent;
469
- /** Responses from all modules, or blob ID if large */
471
+ /** The modules' responses that applied, or blob ID if large. A response
472
+ * refused for content context-manager won't store is left out; its stderr
473
+ * line and process:response-refused trace name it. */
470
474
  responses: ModuleProcessResponse[] | { blobId: string };
471
475
  }
472
476
 
@@ -476,7 +480,7 @@ export interface ProcessLogEntry {
476
480
  export interface ProcessLogQuery {
477
481
  /** Filter by process event type */
478
482
  eventType?: string;
479
- /** Filter by module name (modules that responded) */
483
+ /** Filter by module name (modules whose responses applied) */
480
484
  moduleName?: string;
481
485
  /** Max number of logs to return */
482
486
  limit?: number;