@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
@@ -0,0 +1,417 @@
1
+ /**
2
+ * A direct mount's writes (MountConfig.backend): disk is the only copy, so a
3
+ * write lands there whole when its call answers, or doesn't happen and leaves
4
+ * disk as it was. Ported from anima-research/hands-mcp's staged write
5
+ * (src/shared.ts), behind the mount's boundary as the workspace's other
6
+ * effects hold it (effects.ts):
7
+ *
8
+ * - The file's directory is located inside the mount (checkDir), and where
9
+ * the file lands is judged as a push's write judges it (refusedLanding):
10
+ * inside the mount, in no nested mount's directory, and covered by the
11
+ * mount's include. A link at the file is refused on a mount that doesn't
12
+ * follow links; on one that does, the write goes to the file it leads to
13
+ * when the call stages it, judged the same way, and the link stays.
14
+ * - The new bytes are staged beside the file, in `.workspace-staged-<id>`,
15
+ * at the file's mode, special bits included as far as the kernel lets this
16
+ * process set them (a setgid bit only for a member of the file's group),
17
+ * and renamed over it, so a reader beside the agent never sees half a file
18
+ * and a process killed mid-write leaves the old file whole. A file this
19
+ * process may not write is refused, as a push's write, which opens it for
20
+ * writing, is refused: the rename needs only the directory's permission.
21
+ * A process that dies between staging and its rename leaves the staged
22
+ * file, under that name, which `ls` and `git status` show; so does a
23
+ * refused write whose removal of its staged file fails or can't tell the
24
+ * file is its own, or whose directory another writer moved after the
25
+ * staging, since the removal goes by path (discard) and the path then
26
+ * leads elsewhere. Nothing sweeps it, and the answer doesn't name it.
27
+ * The removal checks the staged name holds that file and unlinks it with
28
+ * no turn of the event loop between, so only a file another writer puts
29
+ * there in that instant can be removed in its place, or one put there
30
+ * that a stat can't tell from it: on the freed inode number, with the
31
+ * same size, mode and owner, in the same tick of a coarse clock.
32
+ * - The rename gives a hard-linked path a file of its own, and the other
33
+ * links keep the old text: `otherLinks` counts them for the answer.
34
+ * - Just before the rename, an edit's file is checked unchanged since its
35
+ * read, by its bytes, read again, and its identity (checkUnchanged),
36
+ * synchronously, so the check and the renames after it run with no turn
37
+ * of the event loop between. Only a change landing in that instant isn't
38
+ * seen, which for edit_many runs from the check through its last rename:
39
+ * no rename can be made conditional.
40
+ * - No sync. The store-backed kind syncs its pushes for its agreement
41
+ * record (reconcile.ts), and a direct mount keeps none, so a power loss
42
+ * or a kernel crash can lose a direct write after its answer said it
43
+ * landed, as it can a hands-mcp write. A process dying can't: the rename is
44
+ * whole by then.
45
+ *
46
+ * Node has no openat or renameat, so the stage and the rename go through the
47
+ * checked directory's path: a directory replaced by another writer on disk
48
+ * between the directory's check and the staging, or after its last check
49
+ * (placedStill), can redirect them, as effects.ts says of a create. For
50
+ * edit_many that last check is each file's, before the other files' checks.
51
+ */
52
+ import { randomUUID } from 'node:crypto';
53
+ import { accessSync, closeSync, constants as fsConstants, lstatSync, openSync, readSync, renameSync, unlinkSync } from 'node:fs';
54
+ import { lstat, open, realpath } from 'node:fs/promises';
55
+ import { relative, sep } from 'node:path';
56
+ import { refusedLanding } from './effects.js';
57
+ import { ROOT_RECHECK, errnoWayOn, errnoWords, onlyTheHost, systemWords } from './answers.js';
58
+ import { NONBLOCK, LEAVES_MOUNT, NOT_A_DIRECTORY, NOT_FOLLOWED, OUTSIDE_PARENT, ROOT_UNAVAILABLE, checkDir, checkRoot, errno, rootAbsence, nameOf, parentOf, } from './observe.js';
59
+ const DIRECTORY_THERE = 'A file can\'t be written over a directory; give another path.';
60
+ const NOT_REGULAR_THERE = onlyTheHost('put a regular file there');
61
+ /** An open that follows no link at the path, where the platform has one. */
62
+ const NOFOLLOW = typeof fsConstants.O_NOFOLLOW === 'number' ? fsConstants.O_NOFOLLOW : 0;
63
+ /** The name a staged file starts with, beside the file it replaces. */
64
+ export const STAGED_PREFIX = '.workspace-staged-';
65
+ /**
66
+ * Why a direct write didn't happen when its mount's root was replaced under
67
+ * it: a direct mount keeps no root to sync to (MountConfig.backend), so its
68
+ * next call works beneath whatever is there then.
69
+ */
70
+ export const ROOT_MOVED = 'the mount root changed while this call was being applied: a new call works beneath whatever directory the mount\'s path leads to then';
71
+ export const identityOf = (info) => ({
72
+ dev: info.dev,
73
+ ino: info.ino,
74
+ size: info.size,
75
+ mode: info.mode,
76
+ uid: info.uid,
77
+ gid: info.gid,
78
+ mtimeNs: info.mtimeNs,
79
+ ctimeNs: info.ctimeNs,
80
+ });
81
+ export const sameIdentity = (a, b) => a.dev === b.dev && a.ino === b.ino && a.size === b.size && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid
82
+ && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
83
+ /** Another writer replaced, changed or removed the file between its read and its rename; nothing was written. */
84
+ export class FileChanged extends Error {
85
+ }
86
+ /** Why a direct write didn't happen, in words for its answer; disk is as it was. */
87
+ export class WriteRefused extends Error {
88
+ wayOn;
89
+ errno;
90
+ constructor(message, wayOn = null, errno) {
91
+ super(message);
92
+ this.wayOn = wayOn;
93
+ this.errno = errno;
94
+ }
95
+ }
96
+ const AFRESH = 'Calling again checks it afresh.';
97
+ const NAME_ITS_OWN = "Calling again won't change that; if you know the file it leads to, name that file by its own path.";
98
+ function rootWayOn(code) {
99
+ return code === 'ENOENT' || code === 'ENOTDIR' ? onlyTheHost("put the mount's directory back at its path") : errnoWayOn(code, ROOT_RECHECK, true);
100
+ }
101
+ function refusedWayOn(reason, code, hostsName = false) {
102
+ if (reason === ROOT_UNAVAILABLE)
103
+ return rootWayOn(code);
104
+ if (code !== undefined)
105
+ return errnoWayOn(code, undefined, hostsName);
106
+ if (reason === NOT_FOLLOWED)
107
+ return NAME_ITS_OWN;
108
+ if (reason === OUTSIDE_PARENT || reason === LEAVES_MOUNT)
109
+ return "Calling again won't change that; the workspace writes only inside its mounts.";
110
+ if (reason === NOT_A_DIRECTORY)
111
+ return "Calling again won't change that while something other than a directory stands where the path needs one; another path, or removing what stands there, would.";
112
+ return null;
113
+ }
114
+ /**
115
+ * Test seams. `afterStage` is called once a call's files are staged, before
116
+ * they're checked unchanged (another writer acting then); `beforeRename`
117
+ * just before each rename, inside its refusal, so throwing stands for the
118
+ * rename failing. Production sets neither.
119
+ */
120
+ export const directWriteTestSeam = {};
121
+ /** The mount-relative path of a real path inside the mount's root, `/`-separated. */
122
+ function relativeTo(rootReal, real) {
123
+ return relative(rootReal, real).split(sep).join('/');
124
+ }
125
+ /**
126
+ * The checked directory a mount-relative file lies in, beneath a checked
127
+ * root, or why not. With `makeDirectory`, missing directories are made, as
128
+ * a push's write makes them; without it, a missing one refuses the write,
129
+ * since an edit replaces a file that exists.
130
+ */
131
+ async function directoryOf(view, rootReal, rel, makeDirectory) {
132
+ const checked = await checkDir(view, rootReal, parentOf(rel), { create: makeDirectory });
133
+ if (checked.kind === 'checked')
134
+ return checked.dir;
135
+ if (checked.kind === 'absent')
136
+ throw new FileChanged(rel);
137
+ const reason = checked.outside ? OUTSIDE_PARENT : checked.reason;
138
+ throw new WriteRefused(reason, refusedWayOn(reason, checked.errno));
139
+ }
140
+ /**
141
+ * Stage `bytes` beside the mount-relative file `rel`, to be renamed over it:
142
+ * the first half of a direct write. The file keeps its mode, special bits
143
+ * included, as far as the kernel lets this process set them; a new file
144
+ * takes `0666` less the umask, as a shell's would. Its owner and group are
145
+ * this process's and the ones its directory gives a new file, and an ACL or
146
+ * extended attribute on the file isn't carried over, as with any write that
147
+ * renames over a file. Anything there that isn't a regular file is refused,
148
+ * and a refusal removes the staged file it made, except where the header
149
+ * says one is left. The staged file is written and moded through its own
150
+ * handle, never through its name again.
151
+ */
152
+ export async function stageContained(view, rootReal, rel, bytes, { makeDirectory = false } = {}) {
153
+ const root = await checkRoot(view, rootReal);
154
+ if ('reason' in root) {
155
+ const code = root.errno ?? await rootAbsence(rootReal);
156
+ throw new WriteRefused(root.reason, rootWayOn(code), code);
157
+ }
158
+ let parent = await directoryOf(view, rootReal, rel, makeDirectory);
159
+ let path = parent.at(nameOf(rel));
160
+ let info = await lstat(path, { bigint: true }).catch((err) => {
161
+ if (errno(err) === 'ENOENT')
162
+ return null;
163
+ throw new WriteRefused(`cannot stat the file (${systemWords(errno(err), err)})`, errnoWayOn(errno(err)), errno(err));
164
+ });
165
+ if (info?.isSymbolicLink()) {
166
+ if (!view.followSymlinks)
167
+ throw new WriteRefused(NOT_FOLLOWED, NAME_ITS_OWN);
168
+ // A followed link stands for the file it leads to, as a read takes it:
169
+ // the write goes there, judged where it lands, and the link stays.
170
+ const real = await realpath(path).catch((err) => {
171
+ throw errno(err) === 'ENOENT'
172
+ ? new WriteRefused("a symlink whose target doesn't exist", onlyTheHost('remove the link or make it lead to a file'))
173
+ : new WriteRefused(`cannot resolve the symlink (${systemWords(errno(err), err)})`, errnoWayOn(errno(err)), errno(err));
174
+ });
175
+ const leaves = await refusedLanding(view, rootReal, rel, real);
176
+ if (leaves !== null)
177
+ throw new WriteRefused(leaves, refusedWayOn(leaves));
178
+ const realRel = relativeTo(rootReal, real);
179
+ parent = await directoryOf(view, rootReal, realRel, false);
180
+ path = parent.at(nameOf(realRel));
181
+ info = await lstat(path, { bigint: true }).catch((err) => {
182
+ // Gone since realpath found it: another writer's change.
183
+ if (errno(err) === 'ENOENT')
184
+ throw new FileChanged(rel);
185
+ throw new WriteRefused(`cannot stat the file the symlink leads to (${systemWords(errno(err), err)})`, errnoWayOn(errno(err)), errno(err));
186
+ });
187
+ if (info.isSymbolicLink())
188
+ throw new FileChanged(rel);
189
+ }
190
+ else {
191
+ // A file, or none yet, reached through a link among its directories
192
+ // lands where the mount may not act: in a nested mount's directory, or
193
+ // on a file its own include leaves out (refusedLanding).
194
+ const leaves = await refusedLanding(view, rootReal, rel, path);
195
+ if (leaves !== null)
196
+ throw new WriteRefused(leaves, refusedWayOn(leaves));
197
+ }
198
+ if (info?.isDirectory())
199
+ throw new WriteRefused('a directory', DIRECTORY_THERE);
200
+ if (info && !info.isFile())
201
+ throw new WriteRefused('not a regular file', NOT_REGULAR_THERE);
202
+ // A fixed-length name, so a file whose own name is near the 255-byte limit can still be replaced.
203
+ const temp = parent.at(`${STAGED_PREFIX}${randomUUID()}`);
204
+ let made = false;
205
+ try {
206
+ // A new file is made at 0666 and the kernel takes the umask off as it
207
+ // makes it. A file being replaced gives the staged file its own mode,
208
+ // which only a chmod can set past the umask, and only after the bytes
209
+ // are written, since a write clears a set-user or set-group bit; until
210
+ // then the staged file is the owner's alone, so no other user can read
211
+ // a private file's new text while it's staged.
212
+ const handle = await open(temp, 'wx', info ? 0o600 : 0o666);
213
+ made = true;
214
+ let staged;
215
+ try {
216
+ await handle.writeFile(bytes);
217
+ if (info)
218
+ await handle.chmod(Number(info.mode & 4095n));
219
+ const own = await handle.stat({ bigint: true });
220
+ staged = { path, temp, tempIdentity: identityOf(own), root: root.dir, parent, otherLinks: 0 };
221
+ }
222
+ catch (err) {
223
+ // Removed while its handle is still open, so its device and inode
224
+ // name it and no other file: one made at its name since can't have
225
+ // taken its inode number. One that can't be told is left.
226
+ const mine = await handle.stat({ bigint: true }).catch(() => null);
227
+ if (mine)
228
+ removeIf(temp, (there) => there.dev === mine.dev && there.ino === mine.ino);
229
+ await handle.close().catch(() => undefined);
230
+ throw err;
231
+ }
232
+ try {
233
+ await handle.close();
234
+ }
235
+ catch (err) {
236
+ // Closed, or not, with its bytes in doubt: removed by its whole identity, as a refusal's.
237
+ await discard(staged);
238
+ throw err;
239
+ }
240
+ return staged;
241
+ }
242
+ catch (err) {
243
+ // A staged file that couldn't be made names the directory: a push's
244
+ // write in place needs only the file, and this one needs both.
245
+ throw new WriteRefused(made
246
+ ? `cannot write the staged file beside it (${systemWords(errno(err), err)})`
247
+ : `cannot make the staged file in its directory (${systemWords(errno(err), err)}): a direct write needs to write the file's directory as well as the file`, errnoWayOn(errno(err)), errno(err));
248
+ }
249
+ }
250
+ /**
251
+ * Throw unless the root and the file's directory are still the ones the
252
+ * staging checked, with a push's words for the directory (effects.ts
253
+ * moved()).
254
+ */
255
+ export async function placedStill(staged) {
256
+ if (!(await staged.root.stillHere()))
257
+ throw new WriteRefused(ROOT_MOVED);
258
+ if (!(await staged.parent.stillHere()))
259
+ throw new WriteRefused('the directory changed since it was checked', AFRESH);
260
+ }
261
+ /**
262
+ * Throw unless the file is still what its read found: FileChanged when it
263
+ * changed or is gone, nothing written. `expected` is null for a write, which
264
+ * read nothing and replaces whatever is there. Either way a link swapped in
265
+ * at the file the rename replaces (on a mount that follows links, the file
266
+ * the link at the agent's path led to when the call staged it) is refused
267
+ * rather than replaced, and so is a file this process may not write. It reads the entry the
268
+ * rename replaces, and then the file's bytes, synchronously, as renameInto
269
+ * runs, so a check and the renames after it run with no turn of the event
270
+ * loop between.
271
+ *
272
+ * The identity, taken once the bytes are read, catches a file replaced or
273
+ * re-moded under the call, which equal bytes wouldn't show. The bytes are
274
+ * compared with the ones the edit read: a kernel that stamps files from a
275
+ * coarse clock, as Linux before 6.13 does, leaves a file's identity as it
276
+ * was after a second write of the same size within one tick of its last
277
+ * change (on Debian's Linux 6.12.113 with ext4, 1,632 to 1,752 of 2,000
278
+ * such rewrites kept every stamp, in three runs), and the bytes show that
279
+ * change on every clock. The bytes are read through an open that follows
280
+ * no link and doesn't wait on a pipe put at the path since its lstat, and
281
+ * at most one byte past what the edit read, so the check never blocks and
282
+ * reads no more than that.
283
+ */
284
+ export function checkUnchanged(staged, expected) {
285
+ let now;
286
+ try {
287
+ now = lstatSync(staged.path, { bigint: true });
288
+ }
289
+ catch (err) {
290
+ if (errno(err) !== 'ENOENT')
291
+ throw new WriteRefused(`cannot check that it hadn't changed (${systemWords(errno(err), err)})`, errnoWayOn(errno(err)), errno(err));
292
+ if (expected !== null)
293
+ throw new FileChanged(staged.path);
294
+ return;
295
+ }
296
+ if (now.isSymbolicLink())
297
+ throw new WriteRefused('it became a symlink while this call was being applied', AFRESH);
298
+ if (now.isDirectory())
299
+ throw new WriteRefused('a directory', DIRECTORY_THERE);
300
+ if (!now.isFile())
301
+ throw new WriteRefused('not a regular file', NOT_REGULAR_THERE);
302
+ // As a push's write, which opens the file for writing, would be refused:
303
+ // the rename needs only the directory's permission, and would replace a
304
+ // file its owner made read-only.
305
+ try {
306
+ accessSync(staged.path, fsConstants.W_OK);
307
+ }
308
+ catch (err) {
309
+ const code = errno(err);
310
+ // Removed since the lstat: a write makes the file anew; an edit's read is gone.
311
+ if (code === 'ENOENT') {
312
+ if (expected === null)
313
+ return;
314
+ throw new FileChanged(staged.path);
315
+ }
316
+ if (code === 'EACCES' || code === 'EPERM') {
317
+ throw new WriteRefused(`a file the host's process may not write (${errnoWords(code)}): its mode or owner keeps that process out, and calling again is refused until the file's owner, or whoever runs the host, changes that`);
318
+ }
319
+ throw new WriteRefused(`cannot check that the host's process may write it (${systemWords(code, err)})`, errnoWayOn(code), code);
320
+ }
321
+ if (expected === null)
322
+ return;
323
+ let bytes;
324
+ let after;
325
+ try {
326
+ // No link followed, and no wait on a pipe, if either was put at the path since the lstat.
327
+ const fd = openSync(staged.path, fsConstants.O_RDONLY | NOFOLLOW | NONBLOCK);
328
+ try {
329
+ // One byte past what the edit read, enough to see the file differs:
330
+ // a file another writer made larger, or a pipe a writer keeps
331
+ // filling, put at the path since, costs no more than that.
332
+ bytes = readBounded(fd, expected.bytes.length + 1);
333
+ }
334
+ finally {
335
+ closeSync(fd);
336
+ }
337
+ // And the entry is still the file the edit read, after its bytes were read.
338
+ after = lstatSync(staged.path, { bigint: true });
339
+ }
340
+ catch (err) {
341
+ // ELOOP: a link put at the path since the lstat; EAGAIN: a pipe, with a
342
+ // writer and nothing in it yet. Either is another writer's change.
343
+ if (errno(err) === 'ENOENT' || errno(err) === 'ELOOP' || errno(err) === 'EAGAIN')
344
+ throw new FileChanged(staged.path);
345
+ throw new WriteRefused(`cannot check that it hadn't changed (${systemWords(errno(err), err)})`, errnoWayOn(errno(err)), errno(err));
346
+ }
347
+ if (!sameIdentity(identityOf(after), expected.identity) || !bytes.equals(expected.bytes))
348
+ throw new FileChanged(staged.path);
349
+ }
350
+ /** Up to `limit` bytes from the start of `fd`, fewer at its end. */
351
+ function readBounded(fd, limit) {
352
+ const buffer = Buffer.alloc(limit);
353
+ let filled = 0;
354
+ while (filled < limit) {
355
+ // From where the last read ended (a pipe has no positions to read at).
356
+ const got = readSync(fd, buffer, filled, limit - filled, null);
357
+ if (got === 0)
358
+ break;
359
+ filled += got;
360
+ }
361
+ return buffer.subarray(0, filled);
362
+ }
363
+ /**
364
+ * Rename the staged file over the file, synchronously (checkUnchanged says
365
+ * why), counting the other hard links it splits the path from just before.
366
+ * A failed rename leaves the file as it was; the caller removes the staged
367
+ * file (discard).
368
+ */
369
+ export function renameInto(staged) {
370
+ let otherLinks;
371
+ try {
372
+ directWriteTestSeam.beforeRename?.(staged.path);
373
+ // The staged file is still the one this call wrote, not another put at its name.
374
+ const temp = lstatSync(staged.temp, { bigint: true });
375
+ if (!temp.isFile() || !sameIdentity(identityOf(temp), staged.tempIdentity)) {
376
+ throw new WriteRefused('another writer replaced the staged copy of the new text before it could be renamed over the file, which is as it was', 'Calling again writes it afresh.');
377
+ }
378
+ otherLinks = otherLinksAt(staged.path);
379
+ renameSync(staged.temp, staged.path);
380
+ }
381
+ catch (err) {
382
+ if (err instanceof WriteRefused)
383
+ throw err;
384
+ throw new WriteRefused(`cannot rename the staged file over it (${systemWords(errno(err), err)})`, errnoWayOn(errno(err)), errno(err));
385
+ }
386
+ // Set once the rename has happened, so a count never stands for a split that didn't.
387
+ staged.otherLinks = otherLinks;
388
+ }
389
+ /** The other hard links of what the entry at `path` holds: 0 for none there, and when lstat fails, since the count only informs the answer. */
390
+ function otherLinksAt(path) {
391
+ try {
392
+ return lstatSync(path).nlink - 1;
393
+ }
394
+ catch {
395
+ return 0;
396
+ }
397
+ }
398
+ /**
399
+ * Remove a staged file that won't be renamed, best effort: only the file
400
+ * this call staged, by its whole identity, since whatever another writer
401
+ * put at its name since is theirs, as renameInto leaves it too. Checked and
402
+ * removed synchronously, as the rename is (checkUnchanged says why).
403
+ */
404
+ export async function discard(staged) {
405
+ removeIf(staged.temp, (there) => there.isFile() && sameIdentity(identityOf(there), staged.tempIdentity));
406
+ }
407
+ /** Unlink what's at `path` if `ours` says it's the file meant; best effort, with no turn of the event loop between the check and the unlink. */
408
+ function removeIf(path, ours) {
409
+ try {
410
+ if (ours(lstatSync(path, { bigint: true })))
411
+ unlinkSync(path);
412
+ }
413
+ catch {
414
+ // Gone already, or not ours to know: left, as the header says.
415
+ }
416
+ }
417
+ //# sourceMappingURL=direct-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direct-write.js","sourceRoot":"","sources":["../../../../src/modules/workspace/direct-write.ts"],"names":[],"mappings":"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"}
@@ -10,14 +10,14 @@
10
10
  * intent — tombstones, store origin, conflicts — lives in BranchIntents and
11
11
  * rewinds with the tree.
12
12
  *
13
- * P is evidence about one directory, so each mount also records the identity
14
- * (device and inode) of the root that evidence was gathered under. A root
15
- * that isn't that directory any more (an unmounted drive whose mountpoint is
16
- * now empty, a replaced directory) is an unavailable mount, never a disk on
17
- * which every file was deleted; only an explicit acceptance takes it, and
18
- * that starts its evidence afresh. A place, where a walk stopped (`walks`),
19
- * binds no root: it only says where the next walk starts, in whatever
20
- * directory the root is by then.
13
+ * P is evidence about one directory, so each mount also records the identity of
14
+ * the root that evidence was gathered under (sameRootIdentity says when two
15
+ * name the same directory). A root that isn't that directory any more (an
16
+ * unmounted drive whose mountpoint is now empty, a replaced directory) is an
17
+ * unavailable mount, never a disk on which every file was deleted; only an
18
+ * explicit acceptance takes it, and that starts its evidence afresh. A place,
19
+ * where a walk stopped (`walks`), binds no root: it only says where the next
20
+ * walk starts, in whatever directory the root is by then.
21
21
  *
22
22
  * Every transition is write-ahead (see reconcile.ts): an `intent` entry
23
23
  * (`pending`, with the prior value and the expected one) is durable before the
@@ -32,7 +32,14 @@
32
32
  * agree); one that shows on disk is completed only by a push whose barriers
33
33
  * succeed, since its completion releases a draft's protection.
34
34
  */
35
+ import { AsyncLocalStorage } from 'node:async_hooks';
35
36
  import type { JsStore } from '@animalabs/chronicle';
37
+ import { Refusal } from './answers.js';
38
+ export declare class EvidenceUnavailable extends Refusal {
39
+ }
40
+ export declare const callGate: AsyncLocalStorage<{
41
+ passed: boolean;
42
+ }>;
36
43
  /** Everything `stat` says about a file that can vouch for unchanged bytes. */
37
44
  export interface Fingerprint {
38
45
  size: number;
@@ -98,11 +105,23 @@ export interface Interrupted {
98
105
  candidates: Candidate[];
99
106
  }
100
107
  export type Physical = Agreed | Pending | Interrupted;
101
- /** Which directory a mount root is: its device and inode, as decimal strings. */
108
+ /**
109
+ * Which directory a mount root is: its device and inode, and its birth time
110
+ * in nanoseconds as the runtime reports it, where that is above zero, as decimal
111
+ * strings.
112
+ */
102
113
  export interface RootIdentity {
103
114
  dev: string;
104
115
  ino: string;
116
+ born?: string;
105
117
  }
118
+ /**
119
+ * Whether two identities name the same directory: the same device and inode,
120
+ * or the same inode and birth time on another device, since macOS can give a
121
+ * volume another device number after a reboot. A birth time in whole seconds
122
+ * doesn't count across devices: filesystems made in the same second can give
123
+ * their roots one inode (2 on ext4) and one birth time.
124
+ */
106
125
  export declare function sameRootIdentity(a: RootIdentity, b: RootIdentity): boolean;
107
126
  export declare const DISK_AGREEMENT_RECORD_TYPE = "workspace/disk-agreement";
108
127
  export declare function candidateOf(value: Agreed): Candidate;
@@ -151,6 +170,7 @@ export declare class DiskAgreement {
151
170
  * open's rebuild threw: there is no whole evidence to read.
152
171
  */
153
172
  ensureReconciled(): void;
173
+ get awaitsReopen(): boolean;
154
174
  /** Whether evidence recorded so far still awaits a barrier. */
155
175
  get needsBarrier(): boolean;
156
176
  /**
@@ -179,8 +199,10 @@ export declare class DiskAgreement {
179
199
  rootIdentityForRead(mount: string): RootIdentity | undefined;
180
200
  /**
181
201
  * Record the root's identity just before the first evidence a mount gains:
182
- * the caller saw it as the root while gathering that evidence. A no-op once
183
- * one is recorded; changing it is acceptRoot's alone.
202
+ * the caller saw it as the root while gathering that evidence. Once one is
203
+ * recorded, the same directory seen on another device, or with a birth
204
+ * time the record lacks, replaces it and keeps the evidence; another
205
+ * directory is acceptRoot's alone.
184
206
  */
185
207
  recordRootIdentity(mount: string, identity: RootIdentity): void;
186
208
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"disk-agreement.d.ts","sourceRoot":"","sources":["../../../../src/modules/workspace/disk-agreement.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"disk-agreement.d.ts","sourceRoot":"","sources":["../../../../src/modules/workspace/disk-agreement.ts"],"names":[],"mappings":"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"}
@@ -10,14 +10,14 @@
10
10
  * intent — tombstones, store origin, conflicts — lives in BranchIntents and
11
11
  * rewinds with the tree.
12
12
  *
13
- * P is evidence about one directory, so each mount also records the identity
14
- * (device and inode) of the root that evidence was gathered under. A root
15
- * that isn't that directory any more (an unmounted drive whose mountpoint is
16
- * now empty, a replaced directory) is an unavailable mount, never a disk on
17
- * which every file was deleted; only an explicit acceptance takes it, and
18
- * that starts its evidence afresh. A place, where a walk stopped (`walks`),
19
- * binds no root: it only says where the next walk starts, in whatever
20
- * directory the root is by then.
13
+ * P is evidence about one directory, so each mount also records the identity of
14
+ * the root that evidence was gathered under (sameRootIdentity says when two
15
+ * name the same directory). A root that isn't that directory any more (an
16
+ * unmounted drive whose mountpoint is now empty, a replaced directory) is an
17
+ * unavailable mount, never a disk on which every file was deleted; only an
18
+ * explicit acceptance takes it, and that starts its evidence afresh. A place,
19
+ * where a walk stopped (`walks`), binds no root: it only says where the next
20
+ * walk starts, in whatever directory the root is by then.
21
21
  *
22
22
  * Every transition is write-ahead (see reconcile.ts): an `intent` entry
23
23
  * (`pending`, with the prior value and the expected one) is durable before the
@@ -32,15 +32,33 @@
32
32
  * agree); one that shows on disk is completed only by a push whose barriers
33
33
  * succeed, since its completion releases a draft's protection.
34
34
  */
35
+ import { AsyncLocalStorage } from 'node:async_hooks';
35
36
  import { resolve } from 'node:path';
36
37
  import { RecordJournal } from '../../record-journal.js';
38
+ import { Refusal, onlyTheHost } from './answers.js';
39
+ const UNTIL_REOPENED = "the workspace can't check its files against disk until its store is reopened";
40
+ const REOPEN = onlyTheHost('reopen it with a restart');
41
+ export class EvidenceUnavailable extends Refusal {
42
+ }
43
+ export const callGate = new AsyncLocalStorage();
37
44
  /** Whether two configured roots name the same path, whatever their spelling
38
45
  * (a trailing slash, a relative form). A symlinked alias is another path. */
39
46
  function sameRootPath(a, b) {
40
47
  return resolve(a) === resolve(b);
41
48
  }
49
+ /**
50
+ * Whether two identities name the same directory: the same device and inode,
51
+ * or the same inode and birth time on another device, since macOS can give a
52
+ * volume another device number after a reboot. A birth time in whole seconds
53
+ * doesn't count across devices: filesystems made in the same second can give
54
+ * their roots one inode (2 on ext4) and one birth time.
55
+ */
42
56
  export function sameRootIdentity(a, b) {
43
- return a.dev === b.dev && a.ino === b.ino;
57
+ if (a.ino !== b.ino)
58
+ return false;
59
+ if (a.dev === b.dev)
60
+ return true;
61
+ return a.born !== undefined && a.born === b.born && !a.born.endsWith('000000000');
44
62
  }
45
63
  export const DISK_AGREEMENT_RECORD_TYPE = 'workspace/disk-agreement';
46
64
  /** Checkpoint once the tail outgrows this many entries, or the live paths, whichever is larger. */
@@ -113,15 +131,19 @@ export class DiskAgreement {
113
131
  */
114
132
  ensureReconciled() {
115
133
  if (this.journal.needsReopen) {
116
- throw new Error('Disk-agreement evidence is unavailable until the store is reopened, as at the next start: ' +
117
- 'an append to its journal failed inside the store, and the evidence may lack what it recorded');
134
+ throw new EvidenceUnavailable(`${UNTIL_REOPENED}: an append to its record of what disk last agreed with failed inside the store, so that record may lack what the append held. ${REOPEN}`);
118
135
  }
119
136
  if (!this.whole) {
120
- throw new Error('Disk-agreement evidence is unavailable until the store is reopened, as at the next start: ' +
121
- 'reading it back from the store failed');
137
+ throw new EvidenceUnavailable(`${UNTIL_REOPENED}: reading back its record of what disk last agreed with failed. ${REOPEN}`);
122
138
  }
123
139
  if (this.journal.needsReconcile)
124
140
  this.rebuild();
141
+ const gate = callGate.getStore();
142
+ if (gate)
143
+ gate.passed = true;
144
+ }
145
+ get awaitsReopen() {
146
+ return this.journal.needsReopen || !this.whole;
125
147
  }
126
148
  /** Whether evidence recorded so far still awaits a barrier. */
127
149
  get needsBarrier() {
@@ -204,12 +226,21 @@ export class DiskAgreement {
204
226
  }
205
227
  /**
206
228
  * Record the root's identity just before the first evidence a mount gains:
207
- * the caller saw it as the root while gathering that evidence. A no-op once
208
- * one is recorded; changing it is acceptRoot's alone.
229
+ * the caller saw it as the root while gathering that evidence. Once one is
230
+ * recorded, the same directory seen on another device, or with a birth
231
+ * time the record lacks, replaces it and keeps the evidence; another
232
+ * directory is acceptRoot's alone.
209
233
  */
210
234
  recordRootIdentity(mount, identity) {
211
- if (this.rootIdentity(mount) !== undefined)
212
- return;
235
+ const recorded = this.rootIdentity(mount);
236
+ if (recorded !== undefined) {
237
+ if (!sameRootIdentity(recorded, identity))
238
+ return;
239
+ const renumbered = recorded.dev !== identity.dev;
240
+ const bornLearned = recorded.born === undefined && identity.born !== undefined;
241
+ if (!renumbered && !bornLearned)
242
+ return;
243
+ }
213
244
  this.write({ t: 'root', mount, identity }, {});
214
245
  }
215
246
  /**