@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
@@ -72,6 +72,7 @@ import {
72
72
  readPath,
73
73
  sniffImageMime,
74
74
  walkScope,
75
+ withinIncludes,
75
76
  } from './observe.js';
76
77
  import { EffectFailed, type Expect, chainOf, chmodContained, narrowedTo, syncDirectories, unlinkContained, writeContained } from './effects.js';
77
78
 
@@ -856,16 +857,22 @@ async function gather(store: JsStore, agreement: DiskAgreement, mount: MountRunt
856
857
  }
857
858
 
858
859
  // Which paths to decide: what disk shows, and what the store, P and intent
859
- // track. The tracked ones are read synchronously, on this branch.
860
+ // track. The tracked ones are read synchronously, on this branch. A path
861
+ // the mount's include doesn't cover is outside the mount, so no pass
862
+ // decides it: the workspace's history may hold it from before the include,
863
+ // and it stays there as it is. So is one in a nested mount's directory
864
+ // that mount's include leaves out, such as history taken in before the
865
+ // mount was nested there: it's no mount's (namesNestedPlace).
860
866
  const candidates = new Set<string>();
867
+ const inside = (p: string): boolean => withinIncludes(mount.view, p);
861
868
  let walk: Walk | null = null;
862
869
  if (scope.kind === 'paths') {
863
- for (const p of scope.paths) candidates.add(p);
870
+ for (const p of scope.paths) if (inside(p)) candidates.add(p);
864
871
  } else {
865
872
  const prefix = scope.dir ? scope.dir + '/' : undefined;
866
- for (const e of store.treeList(mount.treeStateId, prefix)) if (inScope(e.path, scope)) candidates.add(e.path);
867
- for (const [p] of agreement.paths(mount.name, scope.dir)) if (inScope(p, scope)) candidates.add(p);
868
- for (const [p] of mount.intents.list(scope.dir)) if (inScope(p, scope)) candidates.add(p);
873
+ for (const e of store.treeList(mount.treeStateId, prefix)) if (inScope(e.path, scope) && inside(e.path)) candidates.add(e.path);
874
+ for (const [p] of agreement.paths(mount.name, scope.dir)) if (inScope(p, scope) && inside(p)) candidates.add(p);
875
+ for (const [p] of mount.intents.list(scope.dir)) if (inScope(p, scope) && inside(p)) candidates.add(p);
869
876
  if (rootUnavailable === null) {
870
877
  const resumeAfter = opts.resume ? agreement.walkResumesAfter(mount.name, scope.dir) : undefined;
871
878
  walk = await walkScope(mount.view, mount.rootReal, scope.dir, scope.recursive, opts.cap ?? mount.walkCap, {
@@ -1146,8 +1153,8 @@ export async function reconcilePass(
1146
1153
  // it, so it's always named. Another mount's directory met beneath the
1147
1154
  // scope is passed over by design even where this mount's tree holds
1148
1155
  // copies there: no tool of this mount syncs, pushes or clears them
1149
- // (listings show them, naming their holder), so naming the directory
1150
- // as unreached would never clear.
1156
+ // (listings show those that mount's include covers, naming their
1157
+ // holder), so naming the directory as unreached would never clear.
1151
1158
  const wholeScope = scope.kind === 'dir' && scope.dir !== '' && region.path === scope.dir;
1152
1159
  const unreached = hides && region.reason !== OTHER_MOUNT;
1153
1160
  if (region.kind === 'error' || region.kind === 'cap' || unreached || wholeScope) result.incomplete.push({ path: region.path, reason: region.reason });
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Lines numbered the way `cat -n` numbers them, with a long one cut, and a
3
+ * string's length as JSON, which a budget for a stored result counts. edit's
4
+ * answers use them.
5
+ */
6
+
7
+ /** Whether the bytes open with a UTF-8 byte order mark. */
8
+ export function hasBom(bytes: Uint8Array): boolean {
9
+ return bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf;
10
+ }
11
+
12
+ /** How long a line read shows before it cuts the line. */
13
+ export const MAX_LINE_CHARS = 2000;
14
+
15
+ /**
16
+ * Where to cut a string at or just before `at` without splitting a surrogate
17
+ * pair: a lone surrogate isn't text, and a model provider can refuse a
18
+ * request that carries one.
19
+ */
20
+ export function safeCut(value: string, at: number): number {
21
+ if (at <= 0 || at >= value.length) return Math.max(0, Math.min(at, value.length));
22
+ const before = value.charCodeAt(at - 1);
23
+ return before >= 0xd800 && before <= 0xdbff ? at - 1 : at;
24
+ }
25
+
26
+ /**
27
+ * One line numbered the way `cat -n` numbers it: a six-wide number, a tab and
28
+ * the line. A line longer than `maxChars` is cut, saying how many characters
29
+ * it holds past the cut, and `readOn` adds how to read them.
30
+ */
31
+ export function numberedLine(line: string, n: number, maxChars = MAX_LINE_CHARS, readOn?: (cut: number) => string): string {
32
+ const number = String(n).padStart(6);
33
+ if (line.length <= maxChars) return `${number}\t${line}`;
34
+ const cut = safeCut(line, maxChars);
35
+ return `${number}\t${line.slice(0, cut)} [… ${line.length - cut} more chars${readOn ? `; ${readOn(cut)}` : ''}]`;
36
+ }
37
+
38
+ /** Lines numbered from `start`, each as numberedLine shows it. */
39
+ export function numbered(lines: string[], start: number, maxChars = MAX_LINE_CHARS): string {
40
+ return lines.map((line, i) => numberedLine(line, start + i, maxChars)).join('\n');
41
+ }
42
+
43
+ /**
44
+ * How many characters a string takes inside JSON's quotes, as JSON.stringify
45
+ * writes it: a quote, a backslash or a control character is escaped, and so
46
+ * is a lone surrogate.
47
+ */
48
+ export function jsonLength(value: string): number {
49
+ let length = 0;
50
+ for (let i = 0; i < value.length; i++) length += jsonCharLength(value, i);
51
+ return length;
52
+ }
53
+
54
+ /** The JSON length of the code unit at `i`, as jsonLength counts it. */
55
+ export function jsonCharLength(value: string, i: number): number {
56
+ const c = value.charCodeAt(i);
57
+ if (c === 0x22 || c === 0x5c) return 2;
58
+ if (c < 0x20) return c === 0x08 || c === 0x09 || c === 0x0a || c === 0x0c || c === 0x0d ? 2 : 6;
59
+ if (c >= 0xd800 && c <= 0xdbff) {
60
+ const next = value.charCodeAt(i + 1);
61
+ return next >= 0xdc00 && next <= 0xdfff ? 1 : 6;
62
+ }
63
+ if (c >= 0xdc00 && c <= 0xdfff) {
64
+ const before = i > 0 ? value.charCodeAt(i - 1) : 0;
65
+ return before >= 0xd800 && before <= 0xdbff ? 1 : 6;
66
+ }
67
+ return 1;
68
+ }
@@ -16,6 +16,50 @@ export interface MountConfig {
16
16
  path: string;
17
17
  /** Access mode */
18
18
  mode: 'read-write' | 'read-only';
19
+ /**
20
+ * Where the mount's tools get what they read (default: 'chronicle').
21
+ * - 'chronicle': the workspace keeps the mount's files in its store, where
22
+ * writes and edits land first, and takes in disk's changes under the
23
+ * three-way rule as its listings, searches, syncs and scans reach them.
24
+ * A read of a path the store doesn't hold takes in a new file there;
25
+ * otherwise it reads the store's copy.
26
+ * - 'direct': for a checkout an agent reads, not one it changes. Every
27
+ * read, listing and search is disk's own copy as it is when it runs,
28
+ * and none of its files go into the store. No scan or watcher
29
+ * runs, so no call waits behind one, and `sync`, `materialize` and the
30
+ * watch settings have nothing to act on. Only with `mode: 'read-only'`,
31
+ * and not with `watch` other than 'never', `watchDebounceMs`,
32
+ * `watchRootPollMs`, a `wakeOnChange` that would wake (true, or a
33
+ * non-empty list) or `autoMaterialize: true`: each of those would never
34
+ * act, and the workspace refuses the mount. Its `ignore`
35
+ * has a default (see there). Paths, links, its `include`, other mounts'
36
+ * directories, size limits and the walk cap hold as on a 'chronicle'
37
+ * mount. Its tools read beneath whatever directory is at its
38
+ * root, with no check against the one a 'chronicle' run of it last
39
+ * agreed with. Where that root now lies elsewhere than the last start
40
+ * placed it, each call nests in it, as a start would now, every mount
41
+ * configured beneath its path, where that path leads beneath its root,
42
+ * and every other mount whose root, as that start placed it, lies
43
+ * beneath where its root lies now, or whose configured path lies
44
+ * beneath a directory that start found there. A mount configured
45
+ * elsewhere whose own root has moved since, and the other mounts' own
46
+ * placements, wait for the next start, so a direct root moved into
47
+ * another mount's directory is still that mount's too.
48
+ *
49
+ * A mount configured 'direct' runs as 'chronicle' from a start that finds
50
+ * the store holding changes there that disk lacks, on the branch selected
51
+ * then: a draft, a workspace deletion or a conflict, as `status` counts
52
+ * them pending, or an intent the workspace can't read, which may hold one
53
+ * of those. They stay in view, and the workspace says so on stderr and
54
+ * through the framework's ops alerts, naming them and the ways to start it
55
+ * direct. It runs unwatched, and walks by the direct kind's default
56
+ * ignore list where its config names none; `ls` without a path, and
57
+ * `status`, give its `backend` as 'chronicle' and that list. A mount that
58
+ * ran direct and runs as 'chronicle' again takes in what disk changed
59
+ * meanwhile as its listings, searches and syncs reach it, as an unwatched
60
+ * 'chronicle' mount does after any time away.
61
+ */
62
+ backend?: 'chronicle' | 'direct';
19
63
  /**
20
64
  * Watch mode for filesystem changes:
21
65
  * - 'always': chokidar watches continuously; each event re-observes its paths
@@ -25,8 +69,8 @@ export interface MountConfig {
25
69
  * the round goes on. Each scan walks from the mount's start, so on a
26
70
  * mount over its `walkCap` it covers the first `walkCap` entries every
27
71
  * time, and files past them come in as `walkCap` describes
28
- * - 'never': no background reads; listings and lazy reads still observe
29
- * the paths they show
72
+ * - 'never': no background reads; listings still observe the paths they
73
+ * show, and a read observes a path the store doesn't hold
30
74
  */
31
75
  watch?: 'always' | 'on-agent-action' | 'never';
32
76
  /** Debounce window in ms for watch: 'always' mode (default: 300) */
@@ -43,17 +87,157 @@ export interface MountConfig {
43
87
  * says, a directory with what lies beneath it: `.name.swp` and `.name.swx`
44
88
  * (the whole name), and names ending in `~` or in `.subl…tmp`, matched by
45
89
  * `^\..+\.sw[px]$`, `~$` and `\.subl.*\.tmp$`.
90
+ *
91
+ * A mount configured direct (`backend`) whose config names no list takes
92
+ * `node_modules`, `dist`, `.git`, `build`, `coverage`, `target`,
93
+ * `__pycache__` and `.venv`, each matching that name at any depth,
94
+ * whichever kind it runs as. A direct mount's walks of the whole mount,
95
+ * and of any path no pattern covers, pass over what the list covers. A
96
+ * listing, glob or grep whose path, or whose pattern's directory, the
97
+ * list covers is walked all the same, the patterns covering it set aside
98
+ * and the rest applying beneath it, as a sync that names a path walks it
99
+ * on a 'chronicle' mount.
46
100
  */
47
101
  ignore?: string[];
102
+ /**
103
+ * The files this mount holds, when it holds only some of what is under its
104
+ * path: the same pattern forms as `ignore`, read for files. A name or an
105
+ * extension glob covers a file by its own name, at any depth, and a directory
106
+ * glob (`dir/**`) covers what lies beneath that directory, and not a file
107
+ * named like it. Unlike `ignore`, a pattern that matches a directory's name
108
+ * covers nothing beneath it, since each file is covered by its own name:
109
+ * `["gate.json"]` holds every file named `gate.json`, and no other file
110
+ * beneath a directory of that name. A subtree is named with `dir/**`.
111
+ *
112
+ * It's the mount's boundary for content: no workspace path reads, stores,
113
+ * serves or writes back a file it doesn't cover, and no tool lists,
114
+ * matches or names one, beyond echoing a path it was given, which it may
115
+ * spell as disk does. A walk passes over one and counts it under
116
+ * `passedOver`, and passes over a link its names don't cover with the
117
+ * reason a mount without an include gives a link it doesn't enter, "a
118
+ * symlink, not walked", where the link leads to a directory, and wherever
119
+ * it leads on a mount that doesn't follow links. No pass decides a change
120
+ * the watcher reports there, and a materialize doesn't write it back even
121
+ * when the workspace's history holds it. A materialize that names a
122
+ * directory inside the mount counts each such entry beneath it under
123
+ * `passedOver`,
124
+ * with the reason and without its path, a deletion or conflict held there
125
+ * included; one of the whole mount takes none of them up. The file
126
+ * operations (read, write, edit, delete, and the module's binary and disk
127
+ * reads and writes) refuse a path it doesn't cover as outside the mount's
128
+ * include, a directory's included, and `resolveAbsolutePath` returns null
129
+ * for one; the mount's root is inside. Whether a name is taken isn't
130
+ * withheld: a refusal may show that something occupies a path the caller
131
+ * named. On a mount that follows links, a path is judged where it lands as
132
+ * well as where it's named, as disk spells it there: a covered path that
133
+ * reaches a file the include doesn't cover, through a link at the path or
134
+ * on its way, is refused as one that leaves the mount (the file operations
135
+ * refuse it before anything is stored), and a listing from where a link
136
+ * leads passes over such a file. A covered path whose landing realpath
137
+ * can't resolve, as for a loop or a directory this process can't search,
138
+ * is refused too, by every file operation, a delete included, since
139
+ * where it lands can't be judged.
140
+ *
141
+ * A sync, materialize, grep, glob or ls that names a path the include
142
+ * doesn't cover takes it as a directory, its scope, exactly where a listing
143
+ * names one: where disk has a directory reached from the mount's root
144
+ * through directories, with no link among them, or the workspace's history
145
+ * holds an entry beneath it that the include covers, and that a nested
146
+ * mount's include covers too where it lies in that mount's directory. A
147
+ * directory `ignore` covers is a scope too when it's named, though a
148
+ * listing passes over it. Each refuses any other path the include doesn't
149
+ * cover. A covered path is a file to a sync, materialize or grep when disk
150
+ * or history has a file there, and otherwise a directory; a glob takes its
151
+ * path as a directory. Beneath a directory, each passes over what the
152
+ * include doesn't cover, and a listing that isn't recursive names each
153
+ * directory directly beneath its path that disk has, and each that a
154
+ * covered entry in history implies, with only the files the include
155
+ * covers. A mount running direct has no history (`backend`), so there
156
+ * disk alone decides each of these. `ignore` still applies to what
157
+ * `include` covers.
158
+ *
159
+ * On a disk that folds names, the parse reads a path in disk's spelling,
160
+ * as the walk names what it lists, so a covered name whose file disk
161
+ * spells otherwise is outside the mount, and the include's refusal shows
162
+ * the path as given. A path that names one of the workspace's entries
163
+ * exactly, or does once its directories take disk's spelling, keeps the
164
+ * entry's spelling, as the parse gives it, and what lies past a link on
165
+ * the way keeps the caller's spelling, since the parse reads nothing past
166
+ * a link. Other answers name a path as the parse spells it, a name the
167
+ * include leaves out included, since how disk spells a name the caller
168
+ * gave is no content of that file. Where a
169
+ * followed path lands is judged as disk spells it there, and a path that
170
+ * meets no link where the parse names it.
171
+ * `resolveAbsolutePath`, which is synchronous, judges the include on the
172
+ * path as given and follows no link: on such a disk it returns the path to
173
+ * a covered name's file that disk spells otherwise, and null for a covered
174
+ * file named in another case.
175
+ *
176
+ * A path in a nested mount's directory is that mount's, whatever this
177
+ * include covers: every tool gives the answer that names the mount holding
178
+ * it and the path to use there. Through a link a mount follows, a place in
179
+ * that directory is named only where it's a directory, or a file that
180
+ * mount's own include covers with no file on its way, and is otherwise
181
+ * refused as leaving the mount. On a mount that doesn't follow links, a
182
+ * link on a path's way is refused, as on any such mount (followSymlinks).
183
+ * A nested mount's root is placed where disk has it at each start, and its
184
+ * directory is matched by spelling, so four cases fall outside this, as
185
+ * for any nested mount, and a fifth on a direct one: on a disk that folds
186
+ * names, a mount configured
187
+ * through a link that spells a directory otherwise than disk does isn't
188
+ * found nested, and this mount holds its files as its own; on such a disk,
189
+ * a path through a link that spells a nested mount's directory otherwise
190
+ * than that mount's configured path does, whether the link or the
191
+ * configured path is the one spelled otherwise than disk, isn't found to
192
+ * lead there, and this mount reads, lists, searches and writes there as
193
+ * its own, creating files too, though a walk from its root passes over the
194
+ * directory; on such a disk, a place in a nested mount's directory that a
195
+ * link does lead to is judged by that mount's include, and named, in the
196
+ * spelling the link's target gives, so a refusal can name a file disk
197
+ * spells outside that include, or leave a covered one unnamed; and a
198
+ * nested mount whose root moves while running, as `acceptRoot` takes,
199
+ * stays placed where it was until the next start; and where a direct
200
+ * mount's root is repointed between a read's parse and its open, a race
201
+ * left to whoever writes the disk, a mount nested in it by its
202
+ * directories alone, since its real root holds that root, can have the
203
+ * read serve a path in its directory.
204
+ *
205
+ * Text the host registers for a path (`registerGeneratedTextFile`) is
206
+ * served there whatever the include says, since it isn't one of the
207
+ * mount's files: it's matched by the path a spelling resolves to, before
208
+ * anything else checks the path, so every spelling that resolves to its
209
+ * registered path reads it, and none writes it. On a disk that folds names,
210
+ * a spelling in another case isn't its path, and reaches whatever file disk
211
+ * has there. The framework keeps its own files (`tool-results/`, `refs/`)
212
+ * only in a writable mount without an include, and what it kept in a mount
213
+ * before the mount had one is outside the mount once it has one, unless the
214
+ * include covers it.
215
+ *
216
+ * Left out, the mount holds everything under its path. Given, it's a list
217
+ * of at least one string, each a name, an `*.ext` glob or a `dir/**` glob,
218
+ * whose directory may be nested (`a/b/**`). The module refuses a pattern
219
+ * with a `/` that isn't such a glob, a `*` anywhere but a leading `*.` or
220
+ * that trailing `/**`, an empty, `.` or `..` segment, and, where `\` is the
221
+ * platform's separator, a `\`. Any other pattern is a file name, matched
222
+ * literally, so `[id].tsx` names the file of that name.
223
+ */
224
+ include?: string[];
48
225
  /**
49
226
  * Whether to follow symlinks (default: false). Without it, no symlink on a
50
227
  * path is followed: not one at the path itself, and not one among the
51
228
  * directories on its way beneath the mount's root, wherever it leads. A path
52
229
  * with a symlink among its directories is refused naming the link, and for
53
- * a link inside the mount, its target. A mount root that is
54
- * itself a symlink is still the root. With it, a symlink on a path is
55
- * followed while what it reaches stays inside the mount. Either way, a
56
- * listing or a sync's walk descends into no symlinked directory.
230
+ * a link inside the mount, its target where a tool could name it: a
231
+ * directory, or a file the include of the mount it lies in covers, where
232
+ * that mount has one. On a mount with an include, a link the include
233
+ * doesn't cover is outside the mount itself, and its refusal tells nothing
234
+ * of where it leads. A mount root that is itself a symlink is still the
235
+ * root. With it, a symlink on a path is followed while what it reaches
236
+ * stays inside the mount, which on a mount with an include means a
237
+ * directory or a file the include covers. Either way, a listing or a
238
+ * sync's walk descends
239
+ * into no symlinked directory beneath its scope; with it, a scope named
240
+ * through a link is listed from where the link leads.
57
241
  */
58
242
  followSymlinks?: boolean;
59
243
  /** Maximum file size in bytes (default: 5MB) */
@@ -148,6 +332,13 @@ export interface WorkspaceConfig {
148
332
  export interface MountState {
149
333
  /** The mount config */
150
334
  config: MountConfig;
335
+ /**
336
+ * Whether the mount runs direct (`MountConfig.backend`), as the workspace
337
+ * decides when initStore gives the store and again at each start: a mount
338
+ * configured direct runs as 'chronicle' while the store holds changes
339
+ * there that disk lacks.
340
+ */
341
+ direct: boolean;
151
342
  /** Tree state ID in Chronicle */
152
343
  treeStateId: string;
153
344
  /** Branch-local intent (tombstones, store origin, conflicts) for this mount's paths */
@@ -257,6 +448,20 @@ export interface EditInput {
257
448
  replaceAll?: boolean;
258
449
  }
259
450
 
451
+ export interface EditManyInput {
452
+ /** The edits, each matched in its file as the call reads it, all in one mount's files. */
453
+ edits: Array<{
454
+ /** File path (mount-prefixed) */
455
+ path: string;
456
+ /** Exact text to replace */
457
+ oldString: string;
458
+ /** Text to replace it with */
459
+ newString: string;
460
+ /** How many times oldString occurs in the file (default: 1); every one is replaced. */
461
+ occurrences?: number;
462
+ }>;
463
+ }
464
+
260
465
  export interface DeleteInput {
261
466
  /** File path (mount-prefixed) */
262
467
  path: string;
@@ -28,8 +28,9 @@ export interface WatcherEvents {
28
28
  * identical to an empty directory in the recorded state.
29
29
  */
30
30
  export interface WatcherLifecycle {
31
- /** Fires when chokidar completes its initial scan: once for each attach, so
32
- * again after a re-attach. */
31
+ /** Fires when chokidar completes an attach's initial scan, so again after a
32
+ * re-attach. An attach closed before its scan completes, by a re-attach or
33
+ * a stop, never fires it: chokidar drops its listeners at the close. */
33
34
  onReady?: () => void;
34
35
  /** Fires on any chokidar-reported error (ENOENT, EACCES, platform faults). */
35
36
  onError?: (err: Error) => void;
@@ -118,35 +118,63 @@ export function capIds(ids: string[]): { ids: string[]; count: number; truncated
118
118
  : { ids, count: ids.length };
119
119
  }
120
120
 
121
+ /**
122
+ * Facts every surgery marker receipt carries, whatever happened: the
123
+ * publication scope chosen (`none` unless the operator chose marks), how many
124
+ * removed addressable Discord messages the choice leaves unmarked (outside its
125
+ * scope or its authorized refs; whether the chosen ones are scheduled is the
126
+ * receipt's `status`), and how many authorized refs it did not remove (a
127
+ * delayed application whose anchor no longer covered them; their marks are
128
+ * never widened to anything else).
129
+ */
130
+ export interface SurgeryMarkerFacts {
131
+ scope: 'none' | 'addressed' | 'all';
132
+ unmarked: number;
133
+ notRemoved: number;
134
+ }
135
+
121
136
  /**
122
137
  * What a live surgery did about Discord awareness marks, reported apart from
123
138
  * the body change itself. A surgery that returns has applied its body change;
124
139
  * this says only whether marks were scheduled:
125
140
  *
126
- * - `none` — no marks were scheduled: the change removed no addressable
127
- * Discord message, or the awareness outbox is disabled (no
128
- * `discordAwarenessOutboxPath` and no `storePath`). It is not proof that
129
- * no addressable Discord message was removed: the surgery's `removedRefs`
130
- * (`discordRefs` in the operator log) say whether any was.
131
- * - `queued` — the outbox batch is durably active, and its delivery was
132
- * started in the background. If starting it fails (reconciliation reading
133
- * or writing the ledger), a `discord-awareness-delivery` ops alert reports
134
- * the failure, the batch stays durably active, and the next sync,
135
- * reconnect or start delivers it. It is not a delivery claim: per-message
136
- * outcomes stay in the outbox ledger.
141
+ * - `none` — no marks were requested (the default), or none of the removed
142
+ * messages fell within the chosen scope and authorized refs. It says
143
+ * nothing about whether Discord messages were removed: `unmarked` counts
144
+ * the addressable ones left unmarked, the surgery's `removedRefs` list
145
+ * every one, and the operator log's `discordRefs` counts them.
146
+ * - `queued` — one add request per mark is recorded, and delivered when its
147
+ * route is up: a pass starts at once unless the host is stopping, and a
148
+ * later one (a reconnect, a tools-list change or the next start) takes
149
+ * what remains. If a delivery pass fails, the requests stay recorded and
150
+ * the `discord-awareness-delivery` ops alert says so. The same alert names
151
+ * the marks a pass, or a start, gave up on: refused by Discord for good,
152
+ * or after three unconfirmed attempts. It is not a delivery claim: what
153
+ * Discord answers is kept in the awareness ledger.
137
154
  * - `not-scheduled` — the body change landed, but recording the batch as
138
- * active failed, so it was retired from the ledger: none of its marks will
139
- * be delivered.
140
- * - `unresolved` — the ledger could record neither the activation nor the
141
- * retirement. `batchId` names a retained batch whose scheduling outcome is
142
- * unresolved: any later reconciliation or restart that can read it may
143
- * promote and deliver it, and nothing here promises otherwise.
155
+ * active failed, or it was never recorded: none of its marks will be
156
+ * delivered. `error` says why, and whether the batch was retired or stays
157
+ * prepared for a start to retire.
144
158
  */
145
- export type SurgeryMarkerReceipt =
159
+ export type SurgeryMarkerReceipt = SurgeryMarkerFacts & (
146
160
  | { status: 'none'; queued: 0 }
147
161
  | { status: 'queued'; queued: number; batchId: string }
148
162
  | { status: 'not-scheduled'; queued: 0; error: string }
149
- | { status: 'unresolved'; queued: 0; batchId: string; error: string };
163
+ );
164
+
165
+ /** What a surgery would remove and which messages each marks scope covers. */
166
+ export interface SurgeryMarksPreview {
167
+ messagesRemoved: number;
168
+ /** Removed messages that carry a Discord address at all. */
169
+ addressable: number;
170
+ emoji: string;
171
+ scopes: Record<'addressed' | 'all', {
172
+ count: number;
173
+ channels: Array<{ channelId: string; count: number }>;
174
+ /** Pass back as `marks.refs` to bind the choice to exactly these. */
175
+ refs: Array<{ serverId: string; channelId: string; messageId: string }>;
176
+ }>;
177
+ }
150
178
 
151
179
  /** Thrown by live surgery methods when a request cannot be honored. `code`
152
180
  * lets surfaces distinguish "agent busy — quiesce first" from bad input. */