@animalabs/agent-framework 0.21.0 → 0.22.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 (271) hide show
  1. package/README.md +43 -0
  2. package/dist/src/agent.d.ts +17 -4
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +102 -42
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/code-execution/py-runner.d.ts +22 -1
  7. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  8. package/dist/src/code-execution/py-runner.js +64 -9
  9. package/dist/src/code-execution/py-runner.js.map +1 -1
  10. package/dist/src/code-execution/runtime-py.d.ts +4 -0
  11. package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
  12. package/dist/src/code-execution/runtime-py.js +215 -38
  13. package/dist/src/code-execution/runtime-py.js.map +1 -1
  14. package/dist/src/framework.d.ts +250 -39
  15. package/dist/src/framework.d.ts.map +1 -1
  16. package/dist/src/framework.js +1602 -371
  17. package/dist/src/framework.js.map +1 -1
  18. package/dist/src/gate/event-gate.d.ts +21 -0
  19. package/dist/src/gate/event-gate.d.ts.map +1 -1
  20. package/dist/src/gate/event-gate.js +40 -7
  21. package/dist/src/gate/event-gate.js.map +1 -1
  22. package/dist/src/index.d.ts +5 -0
  23. package/dist/src/index.d.ts.map +1 -1
  24. package/dist/src/index.js +4 -0
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  27. package/dist/src/mcpl/channel-registry.js +26 -3
  28. package/dist/src/mcpl/channel-registry.js.map +1 -1
  29. package/dist/src/mcpl/conversation-router.d.ts +28 -1
  30. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  31. package/dist/src/mcpl/conversation-router.js +17 -0
  32. package/dist/src/mcpl/conversation-router.js.map +1 -1
  33. package/dist/src/mcpl/feature-set-manager.d.ts +11 -0
  34. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
  35. package/dist/src/mcpl/feature-set-manager.js +31 -7
  36. package/dist/src/mcpl/feature-set-manager.js.map +1 -1
  37. package/dist/src/mcpl/index.d.ts +2 -0
  38. package/dist/src/mcpl/index.d.ts.map +1 -1
  39. package/dist/src/mcpl/index.js +1 -0
  40. package/dist/src/mcpl/index.js.map +1 -1
  41. package/dist/src/mcpl/prose-stream-router.d.ts +27 -6
  42. package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
  43. package/dist/src/mcpl/prose-stream-router.js +85 -22
  44. package/dist/src/mcpl/prose-stream-router.js.map +1 -1
  45. package/dist/src/mcpl/push-coalescer.d.ts +32 -4
  46. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  47. package/dist/src/mcpl/push-coalescer.js +46 -13
  48. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  49. package/dist/src/mcpl/push-handler.d.ts +5 -3
  50. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  51. package/dist/src/mcpl/push-handler.js +41 -11
  52. package/dist/src/mcpl/push-handler.js.map +1 -1
  53. package/dist/src/mcpl/server-connection.d.ts +4 -0
  54. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  55. package/dist/src/mcpl/server-connection.js +45 -8
  56. package/dist/src/mcpl/server-connection.js.map +1 -1
  57. package/dist/src/mcpl/server-registry.d.ts +5 -2
  58. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  59. package/dist/src/mcpl/server-registry.js +25 -7
  60. package/dist/src/mcpl/server-registry.js.map +1 -1
  61. package/dist/src/mcpl/tool-lifecycle.d.ts +52 -2
  62. package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
  63. package/dist/src/mcpl/tool-lifecycle.js +47 -1
  64. package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
  65. package/dist/src/mcpl/tool-pattern-check.d.ts +51 -0
  66. package/dist/src/mcpl/tool-pattern-check.d.ts.map +1 -0
  67. package/dist/src/mcpl/tool-pattern-check.js +79 -0
  68. package/dist/src/mcpl/tool-pattern-check.js.map +1 -0
  69. package/dist/src/mcpl/types.d.ts +15 -5
  70. package/dist/src/mcpl/types.d.ts.map +1 -1
  71. package/dist/src/mcpl/types.js.map +1 -1
  72. package/dist/src/mcpl/visible-content.d.ts +39 -0
  73. package/dist/src/mcpl/visible-content.d.ts.map +1 -0
  74. package/dist/src/mcpl/visible-content.js +54 -0
  75. package/dist/src/mcpl/visible-content.js.map +1 -0
  76. package/dist/src/module-registry.d.ts +81 -3
  77. package/dist/src/module-registry.d.ts.map +1 -1
  78. package/dist/src/module-registry.js +257 -16
  79. package/dist/src/module-registry.js.map +1 -1
  80. package/dist/src/modules/workspace/branch-intent.d.ts +72 -0
  81. package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -0
  82. package/dist/src/modules/workspace/branch-intent.js +83 -0
  83. package/dist/src/modules/workspace/branch-intent.js.map +1 -0
  84. package/dist/src/modules/workspace/disk-agreement.d.ts +167 -0
  85. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -0
  86. package/dist/src/modules/workspace/disk-agreement.js +259 -0
  87. package/dist/src/modules/workspace/disk-agreement.js.map +1 -0
  88. package/dist/src/modules/workspace/effects.d.ts +84 -0
  89. package/dist/src/modules/workspace/effects.d.ts.map +1 -0
  90. package/dist/src/modules/workspace/effects.js +368 -0
  91. package/dist/src/modules/workspace/effects.js.map +1 -0
  92. package/dist/src/modules/workspace/index.d.ts +155 -17
  93. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  94. package/dist/src/modules/workspace/index.js +1059 -424
  95. package/dist/src/modules/workspace/index.js.map +1 -1
  96. package/dist/src/modules/workspace/observe.d.ts +278 -0
  97. package/dist/src/modules/workspace/observe.d.ts.map +1 -0
  98. package/dist/src/modules/workspace/observe.js +805 -0
  99. package/dist/src/modules/workspace/observe.js.map +1 -0
  100. package/dist/src/modules/workspace/reconcile.d.ts +224 -0
  101. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -0
  102. package/dist/src/modules/workspace/reconcile.js +901 -0
  103. package/dist/src/modules/workspace/reconcile.js.map +1 -0
  104. package/dist/src/modules/workspace/sync.d.ts +6 -59
  105. package/dist/src/modules/workspace/sync.d.ts.map +1 -1
  106. package/dist/src/modules/workspace/sync.js +6 -257
  107. package/dist/src/modules/workspace/sync.js.map +1 -1
  108. package/dist/src/modules/workspace/types.d.ts +77 -10
  109. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  110. package/dist/src/modules/workspace/types.js.map +1 -1
  111. package/dist/src/record-journal.d.ts +127 -0
  112. package/dist/src/record-journal.d.ts.map +1 -0
  113. package/dist/src/record-journal.js +202 -0
  114. package/dist/src/record-journal.js.map +1 -0
  115. package/dist/src/shutdown.d.ts +13 -0
  116. package/dist/src/shutdown.d.ts.map +1 -0
  117. package/dist/src/shutdown.js +46 -0
  118. package/dist/src/shutdown.js.map +1 -0
  119. package/dist/src/silent-heartbeat.d.ts +74 -0
  120. package/dist/src/silent-heartbeat.d.ts.map +1 -0
  121. package/dist/src/silent-heartbeat.js +77 -0
  122. package/dist/src/silent-heartbeat.js.map +1 -0
  123. package/dist/src/tool-presentation.d.ts +50 -0
  124. package/dist/src/tool-presentation.d.ts.map +1 -0
  125. package/dist/src/tool-presentation.js +235 -0
  126. package/dist/src/tool-presentation.js.map +1 -0
  127. package/dist/src/tool-result-guard.d.ts +125 -6
  128. package/dist/src/tool-result-guard.d.ts.map +1 -1
  129. package/dist/src/tool-result-guard.js +310 -35
  130. package/dist/src/tool-result-guard.js.map +1 -1
  131. package/dist/src/tool-result-history.d.ts +13 -0
  132. package/dist/src/tool-result-history.d.ts.map +1 -1
  133. package/dist/src/tool-result-history.js +21 -1
  134. package/dist/src/tool-result-history.js.map +1 -1
  135. package/dist/src/types/agent.d.ts +18 -2
  136. package/dist/src/types/agent.d.ts.map +1 -1
  137. package/dist/src/types/events.d.ts +4 -0
  138. package/dist/src/types/events.d.ts.map +1 -1
  139. package/dist/src/types/framework.d.ts +12 -0
  140. package/dist/src/types/framework.d.ts.map +1 -1
  141. package/dist/src/types/module.d.ts +14 -0
  142. package/dist/src/types/module.d.ts.map +1 -1
  143. package/dist/src/types/trace.d.ts +73 -0
  144. package/dist/src/types/trace.d.ts.map +1 -1
  145. package/dist/test/batched-wake-locus.test.d.ts +2 -0
  146. package/dist/test/batched-wake-locus.test.d.ts.map +1 -0
  147. package/dist/test/batched-wake-locus.test.js +166 -0
  148. package/dist/test/batched-wake-locus.test.js.map +1 -0
  149. package/dist/test/code-execution.test.js +386 -2
  150. package/dist/test/code-execution.test.js.map +1 -1
  151. package/dist/test/component-defaults.test.d.ts +2 -0
  152. package/dist/test/component-defaults.test.d.ts.map +1 -0
  153. package/dist/test/component-defaults.test.js +54 -0
  154. package/dist/test/component-defaults.test.js.map +1 -0
  155. package/dist/test/conversation-routing.test.js +57 -1
  156. package/dist/test/conversation-routing.test.js.map +1 -1
  157. package/dist/test/ephemeral-settle.test.js +3 -0
  158. package/dist/test/ephemeral-settle.test.js.map +1 -1
  159. package/dist/test/event-gate.test.js +118 -0
  160. package/dist/test/event-gate.test.js.map +1 -1
  161. package/dist/test/explicit-prose-routing.test.js +977 -0
  162. package/dist/test/explicit-prose-routing.test.js.map +1 -1
  163. package/dist/test/helpers/pytc-stop-rows.d.ts +11 -0
  164. package/dist/test/helpers/pytc-stop-rows.d.ts.map +1 -0
  165. package/dist/test/helpers/pytc-stop-rows.js +143 -0
  166. package/dist/test/helpers/pytc-stop-rows.js.map +1 -0
  167. package/dist/test/mcpl-coalescing.test.js +19 -6
  168. package/dist/test/mcpl-coalescing.test.js.map +1 -1
  169. package/dist/test/mcpl-config-diagnostics.test.d.ts +2 -0
  170. package/dist/test/mcpl-config-diagnostics.test.d.ts.map +1 -0
  171. package/dist/test/mcpl-config-diagnostics.test.js +449 -0
  172. package/dist/test/mcpl-config-diagnostics.test.js.map +1 -0
  173. package/dist/test/mcpl-empty-content.test.d.ts +2 -0
  174. package/dist/test/mcpl-empty-content.test.d.ts.map +1 -0
  175. package/dist/test/mcpl-empty-content.test.js +368 -0
  176. package/dist/test/mcpl-empty-content.test.js.map +1 -0
  177. package/dist/test/mcpl-tool-lifecycle-emitter.test.js +106 -1
  178. package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +1 -1
  179. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +2 -0
  180. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +1 -0
  181. package/dist/test/mcpl-tool-lifecycle-script.test.js +288 -0
  182. package/dist/test/mcpl-tool-lifecycle-script.test.js.map +1 -0
  183. package/dist/test/prose-stream-router.test.js +41 -0
  184. package/dist/test/prose-stream-router.test.js.map +1 -1
  185. package/dist/test/record-journal.test.d.ts +6 -0
  186. package/dist/test/record-journal.test.d.ts.map +1 -0
  187. package/dist/test/record-journal.test.js +269 -0
  188. package/dist/test/record-journal.test.js.map +1 -0
  189. package/dist/test/save-recent-image.test.js +102 -0
  190. package/dist/test/save-recent-image.test.js.map +1 -1
  191. package/dist/test/shutdown-errors.test.d.ts +2 -0
  192. package/dist/test/shutdown-errors.test.d.ts.map +1 -0
  193. package/dist/test/shutdown-errors.test.js +662 -0
  194. package/dist/test/shutdown-errors.test.js.map +1 -0
  195. package/dist/test/silent-heartbeat-rows.test.d.ts +2 -0
  196. package/dist/test/silent-heartbeat-rows.test.d.ts.map +1 -0
  197. package/dist/test/silent-heartbeat-rows.test.js +451 -0
  198. package/dist/test/silent-heartbeat-rows.test.js.map +1 -0
  199. package/dist/test/silent-heartbeat.test.js +49 -38
  200. package/dist/test/silent-heartbeat.test.js.map +1 -1
  201. package/dist/test/stop-with-work-in-flight.test.d.ts +23 -0
  202. package/dist/test/stop-with-work-in-flight.test.d.ts.map +1 -0
  203. package/dist/test/stop-with-work-in-flight.test.js +672 -0
  204. package/dist/test/stop-with-work-in-flight.test.js.map +1 -0
  205. package/dist/test/tool-batch-hook.test.d.ts +8 -0
  206. package/dist/test/tool-batch-hook.test.d.ts.map +1 -0
  207. package/dist/test/tool-batch-hook.test.js +275 -0
  208. package/dist/test/tool-batch-hook.test.js.map +1 -0
  209. package/dist/test/tool-presentation.test.d.ts +2 -0
  210. package/dist/test/tool-presentation.test.d.ts.map +1 -0
  211. package/dist/test/tool-presentation.test.js +268 -0
  212. package/dist/test/tool-presentation.test.js.map +1 -0
  213. package/dist/test/tool-result-guard.test.js +2330 -18
  214. package/dist/test/tool-result-guard.test.js.map +1 -1
  215. package/dist/test/tool-result-history.test.js +2 -1
  216. package/dist/test/tool-result-history.test.js.map +1 -1
  217. package/dist/test/turn-trigger-provenance.test.js +35 -0
  218. package/dist/test/turn-trigger-provenance.test.js.map +1 -1
  219. package/dist/test/unconvertible-tool-result.test.d.ts +2 -0
  220. package/dist/test/unconvertible-tool-result.test.d.ts.map +1 -0
  221. package/dist/test/unconvertible-tool-result.test.js +358 -0
  222. package/dist/test/unconvertible-tool-result.test.js.map +1 -0
  223. package/dist/test/workspace-materialize-freshness.test.d.ts +16 -0
  224. package/dist/test/workspace-materialize-freshness.test.d.ts.map +1 -0
  225. package/dist/test/workspace-materialize-freshness.test.js +252 -0
  226. package/dist/test/workspace-materialize-freshness.test.js.map +1 -0
  227. package/dist/test/workspace-three-way.test.d.ts +12 -0
  228. package/dist/test/workspace-three-way.test.d.ts.map +1 -0
  229. package/dist/test/workspace-three-way.test.js +3079 -0
  230. package/dist/test/workspace-three-way.test.js.map +1 -0
  231. package/dist/tsconfig.tsbuildinfo +1 -1
  232. package/package.json +3 -3
  233. package/src/agent.ts +120 -44
  234. package/src/code-execution/py-runner.ts +66 -9
  235. package/src/code-execution/runtime-py.ts +215 -38
  236. package/src/framework.ts +1661 -363
  237. package/src/gate/event-gate.ts +59 -7
  238. package/src/index.ts +11 -0
  239. package/src/mcpl/channel-registry.ts +24 -3
  240. package/src/mcpl/conversation-router.ts +34 -1
  241. package/src/mcpl/feature-set-manager.ts +36 -11
  242. package/src/mcpl/index.ts +2 -0
  243. package/src/mcpl/prose-stream-router.ts +79 -23
  244. package/src/mcpl/push-coalescer.ts +62 -13
  245. package/src/mcpl/push-handler.ts +41 -12
  246. package/src/mcpl/server-connection.ts +41 -10
  247. package/src/mcpl/server-registry.ts +24 -7
  248. package/src/mcpl/tool-lifecycle.ts +77 -2
  249. package/src/mcpl/tool-pattern-check.ts +106 -0
  250. package/src/mcpl/types.ts +15 -5
  251. package/src/mcpl/visible-content.ts +57 -0
  252. package/src/module-registry.ts +252 -16
  253. package/src/modules/workspace/branch-intent.ts +123 -0
  254. package/src/modules/workspace/disk-agreement.ts +325 -0
  255. package/src/modules/workspace/effects.ts +362 -0
  256. package/src/modules/workspace/index.ts +1058 -446
  257. package/src/modules/workspace/observe.ts +839 -0
  258. package/src/modules/workspace/reconcile.ts +1136 -0
  259. package/src/modules/workspace/sync.ts +6 -336
  260. package/src/modules/workspace/types.ts +75 -10
  261. package/src/record-journal.ts +246 -0
  262. package/src/shutdown.ts +49 -0
  263. package/src/silent-heartbeat.ts +141 -0
  264. package/src/tool-presentation.ts +188 -0
  265. package/src/tool-result-guard.ts +335 -33
  266. package/src/tool-result-history.ts +25 -1
  267. package/src/types/agent.ts +18 -2
  268. package/src/types/events.ts +4 -0
  269. package/src/types/framework.ts +12 -0
  270. package/src/types/module.ts +15 -0
  271. package/src/types/trace.ts +80 -0
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Physical disk-agreement evidence (P): for each mount and path, what disk held
3
+ * when it last agreed with the workspace store, or what the module last wrote
4
+ * there or adopted from it.
5
+ *
6
+ * P describes disk, and disk does not rewind with a branch switch, so P lives
7
+ * in global chronicle records (RecordJournal) rather than branch state: after
8
+ * an undo selects an older tree, P still says what disk holds, and a
9
+ * difference reads as a store-side change, never as a disk edit. Branch-local
10
+ * intent — tombstones, store origin, conflicts — lives in BranchIntents and
11
+ * rewinds with the tree.
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.
19
+ *
20
+ * Every transition is write-ahead (see reconcile.ts): an `intent` entry
21
+ * (`pending`, with the prior value and the expected one) is durable before the
22
+ * disk effect or tree adoption it announces, and the completion `set` is
23
+ * appended only after that effect is durable. One tree adoption has no
24
+ * intent: a drop, which removes the store's copy of a disk version the
25
+ * workspace can't hold, forgets P first, so a crash before the removal leaves
26
+ * P unknown, never a wrong adoption. A pending adoption resolves at its next
27
+ * observation, by the tree. A pending disk effect resolves there only when
28
+ * disk shows it didn't happen (the prior) or can't tell (`interrupted`, a
29
+ * sticky conflict resolved explicitly, or by disk and the store coming to
30
+ * agree); one that shows on disk is completed only by a push whose barriers
31
+ * succeed, since its completion releases a draft's protection.
32
+ */
33
+ import { resolve } from 'node:path';
34
+ import { RecordJournal } from '../../record-journal.js';
35
+ /** Whether two configured roots name the same path, whatever their spelling
36
+ * (a trailing slash, a relative form). A symlinked alias is another path. */
37
+ function sameRootPath(a, b) {
38
+ return resolve(a) === resolve(b);
39
+ }
40
+ export function sameRootIdentity(a, b) {
41
+ return a.dev === b.dev && a.ino === b.ino;
42
+ }
43
+ export const DISK_AGREEMENT_RECORD_TYPE = 'workspace/disk-agreement';
44
+ /** Checkpoint once the tail outgrows this many entries, or the live paths, whichever is larger. */
45
+ const MIN_CHECKPOINT_TAIL = 1000;
46
+ export function candidateOf(value) {
47
+ return value.kind === 'absent' ? { kind: 'absent' } : { kind: 'content', hash: value.hash };
48
+ }
49
+ export function sameCandidate(a, b) {
50
+ return a.kind === b.kind && (a.kind === 'absent' || a.hash === b.hash);
51
+ }
52
+ export class DiskAgreement {
53
+ store;
54
+ journal;
55
+ mounts = new Map();
56
+ configured = [];
57
+ /**
58
+ * An append (or a rebuild) not yet made durable. Cleared only by a
59
+ * barrier that succeeds, so a failed barrier stays owed: a later pass with
60
+ * nothing new to record still settles it before deciding anything.
61
+ */
62
+ unsynced = false;
63
+ constructor(store) {
64
+ this.store = store;
65
+ this.journal = new RecordJournal(store, { type: DISK_AGREEMENT_RECORD_TYPE });
66
+ }
67
+ /**
68
+ * Rebuild P from the journal for the configured mounts. A mount whose
69
+ * recorded root differs from its configured one starts empty: evidence
70
+ * about another directory says nothing about this one.
71
+ */
72
+ open(mounts) {
73
+ this.configured = mounts;
74
+ this.rebuild();
75
+ }
76
+ /**
77
+ * Re-read the journal when an earlier write left it unreconciled, and throw
78
+ * once an append failed inside the store. P may then lack what that append
79
+ * recorded (the store may hold it where this process can't read it back),
80
+ * and a decision from stale P can record what reopening contradicts, such
81
+ * as a conflict that stays after the right evidence is back. So until the
82
+ * store is reopened, no evidence is read, written or checkpointed, and
83
+ * every pass and tool call that needs it fails.
84
+ */
85
+ ensureReconciled() {
86
+ if (this.journal.needsReopen) {
87
+ throw new Error('Disk-agreement evidence is unavailable until the store is reopened, as at the next start: ' +
88
+ 'an append to its journal failed inside the store, and the evidence may lack what it recorded');
89
+ }
90
+ if (this.journal.needsReconcile)
91
+ this.rebuild();
92
+ }
93
+ /** Whether evidence recorded so far still awaits a barrier. */
94
+ get needsBarrier() {
95
+ return this.unsynced;
96
+ }
97
+ /**
98
+ * Replay the journal into memory. Records that read back are not proven
99
+ * durable — a previous owner may have appended them and died before any
100
+ * barrier — so evidence rebuilt here owes one, whether at open or after an
101
+ * ambiguous append.
102
+ */
103
+ rebuild() {
104
+ this.unsynced = true;
105
+ const { snapshot, entries } = this.journal.load();
106
+ const replayed = new Map();
107
+ for (const [name, mount] of Object.entries(snapshot?.mounts ?? {})) {
108
+ replayed.set(name, {
109
+ root: mount.root,
110
+ ...(mount.identity ? { identity: mount.identity } : {}),
111
+ paths: new Map(Object.entries(mount.paths)),
112
+ });
113
+ }
114
+ for (const { entry } of entries) {
115
+ if (entry.t === 'mount') {
116
+ const existing = replayed.get(entry.mount);
117
+ if (!existing || !sameRootPath(existing.root, entry.root))
118
+ replayed.set(entry.mount, { root: entry.root, paths: new Map() });
119
+ continue;
120
+ }
121
+ const mount = replayed.get(entry.mount);
122
+ if (!mount)
123
+ continue;
124
+ if (entry.t === 'root')
125
+ applyRoot(mount, entry);
126
+ else if (entry.t === 'set')
127
+ mount.paths.set(entry.path, entry.value);
128
+ else
129
+ mount.paths.delete(entry.path);
130
+ }
131
+ this.mounts.clear();
132
+ for (const { name, root } of this.configured) {
133
+ const found = replayed.get(name);
134
+ if (found && sameRootPath(found.root, root)) {
135
+ this.mounts.set(name, found);
136
+ }
137
+ else {
138
+ this.mounts.set(name, { root, paths: new Map() });
139
+ this.journal.append({ t: 'mount', mount: name, root });
140
+ }
141
+ }
142
+ }
143
+ /** The identity of the root this mount's evidence was gathered under, once any was. */
144
+ rootIdentity(mount) {
145
+ this.ensureReconciled();
146
+ return this.mounts.get(mount)?.identity;
147
+ }
148
+ /**
149
+ * Record the root's identity just before the first evidence a mount gains:
150
+ * the caller saw it as the root while gathering that evidence. A no-op once
151
+ * one is recorded; changing it is acceptRoot's alone.
152
+ */
153
+ recordRootIdentity(mount, identity) {
154
+ if (this.rootIdentity(mount) !== undefined)
155
+ return;
156
+ this.write({ t: 'root', mount, identity }, {});
157
+ }
158
+ /**
159
+ * Take the root as it is now, at the operator's word: record its identity
160
+ * and forget every path's evidence, which described another directory.
161
+ */
162
+ acceptRoot(mount, identity) {
163
+ this.write({ t: 'root', mount, identity, reset: true }, {});
164
+ }
165
+ get(mount, path) {
166
+ this.ensureReconciled(); // an ambiguous append may have landed: read it back first
167
+ return this.mounts.get(mount)?.paths.get(path);
168
+ }
169
+ /** Every path with evidence under a prefix ('' for the whole mount). */
170
+ paths(mount, prefix = '') {
171
+ this.ensureReconciled();
172
+ const paths = this.mounts.get(mount)?.paths;
173
+ if (!paths)
174
+ return [];
175
+ const out = [];
176
+ for (const [path, value] of paths) {
177
+ if (prefix === '' || path === prefix || path.startsWith(prefix + '/'))
178
+ out.push([path, value]);
179
+ }
180
+ return out;
181
+ }
182
+ /**
183
+ * Record an intent. `durable` makes it (and every append before it) stable
184
+ * before the caller performs the effect; batches pass it on their last one.
185
+ */
186
+ intend(mount, path, intent, opts = {}) {
187
+ this.write({ t: 'set', mount, path, value: { kind: 'pending', ...intent } }, { durable: opts.durable });
188
+ }
189
+ /**
190
+ * Record agreed evidence. `afterCommittedState` when it asserts a tree
191
+ * commit made just before (the commit is synced first).
192
+ */
193
+ set(mount, path, value, opts = {}) {
194
+ this.write({ t: 'set', mount, path, value }, { afterCommittedState: opts.afterCommittedState });
195
+ }
196
+ /** Forget a path: P becomes unknown, the most conservative evidence. */
197
+ forget(mount, path) {
198
+ if (this.get(mount, path) === undefined)
199
+ return;
200
+ this.write({ t: 'forget', mount, path }, {});
201
+ }
202
+ /**
203
+ * Put back exactly the evidence a path had before an intent whose effect
204
+ * never touched it — whatever kind it was (agreed, pending, interrupted, or
205
+ * unknown) — so the intent leaves no trace in what P says.
206
+ */
207
+ restore(mount, path, value) {
208
+ if (value === undefined)
209
+ this.forget(mount, path);
210
+ else
211
+ this.write({ t: 'set', mount, path, value }, {});
212
+ }
213
+ write(entry, opts) {
214
+ this.ensureReconciled();
215
+ this.unsynced = true; // until proven durable: a failed append may still have landed
216
+ this.journal.append(entry, opts);
217
+ if (opts.durable)
218
+ this.unsynced = false; // synced after this append, and so everything before it
219
+ const mount = this.mounts.get(entry.mount);
220
+ if (!mount)
221
+ return;
222
+ if (entry.t === 'root')
223
+ applyRoot(mount, entry);
224
+ else if (entry.t === 'set')
225
+ mount.paths.set(entry.path, entry.value);
226
+ else if (entry.t === 'forget')
227
+ mount.paths.delete(entry.path);
228
+ }
229
+ /** Checkpoint when the tail has outgrown the live evidence. */
230
+ maybeCheckpoint() {
231
+ // Like every entry point: an ambiguous write is read back first.
232
+ this.ensureReconciled();
233
+ let live = 0;
234
+ for (const mount of this.mounts.values())
235
+ live += mount.paths.size;
236
+ if (this.journal.entriesSinceCheckpoint < Math.max(MIN_CHECKPOINT_TAIL, live))
237
+ return;
238
+ const snapshot = { mounts: {} };
239
+ for (const [name, mount] of this.mounts) {
240
+ snapshot.mounts[name] = {
241
+ root: mount.root,
242
+ ...(mount.identity ? { identity: mount.identity } : {}),
243
+ paths: Object.fromEntries(mount.paths),
244
+ };
245
+ }
246
+ this.journal.checkpoint(snapshot);
247
+ }
248
+ /** Make every append so far stable. Throws, and stays owed, if the sync fails. */
249
+ barrier() {
250
+ this.store.sync();
251
+ this.unsynced = false;
252
+ }
253
+ }
254
+ function applyRoot(mount, entry) {
255
+ mount.identity = entry.identity;
256
+ if (entry.reset)
257
+ mount.paths.clear();
258
+ }
259
+ //# sourceMappingURL=disk-agreement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disk-agreement.js","sourceRoot":"","sources":["../../../../src/modules/workspace/disk-agreement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The workspace's disk effects — a materialize's writes and unlinks, and the
3
+ * barriers that make them durable — under the mount boundary.
4
+ *
5
+ * Each effect is bound at its point of use, not at planning time: the parent
6
+ * directory is checked (located inside the mount, see observe.ts), the file is
7
+ * reached through it, and before anything is changed the effect checks that
8
+ * it acts on what was decided on — a regular file inside the mount, at this
9
+ * path, still holding the bytes planning saw (unless `force`, which overrides
10
+ * that freshness but never the boundary). Content is written only through a
11
+ * descriptor whose own location was confirmed inside the mount first, and
12
+ * only into a file with no other hard links: a write in place changes the
13
+ * file under every name it has, and the others may lie outside the mount (a
14
+ * package store's, say), so such a file is refused. Unlinking one name
15
+ * changes no other, so an unlink needs no such check.
16
+ *
17
+ * What an effect reports matters to the caller's evidence: a failure that
18
+ * never touched the path's entry leaves disk as planning saw it, so the
19
+ * path's evidence can be put back as it was; once the entry was touched
20
+ * (created, truncated, written or unlinked) the outcome is for the next
21
+ * observation to resolve. Directories created for a write don't touch the
22
+ * path's entry; they stay, empty, if the write is refused.
23
+ *
24
+ * Node has no openat, so a create, mkdir or unlink still resolves its path by
25
+ * name after the parent was checked: a parent replaced concurrently in that
26
+ * window can redirect that one operation, leaving an empty file or directory
27
+ * outside the mount or unlinking a same-named file there. Nor is there an
28
+ * unlink bound to an inode: an unlink checks the entry's identity again just
29
+ * before it, and a file replaced at its path after that check is the one
30
+ * unlinked. Content writes stay bound to the descriptor verified before
31
+ * modification: inside the mount, with no other hard links (a link another
32
+ * process makes to it after that check shares the write). The hash, which
33
+ * reads the whole file, comes before the checks of where the file is, and
34
+ * the root is checked again beside the parent, since the parent is found by
35
+ * path beneath the root's. A last look at the file (its descriptor's stat
36
+ * before a write, its entry's lstat before an unlink) refuses one written
37
+ * since it was first seen, so only an edit made in the moment between that
38
+ * look and the effect is overwritten or removed.
39
+ */
40
+ import { type MountView } from './observe.js';
41
+ /** What a write or unlink may find at its path when it acts. */
42
+ export type Expect = {
43
+ kind: 'absent';
44
+ }
45
+ /** The bytes planning decided on, by hash. */
46
+ | {
47
+ kind: 'content';
48
+ hash: string;
49
+ }
50
+ /** Anything (force): freshness is overridden, the boundary is not. */
51
+ | {
52
+ kind: 'any';
53
+ };
54
+ export declare class EffectFailed extends Error {
55
+ /** Whether the path's entry was touched before the failure. */
56
+ readonly touched: boolean;
57
+ constructor(message: string, touched: boolean);
58
+ }
59
+ /**
60
+ * Write `bytes` at a mount-relative path, creating missing parent
61
+ * directories, and make the file durable (its directories are synced by the
62
+ * caller, after all of a push's effects: syncDirectories).
63
+ */
64
+ export declare function writeContained(view: MountView, rootReal: string, rel: string, bytes: Buffer, expect: Expect): Promise<void>;
65
+ /**
66
+ * Unlink a mount-relative file, or confirm it gone (`absent` expected: a
67
+ * file found there is never removed). Returns the mount directory whose
68
+ * entries make the absence durable, for the caller's barrier: the file's
69
+ * parent, or — when the parent itself is gone — the directory proven to lack
70
+ * it, since that removal may not be durable either.
71
+ */
72
+ export declare function unlinkContained(view: MountView, rootReal: string, rel: string, expect: Expect): Promise<string>;
73
+ /**
74
+ * Sync each mount directory (mount-relative), deepest first, so the entries
75
+ * a push created or removed in them are durable. Each is checked first and the
76
+ * directory it holds is the one synced — never a replacement at its path.
77
+ * Returns the ones that failed, with why. Where directories can't be synced
78
+ * at all (Windows, or a filesystem answering EINVAL/ENOTSUP), there is
79
+ * nothing to wait for.
80
+ */
81
+ export declare function syncDirectories(view: MountView, rootReal: string, rels: Iterable<string>): Promise<Map<string, string>>;
82
+ /** The directories whose entries make a written file reachable: its parent up to the mount root. */
83
+ export declare function chainOf(rel: string): string[];
84
+ //# sourceMappingURL=effects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effects.d.ts","sourceRoot":"","sources":["../../../../src/modules/workspace/effects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAMH,OAAO,EAEL,KAAK,SAAS,EAWf,MAAM,cAAc,CAAC;AAEtB,gEAAgE;AAChE,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE;AACpB,8CAA8C;GAC5C;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AACnC,sEAAsE;GACpE;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC;AAKpB,qBAAa,YAAa,SAAQ,KAAK;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;gBAEd,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;CAI9C;AAmFD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmFjI;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0DrH;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA0B7H;AAED,oGAAoG;AACpG,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAM7C"}