@animalabs/agent-framework 0.26.2 → 0.28.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 (371) hide show
  1. package/README.md +99 -8
  2. package/dist/src/agent.d.ts +291 -23
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +596 -61
  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 +5 -4
  8. package/dist/src/api/server.js.map +1 -1
  9. package/dist/src/auto-continue.d.ts +34 -0
  10. package/dist/src/auto-continue.d.ts.map +1 -0
  11. package/dist/src/auto-continue.js +36 -0
  12. package/dist/src/auto-continue.js.map +1 -0
  13. package/dist/src/code-execution/isolate/scripts.d.ts +11 -0
  14. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
  15. package/dist/src/code-execution/isolate/scripts.js +5 -1
  16. package/dist/src/code-execution/isolate/scripts.js.map +1 -1
  17. package/dist/src/cut-off-round.d.ts +24 -0
  18. package/dist/src/cut-off-round.d.ts.map +1 -0
  19. package/dist/src/cut-off-round.js +87 -0
  20. package/dist/src/cut-off-round.js.map +1 -0
  21. package/dist/src/framework.d.ts +1038 -193
  22. package/dist/src/framework.d.ts.map +1 -1
  23. package/dist/src/framework.js +5967 -1514
  24. package/dist/src/framework.js.map +1 -1
  25. package/dist/src/gate/event-gate.d.ts +405 -46
  26. package/dist/src/gate/event-gate.d.ts.map +1 -1
  27. package/dist/src/gate/event-gate.js +2646 -399
  28. package/dist/src/gate/event-gate.js.map +1 -1
  29. package/dist/src/gate/index.d.ts +3 -2
  30. package/dist/src/gate/index.d.ts.map +1 -1
  31. package/dist/src/gate/index.js +2 -1
  32. package/dist/src/gate/index.js.map +1 -1
  33. package/dist/src/gate/recipe-reconcile.d.ts +103 -0
  34. package/dist/src/gate/recipe-reconcile.d.ts.map +1 -0
  35. package/dist/src/gate/recipe-reconcile.js +325 -0
  36. package/dist/src/gate/recipe-reconcile.js.map +1 -0
  37. package/dist/src/gate/reply-judge.d.ts +116 -0
  38. package/dist/src/gate/reply-judge.d.ts.map +1 -0
  39. package/dist/src/gate/reply-judge.js +467 -0
  40. package/dist/src/gate/reply-judge.js.map +1 -0
  41. package/dist/src/gate/types.d.ts +229 -8
  42. package/dist/src/gate/types.d.ts.map +1 -1
  43. package/dist/src/heartbeat/heartbeat.d.ts +184 -0
  44. package/dist/src/heartbeat/heartbeat.d.ts.map +1 -0
  45. package/dist/src/heartbeat/heartbeat.js +752 -0
  46. package/dist/src/heartbeat/heartbeat.js.map +1 -0
  47. package/dist/src/heartbeat/reminder-time.d.ts +5 -0
  48. package/dist/src/heartbeat/reminder-time.d.ts.map +1 -0
  49. package/dist/src/heartbeat/reminder-time.js +107 -0
  50. package/dist/src/heartbeat/reminder-time.js.map +1 -0
  51. package/dist/src/index.d.ts +10 -4
  52. package/dist/src/index.d.ts.map +1 -1
  53. package/dist/src/index.js +7 -2
  54. package/dist/src/index.js.map +1 -1
  55. package/dist/src/mcpl/capability-mask.d.ts.map +1 -1
  56. package/dist/src/mcpl/capability-mask.js +3 -2
  57. package/dist/src/mcpl/capability-mask.js.map +1 -1
  58. package/dist/src/mcpl/channel-registry.d.ts +433 -113
  59. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  60. package/dist/src/mcpl/channel-registry.js +1154 -435
  61. package/dist/src/mcpl/channel-registry.js.map +1 -1
  62. package/dist/src/mcpl/conversation-router.d.ts +16 -8
  63. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  64. package/dist/src/mcpl/conversation-router.js +29 -15
  65. package/dist/src/mcpl/conversation-router.js.map +1 -1
  66. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  67. package/dist/src/mcpl/convert-block.js +14 -2
  68. package/dist/src/mcpl/convert-block.js.map +1 -1
  69. package/dist/src/mcpl/hook-orchestrator.d.ts +7 -1
  70. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  71. package/dist/src/mcpl/hook-orchestrator.js +32 -7
  72. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  73. package/dist/src/mcpl/inbound-source.d.ts +26 -10
  74. package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
  75. package/dist/src/mcpl/inbound-source.js +40 -69
  76. package/dist/src/mcpl/inbound-source.js.map +1 -1
  77. package/dist/src/mcpl/index.d.ts +5 -1
  78. package/dist/src/mcpl/index.d.ts.map +1 -1
  79. package/dist/src/mcpl/index.js +4 -0
  80. package/dist/src/mcpl/index.js.map +1 -1
  81. package/dist/src/mcpl/inference-router.d.ts +14 -2
  82. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  83. package/dist/src/mcpl/inference-router.js +73 -28
  84. package/dist/src/mcpl/inference-router.js.map +1 -1
  85. package/dist/src/mcpl/prose-grammar.d.ts +4 -0
  86. package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
  87. package/dist/src/mcpl/prose-grammar.js +6 -0
  88. package/dist/src/mcpl/prose-grammar.js.map +1 -1
  89. package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
  90. package/dist/src/mcpl/protocol-family.js +3 -0
  91. package/dist/src/mcpl/protocol-family.js.map +1 -1
  92. package/dist/src/mcpl/push-coalescer.d.ts +20 -2
  93. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  94. package/dist/src/mcpl/push-coalescer.js +33 -11
  95. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  96. package/dist/src/mcpl/reference-fetcher.d.ts +63 -42
  97. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  98. package/dist/src/mcpl/reference-fetcher.js +177 -145
  99. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  100. package/dist/src/mcpl/references.d.ts +380 -46
  101. package/dist/src/mcpl/references.d.ts.map +1 -1
  102. package/dist/src/mcpl/references.js +736 -77
  103. package/dist/src/mcpl/references.js.map +1 -1
  104. package/dist/src/mcpl/server-connection.d.ts +93 -4
  105. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  106. package/dist/src/mcpl/server-connection.js +176 -11
  107. package/dist/src/mcpl/server-connection.js.map +1 -1
  108. package/dist/src/mcpl/tags.d.ts +9 -2
  109. package/dist/src/mcpl/tags.d.ts.map +1 -1
  110. package/dist/src/mcpl/tags.js +12 -2
  111. package/dist/src/mcpl/tags.js.map +1 -1
  112. package/dist/src/mcpl/tool-lifecycle.d.ts +11 -0
  113. package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
  114. package/dist/src/mcpl/tool-lifecycle.js +21 -0
  115. package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
  116. package/dist/src/mcpl/tool-presence.d.ts +311 -0
  117. package/dist/src/mcpl/tool-presence.d.ts.map +1 -0
  118. package/dist/src/mcpl/tool-presence.js +697 -0
  119. package/dist/src/mcpl/tool-presence.js.map +1 -0
  120. package/dist/src/mcpl/tool-result-normalize.d.ts +9 -3
  121. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
  122. package/dist/src/mcpl/tool-result-normalize.js +14 -6
  123. package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
  124. package/dist/src/mcpl/tool-signature.d.ts +46 -0
  125. package/dist/src/mcpl/tool-signature.d.ts.map +1 -0
  126. package/dist/src/mcpl/tool-signature.js +321 -0
  127. package/dist/src/mcpl/tool-signature.js.map +1 -0
  128. package/dist/src/mcpl/toolset-host.d.ts +251 -0
  129. package/dist/src/mcpl/toolset-host.d.ts.map +1 -0
  130. package/dist/src/mcpl/toolset-host.js +720 -0
  131. package/dist/src/mcpl/toolset-host.js.map +1 -0
  132. package/dist/src/mcpl/toolsets-module.d.ts +83 -0
  133. package/dist/src/mcpl/toolsets-module.d.ts.map +1 -0
  134. package/dist/src/mcpl/toolsets-module.js +523 -0
  135. package/dist/src/mcpl/toolsets-module.js.map +1 -0
  136. package/dist/src/mcpl/transport.d.ts +2 -2
  137. package/dist/src/mcpl/transport.d.ts.map +1 -1
  138. package/dist/src/mcpl/transport.js +3 -2
  139. package/dist/src/mcpl/transport.js.map +1 -1
  140. package/dist/src/mcpl/types.d.ts +24 -2
  141. package/dist/src/mcpl/types.d.ts.map +1 -1
  142. package/dist/src/mcpl/types.js.map +1 -1
  143. package/dist/src/mcpl/visible-content.d.ts +7 -9
  144. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  145. package/dist/src/mcpl/visible-content.js +21 -9
  146. package/dist/src/mcpl/visible-content.js.map +1 -1
  147. package/dist/src/module-registry.d.ts +27 -9
  148. package/dist/src/module-registry.d.ts.map +1 -1
  149. package/dist/src/module-registry.js +77 -17
  150. package/dist/src/module-registry.js.map +1 -1
  151. package/dist/src/modules/discord/index.js +1 -1
  152. package/dist/src/modules/discord/index.js.map +1 -1
  153. package/dist/src/modules/history/index.d.ts +2 -1
  154. package/dist/src/modules/history/index.d.ts.map +1 -1
  155. package/dist/src/modules/history/index.js +73 -28
  156. package/dist/src/modules/history/index.js.map +1 -1
  157. package/dist/src/modules/history/semantic.d.ts +108 -6
  158. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  159. package/dist/src/modules/history/semantic.js +273 -57
  160. package/dist/src/modules/history/semantic.js.map +1 -1
  161. package/dist/src/modules/workspace/answers.d.ts +22 -0
  162. package/dist/src/modules/workspace/answers.d.ts.map +1 -0
  163. package/dist/src/modules/workspace/answers.js +123 -0
  164. package/dist/src/modules/workspace/answers.js.map +1 -0
  165. package/dist/src/modules/workspace/branch-intent.d.ts +5 -3
  166. package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
  167. package/dist/src/modules/workspace/branch-intent.js +6 -2
  168. package/dist/src/modules/workspace/branch-intent.js.map +1 -1
  169. package/dist/src/modules/workspace/direct-write.d.ts +184 -0
  170. package/dist/src/modules/workspace/direct-write.d.ts.map +1 -0
  171. package/dist/src/modules/workspace/direct-write.js +417 -0
  172. package/dist/src/modules/workspace/direct-write.js.map +1 -0
  173. package/dist/src/modules/workspace/disk-agreement.d.ts +33 -11
  174. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
  175. package/dist/src/modules/workspace/disk-agreement.js +48 -17
  176. package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
  177. package/dist/src/modules/workspace/edit-text.d.ts +27 -12
  178. package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
  179. package/dist/src/modules/workspace/edit-text.js +61 -29
  180. package/dist/src/modules/workspace/edit-text.js.map +1 -1
  181. package/dist/src/modules/workspace/effects.d.ts +10 -0
  182. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  183. package/dist/src/modules/workspace/effects.js +21 -14
  184. package/dist/src/modules/workspace/effects.js.map +1 -1
  185. package/dist/src/modules/workspace/index.d.ts +308 -78
  186. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  187. package/dist/src/modules/workspace/index.js +2103 -720
  188. package/dist/src/modules/workspace/index.js.map +1 -1
  189. package/dist/src/modules/workspace/observe.d.ts +75 -63
  190. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  191. package/dist/src/modules/workspace/observe.js +366 -180
  192. package/dist/src/modules/workspace/observe.js.map +1 -1
  193. package/dist/src/modules/workspace/read-page.d.ts +41 -0
  194. package/dist/src/modules/workspace/read-page.d.ts.map +1 -0
  195. package/dist/src/modules/workspace/read-page.js +219 -0
  196. package/dist/src/modules/workspace/read-page.js.map +1 -0
  197. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  198. package/dist/src/modules/workspace/reconcile.js +26 -16
  199. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  200. package/dist/src/modules/workspace/result-text.d.ts +68 -0
  201. package/dist/src/modules/workspace/result-text.d.ts.map +1 -0
  202. package/dist/src/modules/workspace/result-text.js +94 -0
  203. package/dist/src/modules/workspace/result-text.js.map +1 -0
  204. package/dist/src/modules/workspace/sync.d.ts +9 -0
  205. package/dist/src/modules/workspace/sync.d.ts.map +1 -1
  206. package/dist/src/modules/workspace/sync.js +14 -0
  207. package/dist/src/modules/workspace/sync.js.map +1 -1
  208. package/dist/src/modules/workspace/text.d.ts +29 -12
  209. package/dist/src/modules/workspace/text.d.ts.map +1 -1
  210. package/dist/src/modules/workspace/text.js +35 -33
  211. package/dist/src/modules/workspace/text.js.map +1 -1
  212. package/dist/src/modules/workspace/types.d.ts +137 -60
  213. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  214. package/dist/src/modules/workspace/types.js.map +1 -1
  215. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  216. package/dist/src/modules/workspace/watcher.js +8 -3
  217. package/dist/src/modules/workspace/watcher.js.map +1 -1
  218. package/dist/src/prose-drafts.d.ts +4 -3
  219. package/dist/src/prose-drafts.d.ts.map +1 -1
  220. package/dist/src/prose-drafts.js +3 -2
  221. package/dist/src/prose-drafts.js.map +1 -1
  222. package/dist/src/reasoning-loss-notice.d.ts +23 -0
  223. package/dist/src/reasoning-loss-notice.d.ts.map +1 -0
  224. package/dist/src/reasoning-loss-notice.js +50 -0
  225. package/dist/src/reasoning-loss-notice.js.map +1 -0
  226. package/dist/src/reasoning-loss.d.ts +24 -0
  227. package/dist/src/reasoning-loss.d.ts.map +1 -0
  228. package/dist/src/reasoning-loss.js +102 -0
  229. package/dist/src/reasoning-loss.js.map +1 -0
  230. package/dist/src/send-tools.d.ts +3 -2
  231. package/dist/src/send-tools.d.ts.map +1 -1
  232. package/dist/src/send-tools.js +3 -2
  233. package/dist/src/send-tools.js.map +1 -1
  234. package/dist/src/sent-rows.d.ts +59 -0
  235. package/dist/src/sent-rows.d.ts.map +1 -0
  236. package/dist/src/sent-rows.js +145 -0
  237. package/dist/src/sent-rows.js.map +1 -0
  238. package/dist/src/silent-heartbeat.d.ts +17 -10
  239. package/dist/src/silent-heartbeat.d.ts.map +1 -1
  240. package/dist/src/silent-heartbeat.js.map +1 -1
  241. package/dist/src/stable-hash.d.ts +3 -0
  242. package/dist/src/stable-hash.d.ts.map +1 -0
  243. package/dist/src/stable-hash.js +15 -0
  244. package/dist/src/stable-hash.js.map +1 -0
  245. package/dist/src/stored-injections.d.ts +99 -0
  246. package/dist/src/stored-injections.d.ts.map +1 -0
  247. package/dist/src/stored-injections.js +106 -0
  248. package/dist/src/stored-injections.js.map +1 -0
  249. package/dist/src/text-as-stored.d.ts +4 -3
  250. package/dist/src/text-as-stored.d.ts.map +1 -1
  251. package/dist/src/text-as-stored.js +4 -3
  252. package/dist/src/text-as-stored.js.map +1 -1
  253. package/dist/src/thinking-drops.d.ts +74 -0
  254. package/dist/src/thinking-drops.d.ts.map +1 -0
  255. package/dist/src/thinking-drops.js +96 -0
  256. package/dist/src/thinking-drops.js.map +1 -0
  257. package/dist/src/tool-args.d.ts +12 -0
  258. package/dist/src/tool-args.d.ts.map +1 -1
  259. package/dist/src/tool-args.js +35 -1
  260. package/dist/src/tool-args.js.map +1 -1
  261. package/dist/src/tool-image-ledger.d.ts +9 -8
  262. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  263. package/dist/src/tool-image-ledger.js +9 -8
  264. package/dist/src/tool-image-ledger.js.map +1 -1
  265. package/dist/src/tool-presentation.d.ts.map +1 -1
  266. package/dist/src/tool-presentation.js +3 -1
  267. package/dist/src/tool-presentation.js.map +1 -1
  268. package/dist/src/tool-result-guard.d.ts +12 -6
  269. package/dist/src/tool-result-guard.d.ts.map +1 -1
  270. package/dist/src/tool-result-guard.js +44 -17
  271. package/dist/src/tool-result-guard.js.map +1 -1
  272. package/dist/src/tool-result-history.d.ts +176 -24
  273. package/dist/src/tool-result-history.d.ts.map +1 -1
  274. package/dist/src/tool-result-history.js +411 -91
  275. package/dist/src/tool-result-history.js.map +1 -1
  276. package/dist/src/tune-out/coordinator.d.ts +17 -9
  277. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  278. package/dist/src/tune-out/coordinator.js +84 -37
  279. package/dist/src/tune-out/coordinator.js.map +1 -1
  280. package/dist/src/tune-out/tools.d.ts +4 -1
  281. package/dist/src/tune-out/tools.d.ts.map +1 -1
  282. package/dist/src/types/agent.d.ts +217 -7
  283. package/dist/src/types/agent.d.ts.map +1 -1
  284. package/dist/src/types/events.d.ts +33 -9
  285. package/dist/src/types/events.d.ts.map +1 -1
  286. package/dist/src/types/framework.d.ts +39 -3
  287. package/dist/src/types/framework.d.ts.map +1 -1
  288. package/dist/src/types/module.d.ts +95 -9
  289. package/dist/src/types/module.d.ts.map +1 -1
  290. package/dist/src/types/trace.d.ts +91 -3
  291. package/dist/src/types/trace.d.ts.map +1 -1
  292. package/package.json +5 -4
  293. package/src/agent.ts +661 -67
  294. package/src/api/server.ts +5 -4
  295. package/src/auto-continue.ts +43 -0
  296. package/src/code-execution/isolate/scripts.ts +11 -3
  297. package/src/cut-off-round.ts +98 -0
  298. package/src/framework.ts +6168 -1540
  299. package/src/gate/event-gate.ts +2895 -405
  300. package/src/gate/index.ts +10 -1
  301. package/src/gate/recipe-reconcile.ts +355 -0
  302. package/src/gate/reply-judge.ts +512 -0
  303. package/src/gate/types.ts +234 -9
  304. package/src/heartbeat/heartbeat.ts +804 -0
  305. package/src/heartbeat/reminder-time.ts +107 -0
  306. package/src/index.ts +11 -4
  307. package/src/mcpl/capability-mask.ts +3 -2
  308. package/src/mcpl/channel-registry.ts +1222 -468
  309. package/src/mcpl/conversation-router.ts +34 -16
  310. package/src/mcpl/convert-block.ts +14 -1
  311. package/src/mcpl/hook-orchestrator.ts +33 -6
  312. package/src/mcpl/inbound-source.ts +42 -69
  313. package/src/mcpl/index.ts +6 -1
  314. package/src/mcpl/inference-router.ts +79 -34
  315. package/src/mcpl/prose-grammar.ts +7 -0
  316. package/src/mcpl/protocol-family.ts +2 -0
  317. package/src/mcpl/push-coalescer.ts +51 -13
  318. package/src/mcpl/reference-fetcher.ts +201 -162
  319. package/src/mcpl/references.ts +851 -77
  320. package/src/mcpl/server-connection.ts +189 -11
  321. package/src/mcpl/tags.ts +13 -2
  322. package/src/mcpl/tool-lifecycle.ts +21 -0
  323. package/src/mcpl/tool-presence.ts +716 -0
  324. package/src/mcpl/tool-result-normalize.ts +18 -6
  325. package/src/mcpl/tool-signature.ts +316 -0
  326. package/src/mcpl/toolset-host.ts +727 -0
  327. package/src/mcpl/toolsets-module.ts +545 -0
  328. package/src/mcpl/transport.ts +3 -2
  329. package/src/mcpl/types.ts +25 -2
  330. package/src/mcpl/visible-content.ts +22 -9
  331. package/src/module-registry.ts +85 -26
  332. package/src/modules/discord/index.ts +1 -1
  333. package/src/modules/history/index.ts +70 -30
  334. package/src/modules/history/semantic.ts +308 -54
  335. package/src/modules/workspace/answers.ts +133 -0
  336. package/src/modules/workspace/branch-intent.ts +10 -5
  337. package/src/modules/workspace/direct-write.ts +460 -0
  338. package/src/modules/workspace/disk-agreement.ts +52 -21
  339. package/src/modules/workspace/edit-text.ts +84 -35
  340. package/src/modules/workspace/effects.ts +22 -13
  341. package/src/modules/workspace/index.ts +2142 -723
  342. package/src/modules/workspace/observe.ts +381 -177
  343. package/src/modules/workspace/read-page.ts +228 -0
  344. package/src/modules/workspace/reconcile.ts +27 -15
  345. package/src/modules/workspace/result-text.ts +121 -0
  346. package/src/modules/workspace/sync.ts +16 -0
  347. package/src/modules/workspace/text.ts +58 -32
  348. package/src/modules/workspace/types.ts +137 -60
  349. package/src/modules/workspace/watcher.ts +8 -3
  350. package/src/prose-drafts.ts +4 -3
  351. package/src/reasoning-loss-notice.ts +62 -0
  352. package/src/reasoning-loss.ts +120 -0
  353. package/src/send-tools.ts +3 -2
  354. package/src/sent-rows.ts +162 -0
  355. package/src/silent-heartbeat.ts +17 -10
  356. package/src/stable-hash.ts +14 -0
  357. package/src/stored-injections.ts +131 -0
  358. package/src/text-as-stored.ts +4 -3
  359. package/src/thinking-drops.ts +135 -0
  360. package/src/tool-args.ts +34 -1
  361. package/src/tool-image-ledger.ts +9 -8
  362. package/src/tool-presentation.ts +3 -1
  363. package/src/tool-result-guard.ts +45 -17
  364. package/src/tool-result-history.ts +471 -90
  365. package/src/tune-out/coordinator.ts +80 -37
  366. package/src/tune-out/tools.ts +4 -1
  367. package/src/types/agent.ts +220 -7
  368. package/src/types/events.ts +33 -9
  369. package/src/types/framework.ts +36 -3
  370. package/src/types/module.ts +91 -9
  371. package/src/types/trace.ts +94 -3
@@ -11,8 +11,10 @@
11
11
  * and EditMany tools' answers.
12
12
  */
13
13
 
14
+ import type { ToolResult } from '../../types/events.js';
14
15
  import { type Change, type FilePlan, type Leaves, type MatchProblem, SHOWN_MATCHES } from './edit-plan.js';
15
- import { MAX_LINE_CHARS, jsonLength, numberedLine, safeCut } from './text.js';
16
+ import { answerText, fieldLine, sectionLead, sectionText, shownValue, textAnswer, type DiskState } from './result-text.js';
17
+ import { MAX_LINE_CHARS, numberedLine, safeCut } from './text.js';
16
18
 
17
19
  /** The tool a call came through, which words its refusals and receipt. */
18
20
  export type EditTool = 'edit' | 'edit_many';
@@ -190,8 +192,13 @@ function label(problem: EditProblem): string {
190
192
  */
191
193
  const FIRST_LINE_MAX = 400;
192
194
 
193
- /** What an answer is: a refusal, whose problems the agent answers and sends again, or the answer to edits stored, which mustn't be sent again. */
194
- export type AnswerKind = 'refusal' | 'stored';
195
+ /**
196
+ * What an answer is: a refusal, whose problems the agent answers and sends
197
+ * again; the answer to edits stored, which mustn't be sent again; or, on a
198
+ * direct mount, the answer to an edit_many call written partway, whose parts
199
+ * are each file's outcome.
200
+ */
201
+ export type AnswerKind = 'refusal' | 'stored' | 'written';
195
202
 
196
203
  /** A line cut to FIRST_LINE_MAX, as a part's first line is, and as a thrown message is in an answer's head. */
197
204
  export function cutLine(line: string): string {
@@ -205,18 +212,25 @@ export function cutLine(line: string): string {
205
212
  * shown and send the call again to see the rest: once those are answered,
206
213
  * their room can show the rest, details included. For edits stored, the
207
214
  * problems are the files disk refused, and a last line that leaves some out
208
- * points to ls.
215
+ * points to ls. For edits written partway, they're each file's outcome, and
216
+ * one that leaves some out points to read.
209
217
  */
210
218
  function tailText(kind: AnswerKind, maxChars: number, cut: number, problems: number, notes: number, anyShown: boolean): string {
211
219
  const items = [
212
220
  ...(cut ? [`the details of ${cut} of these`] : []),
213
- ...(problems ? [kind === 'refusal' ? `${problems} more ${plural(problems, 'problem')}` : `${problems} more ${plural(problems, 'file')} disk refused`] : []),
221
+ ...(problems
222
+ ? [kind === 'refusal'
223
+ ? `${problems} more ${plural(problems, 'problem')}`
224
+ : kind === 'stored' ? `${problems} more ${plural(problems, 'file')} disk refused` : `the outcomes of ${problems} more ${plural(problems, 'file')}`]
225
+ : []),
214
226
  ...(notes ? [`${notes} ${plural(notes, 'note')} on the files' copies on disk`] : []),
215
227
  ];
216
228
  if (!items.length) return '';
217
229
  const next = kind === 'stored'
218
230
  ? problems ? " ls shows each path's state." : ''
219
- : problems && anyShown ? ' Answer the problems shown and send the call again to see the rest.' : '';
231
+ : kind === 'written'
232
+ ? problems ? ' Reading a file shows it as disk has it now.' : ''
233
+ : problems && anyShown ? ' Answer the problems shown and send the call again to see the rest.' : '';
220
234
  const list = items.length < 2 ? items[0] : `${items.slice(0, -1).join(', ')} and ${items.at(-1)}`;
221
235
  return `[To keep this answer within ${maxChars.toLocaleString('en-US')} characters, it leaves out ${list}.${next}]`;
222
236
  }
@@ -271,9 +285,10 @@ export function fitted(head: string, problems: string[], notes: string[], maxCha
271
285
  * counts its problems and names each, in edit order, labelled with its edits.
272
286
  * `notes` follow the problems: which copy of a file the edits looked in, and
273
287
  * a conflict with disk, which the agent needs whether or not the call stored
274
- * anything.
288
+ * anything. On a direct mount, whose edits go to disk with no store between
289
+ * (`verb` 'wrote'), edit_many says it wrote none.
275
290
  */
276
- export function refusalText(tool: EditTool, problems: EditProblem[], notes: string[], maxChars: number): string {
291
+ export function refusalText(tool: EditTool, problems: EditProblem[], notes: string[], maxChars: number, verb: 'stored' | 'wrote' = 'stored'): string {
277
292
  const sorted = [...problems].sort((a, b) => firstEdit(a) - firstEdit(b));
278
293
  if (tool === 'edit') {
279
294
  const [problem] = sorted;
@@ -281,7 +296,7 @@ export function refusalText(tool: EditTool, problems: EditProblem[], notes: stri
281
296
  const whole = problem!.kind === 'file' && problem!.whole ? text : `The edit did nothing: ${text}`;
282
297
  return fitted('', [whole], notes, maxChars, 'refusal');
283
298
  }
284
- const head = `The edit_many call stored none of its edits: ${sorted.length} ${plural(sorted.length, 'problem')}.`;
299
+ const head = `The edit_many call ${verb} none of its edits: ${sorted.length} ${plural(sorted.length, 'problem')}.`;
285
300
  return fitted(head, sorted.map((problem) => `${label(problem)}${problemText(problem, tool)}`), notes, maxChars, 'refusal');
286
301
  }
287
302
 
@@ -350,7 +365,7 @@ function excerptLine(line: string, n: number, columns: number[]): string {
350
365
 
351
366
  /**
352
367
  * Each edit's first change in a file, as numbered regions merged where they
353
- * meet, while the budget lasts, counted as the answer's JSON holds them; past
368
+ * meet, while the budget lasts, counted as the answer's text holds them; past
354
369
  * it, the regions are named by their lines.
355
370
  */
356
371
  export function shownRegions(plan: FilePlan, budget: { left: number }, maxChars: number): string[] {
@@ -396,7 +411,7 @@ export function shownRegions(plan: FilePlan, budget: { left: number }, maxChars:
396
411
  // region of millions of lines, which is named by its lines, isn't built
397
412
  // whole first. The line before it ("…" between regions, or the line end)
398
413
  // counts too, and each line feed between its lines.
399
- let cost = out.length ? jsonLength('\n…\n') : jsonLength('\n');
414
+ let cost = out.length ? '\n…\n'.length : '\n'.length;
400
415
  const lines: string[] = [];
401
416
  let fits = !omitted.length;
402
417
  for (let lineStart = region.start, n = region.from; fits && lineStart < region.end; n++) {
@@ -407,7 +422,7 @@ export function shownRegions(plan: FilePlan, budget: { left: number }, maxChars:
407
422
  // changes start on it, a stretch from shortly before each.
408
423
  const columns = text.length > MAX_LINE_CHARS ? firsts.filter((first) => first.line === n).map((first) => first.at - lineStart) : [];
409
424
  const numberedText = columns.length ? excerptLine(text, n, columns) : numberedLine(text, n);
410
- cost += jsonLength(numberedText) + (lines.length ? jsonLength('\n') : 0);
425
+ cost += numberedText.length + (lines.length ? 1 : 0);
411
426
  if (cost > budget.left) fits = false;
412
427
  else lines.push(numberedText);
413
428
  lineStart = lineEnd + 1;
@@ -507,24 +522,56 @@ function receiptReserve(tool: EditTool, files: StoredFile[], maxChars: number):
507
522
  const listed = Math.min(SHOWN_MATCHES, plan.edits.length);
508
523
  const range = `${last}–${last}`;
509
524
  const note = regionsLeftOut(maxChars, Array.from({ length: listed }, () => range), plan.edits.length - listed, false);
510
- regions = Math.max(regions, jsonLength('\n') + jsonLength(note));
525
+ regions = Math.max(regions, 1 + note.length);
511
526
  }
512
527
  if (tool === 'edit') return regions;
513
528
  const most = files.length;
514
- return regions + jsonLength('\n\n') + jsonLength(filesLeftOut(maxChars, most, { notListed: most, unshown: most, notes: most, edits: most })!);
529
+ return regions + 2 + filesLeftOut(maxChars, most, { notListed: most, unshown: most, notes: most, edits: most })!.length;
530
+ }
531
+
532
+ /** A file edit_many lists, as its receipt's fields hold it. */
533
+ interface ListedFile {
534
+ path: string;
535
+ size: number;
536
+ replacements: number;
537
+ modeKept?: string;
538
+ edits?: number[];
539
+ note?: string;
540
+ }
541
+
542
+ /** edit_many's line for a file it lists: its path, size and replacements, and then the edits, the mode kept and the notes it has room for. */
543
+ function listedFileLine(file: ListedFile): string {
544
+ return `${shownValue(file.path)}: size ${file.size}, replacements ${file.replacements}`
545
+ + (file.edits !== undefined ? `, edits ${file.edits.join(', ')}` : '')
546
+ + (file.modeKept !== undefined ? `; modeKept: ${shownValue(file.modeKept)}` : '')
547
+ + (file.note !== undefined ? `. ${shownValue(file.note)}` : '');
548
+ }
549
+
550
+ /** edit_many's receipt's text before its changed regions: its counts, and the files it lists. */
551
+ function manyHeadText(data: { edits: number; replacements: number; disk: DiskState; files: ListedFile[]; filesNotListed?: number }): string {
552
+ const header = [fieldLine('edits', data.edits), fieldLine('replacements', data.replacements), fieldLine('disk', data.disk)];
553
+ if (data.filesNotListed !== undefined) header.push(fieldLine('filesNotListed', data.filesNotListed));
554
+ return answerText(header, [sectionText('files', data.files.map(listedFileLine).join('\n'))]);
515
555
  }
516
556
 
517
557
  /**
518
558
  * The receipt of edits stored: each file's size, replacements and notes, and
519
559
  * each edit's first changed region, numbered as read numbers lines, held to
520
- * `maxChars` as the answer's JSON holds it. Its counts and notes come first;
521
- * the regions take what is left, and past it a file's regions are named by
522
- * their lines. edit_many lists its files while they fit, a file whose notes
523
- * don't fit without them, or without its edits' numbers too, and counts the
524
- * rest. edit's receipt, of one file, keeps its counts and notes and shows no
525
- * region where those alone pass `maxChars`.
560
+ * `maxChars` as the answer's text holds it (result-text.ts). Its counts and
561
+ * notes come first; the regions take what is left, and past it a file's
562
+ * regions are named by their lines. edit_many lists its files while they
563
+ * fit, a file whose notes don't fit without them, or without its edits'
564
+ * numbers too, and counts the rest. edit's receipt, of one file, keeps its
565
+ * counts and notes and shows no region where those leave no room for it. The
566
+ * line that names what a receipt leaves out comes after what it keeps, and
567
+ * where its counts and notes leave too little room for that line too, as
568
+ * they can with long notes or a limit far under the 1,000 a residence or an
569
+ * agent can set, the receipt passes `maxChars` by it, and then reaches its
570
+ * caller as any result past its limit does: a model's spills, and a script's
571
+ * call answers ResultTooLarge. Otherwise the model reads the text, and a
572
+ * script gets the receipt's fields beside it.
526
573
  */
527
- export function editReceipt(tool: EditTool, files: StoredFile[], editCount: number, maxChars: number): Record<string, unknown> {
574
+ export function editReceipt(tool: EditTool, files: StoredFile[], editCount: number, maxChars: number, disk: DiskState): ToolResult {
528
575
  const reserve = receiptReserve(tool, files, maxChars);
529
576
  const notesOf = ({ plan, notes }: StoredFile): string[] => {
530
577
  const lineEnds = lineEndNote(plan);
@@ -544,28 +591,30 @@ export function editReceipt(tool: EditTool, files: StoredFile[], editCount: numb
544
591
  if (tool === 'edit') {
545
592
  const file = files[0]!;
546
593
  const notes = notesOf(file);
547
- const data: Record<string, unknown> = {
594
+ const head = {
548
595
  path: file.path,
549
596
  size: file.plan.bytes.length,
550
597
  replacements: file.plan.replacements,
598
+ disk,
551
599
  ...(notes.length ? { note: notes.join(' ') } : {}),
552
600
  ...(file.modeKept ? { modeKept: file.modeKept } : {}),
553
- changed: '',
554
601
  };
555
- const budget = { left: maxChars - JSON.stringify(data).length - reserve };
556
- data.changed = shownRegions(file.plan, budget, maxChars).join('\n');
557
- return data;
602
+ const header = Object.entries(head).map(([key, value]) => fieldLine(key, value));
603
+ const budget = { left: maxChars - answerText(header).length - sectionLead('changed') - reserve };
604
+ const changed = shownRegions(file.plan, budget, maxChars).join('\n');
605
+ return textAnswer(answerText(header, [sectionText('changed', changed)]), { ...head, changed });
558
606
  }
559
- const listed: Array<Record<string, unknown>> = [];
560
- const data: Record<string, unknown> = {
607
+ const listed: ListedFile[] = [];
608
+ const data: { edits: number; replacements: number; disk: DiskState; files: ListedFile[]; filesNotListed?: number; changed: string } = {
561
609
  edits: editCount,
562
610
  replacements: files.reduce((sum, file) => sum + file.plan.replacements, 0),
611
+ disk,
563
612
  files: listed,
564
613
  changed: '',
565
614
  };
566
- const fits = (entry: Record<string, unknown>) => {
615
+ const fits = (entry: ListedFile) => {
567
616
  listed.push(entry);
568
- const fit = JSON.stringify(data).length <= maxChars - reserve;
617
+ const fit = manyHeadText(data).length + sectionLead('changed') <= maxChars - reserve;
569
618
  listed.pop();
570
619
  return fit;
571
620
  };
@@ -574,7 +623,7 @@ export function editReceipt(tool: EditTool, files: StoredFile[], editCount: numb
574
623
  let notListed = 0;
575
624
  for (const [i, file] of files.entries()) {
576
625
  const notes = notesOf(file);
577
- const bare = {
626
+ const bare: ListedFile = {
578
627
  path: file.path,
579
628
  size: file.plan.bytes.length,
580
629
  replacements: file.plan.replacements,
@@ -597,22 +646,22 @@ export function editReceipt(tool: EditTool, files: StoredFile[], editCount: numb
597
646
  if (notes.length && !('note' in entry)) notesLeftOut++;
598
647
  if (!('edits' in entry)) editsLeftOut++;
599
648
  }
600
- const budget = { left: maxChars - JSON.stringify(data).length - reserve };
649
+ const budget = { left: maxChars - manyHeadText(data).length - sectionLead('changed') - reserve };
601
650
  const sections: string[] = [];
602
651
  let unshown = 0;
603
652
  for (const file of files.slice(0, listed.length)) {
604
- const heading = jsonLength(`${file.path}\n`) + (sections.length ? jsonLength('\n\n') : 0);
653
+ const heading = shownValue(file.path).length + 1 + (sections.length ? 2 : 0);
605
654
  if (heading >= budget.left) {
606
655
  unshown++;
607
656
  continue;
608
657
  }
609
658
  budget.left -= heading;
610
- sections.push(`${file.path}\n${shownRegions(file.plan, budget, maxChars).join('\n')}`);
659
+ sections.push(`${shownValue(file.path)}\n${shownRegions(file.plan, budget, maxChars).join('\n')}`);
611
660
  }
612
661
  const last = filesLeftOut(maxChars, files.length, { notListed, unshown, notes: notesLeftOut, edits: editsLeftOut });
613
662
  if (last !== null) sections.push(last);
614
663
  data.changed = sections.join('\n\n');
615
- return data;
664
+ return textAnswer(answerText([manyHeadText(data)], [sectionText('changed', data.changed)]), data);
616
665
  }
617
666
 
618
667
  const capitalized = (value: string) => value.charAt(0).toUpperCase() + value.slice(1);
@@ -47,7 +47,9 @@ import type { FileHandle } from 'node:fs/promises';
47
47
  import {
48
48
  type CheckedDir,
49
49
  type MountView,
50
+ LEAVES_MOUNT,
50
51
  NONBLOCK,
52
+ NOT_A_DIRECTORY,
51
53
  NOT_FOLLOWED,
52
54
  OUTSIDE_PARENT,
53
55
  ROOT_REPLACED,
@@ -58,7 +60,6 @@ import {
58
60
  leadsInto,
59
61
  nameOf,
60
62
  coversLanding,
61
- namesNestedPlace,
62
63
  nestedAtReal,
63
64
  parentOf,
64
65
  } from './observe.js';
@@ -232,7 +233,7 @@ export async function writeContained(
232
233
  createMode?: number,
233
234
  ): Promise<{ perm: number; widenRefused: boolean; modeKept: boolean }> {
234
235
  const checked = await parentFor(view, rootReal, rel, true);
235
- if (checked.kind !== 'checked') throw new EffectFailed('a parent path is not a directory', false);
236
+ if (checked.kind !== 'checked') throw new EffectFailed(NOT_A_DIRECTORY, false);
236
237
  const parent = checked.dir;
237
238
  const root = checked.root;
238
239
  const target = parent.at(nameOf(rel));
@@ -258,8 +259,16 @@ export async function writeContained(
258
259
  touched = true;
259
260
  return handle;
260
261
  } catch (err) {
261
- // O_EXCL refuses any entry: a file, a directory or a link.
262
- if (errno(err) === 'EEXIST') throw new EffectFailed('something appeared at this path since it was checked', false);
262
+ // O_EXCL refuses any entry: a file, a directory or a link, one that
263
+ // leads nowhere included, which a followed observation reads as absent.
264
+ if (errno(err) === 'EEXIST') {
265
+ const link = (await lstat(target).catch(() => null))?.isSymbolicLink() === true;
266
+ const leads = link ? await stat(target).then(() => 'somewhere', (e: unknown) => errno(e)) : null;
267
+ throw new EffectFailed(leads === 'ENOENT' || leads === 'ENOTDIR'
268
+ ? 'a symlink that leads nowhere is at this path, and a push makes a file only where nothing is, forced or not; '
269
+ + 'whoever runs this host can remove the link or make it lead to a file, and a materialize then writes it'
270
+ : 'something appeared at this path since it was checked', false);
271
+ }
263
272
  throw new EffectFailed(`cannot create the file (${errno(err) ?? message(err)})`, false);
264
273
  }
265
274
  };
@@ -385,11 +394,11 @@ export async function writeContained(
385
394
  * the link is what's refused. The opened file's check (checkOpened) asks
386
395
  * it, and so does a create, before anything is made.
387
396
  */
388
- async function refusedLanding(view: MountView, rootReal: string, rel: string, real: string): Promise<string | null> {
389
- const leaves = view.followSymlinks ? 'a symlink that leaves the mount' : OUTSIDE_PARENT;
397
+ export async function refusedLanding(view: MountView, rootReal: string, rel: string, real: string): Promise<string | null> {
398
+ const leaves = view.followSymlinks ? LEAVES_MOUNT : OUTSIDE_PARENT;
390
399
  if (!contained(rootReal, real)) return leaves;
391
- const holder = nestedAtReal(real, view.nested);
392
- if (holder) return namesNestedPlace(holder.rest, holder.include, false) ? leadsInto(view.name, rel, holder) : view.followSymlinks ? leaves : NOT_FOLLOWED;
400
+ const holder = await nestedAtReal(real, view.nested, { name: view.name, rootReal });
401
+ if (holder) return holder.names(false) ? leadsInto(view.name, rel, holder) : view.followSymlinks ? leaves : NOT_FOLLOWED;
393
402
  if (!(await coversLanding(view, rootReal, real, rel))) return view.followSymlinks ? leaves : NOT_FOLLOWED;
394
403
  return null;
395
404
  }
@@ -566,15 +575,15 @@ export async function unlinkContained(view: MountView, rootReal: string, rel: st
566
575
  // The link itself is what is removed; a followed one must still lie inside.
567
576
  if (!view.followSymlinks) throw new EffectFailed(NOT_FOLLOWED, false);
568
577
  const real = await realpath(target).catch(() => null);
569
- if (real === null || !contained(rootReal, real)) throw new EffectFailed('a symlink that leaves the mount', false);
578
+ if (real === null || !contained(rootReal, real)) throw new EffectFailed(LEAVES_MOUNT, false);
570
579
  // One leading into a nested mount's directory is that mount's: the link
571
580
  // and the file it leads to are left as they are. Where that mount's
572
581
  // include leaves the place out, it's no mount's, and the link leaves this
573
582
  // one (namesNestedPlace).
574
- const holder = nestedAtReal(real, view.nested);
583
+ const holder = await nestedAtReal(real, view.nested, { name: view.name, rootReal });
575
584
  if (holder) {
576
- const directory = holder.include !== undefined && await stat(real).then((s) => s.isDirectory(), () => false);
577
- throw new EffectFailed(namesNestedPlace(holder.rest, holder.include, directory) ? leadsInto(view.name, rel, holder) : 'a symlink that leaves the mount', false);
585
+ const directory = holder.judgedByInclude && await stat(real).then((s) => s.isDirectory(), () => false);
586
+ throw new EffectFailed(holder.names(directory) ? leadsInto(view.name, rel, holder) : LEAVES_MOUNT, false);
578
587
  }
579
588
  const through = await stat(real, { bigint: true }).catch(() => null);
580
589
  if (through === null) throw new EffectFailed(CHANGED, false);
@@ -586,7 +595,7 @@ export async function unlinkContained(view: MountView, rootReal: string, rel: st
586
595
  if (!through.isFile()) throw new EffectFailed(through.isDirectory() ? 'a symlink to a directory, not walked' : 'not a regular file', false);
587
596
  // One leading to a file this mount's own include leaves out leaves the
588
597
  // mount too, and that file isn't read for a hash (coversLanding).
589
- if (!(await coversLanding(view, rootReal, real, rel))) throw new EffectFailed('a symlink that leaves the mount', false);
598
+ if (!(await coversLanding(view, rootReal, real, rel))) throw new EffectFailed(LEAVES_MOUNT, false);
590
599
  file = through;
591
600
  } else if (info.isDirectory()) {
592
601
  throw new EffectFailed('a directory', false);