@animalabs/agent-framework 0.21.1 → 0.22.1
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.
- package/README.md +54 -4
- package/dist/src/agent.d.ts +17 -4
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +105 -43
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +41 -4
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/code-execution/py-runner.d.ts +22 -1
- package/dist/src/code-execution/py-runner.d.ts.map +1 -1
- package/dist/src/code-execution/py-runner.js +64 -9
- package/dist/src/code-execution/py-runner.js.map +1 -1
- package/dist/src/code-execution/runtime-py.d.ts +4 -0
- package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
- package/dist/src/code-execution/runtime-py.js +215 -38
- package/dist/src/code-execution/runtime-py.js.map +1 -1
- package/dist/src/framework.d.ts +569 -59
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +3091 -542
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +21 -0
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +40 -7
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/index.d.ts +7 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +26 -3
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts +28 -1
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +17 -0
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/feature-set-manager.d.ts +11 -0
- package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
- package/dist/src/mcpl/feature-set-manager.js +31 -7
- package/dist/src/mcpl/feature-set-manager.js.map +1 -1
- package/dist/src/mcpl/index.d.ts +4 -0
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js +1 -0
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/prose-stream-router.d.ts +27 -6
- package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
- package/dist/src/mcpl/prose-stream-router.js +85 -22
- package/dist/src/mcpl/prose-stream-router.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +32 -4
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +46 -13
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +5 -3
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +41 -11
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/reference-fetcher.d.ts +2 -1
- package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
- package/dist/src/mcpl/reference-fetcher.js +20 -9
- package/dist/src/mcpl/reference-fetcher.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +29 -6
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +57 -16
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +32 -1
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +76 -11
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/server-registry.d.ts +5 -2
- package/dist/src/mcpl/server-registry.d.ts.map +1 -1
- package/dist/src/mcpl/server-registry.js +25 -7
- package/dist/src/mcpl/server-registry.js.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.d.ts +52 -2
- package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.js +47 -1
- package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
- package/dist/src/mcpl/tool-pattern-check.d.ts +51 -0
- package/dist/src/mcpl/tool-pattern-check.d.ts.map +1 -0
- package/dist/src/mcpl/tool-pattern-check.js +79 -0
- package/dist/src/mcpl/tool-pattern-check.js.map +1 -0
- package/dist/src/mcpl/types.d.ts +15 -5
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/mcpl/visible-content.d.ts +39 -0
- package/dist/src/mcpl/visible-content.d.ts.map +1 -0
- package/dist/src/mcpl/visible-content.js +54 -0
- package/dist/src/mcpl/visible-content.js.map +1 -0
- package/dist/src/module-registry.d.ts +90 -6
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +260 -18
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/workspace/branch-intent.d.ts +72 -0
- package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -0
- package/dist/src/modules/workspace/branch-intent.js +83 -0
- package/dist/src/modules/workspace/branch-intent.js.map +1 -0
- package/dist/src/modules/workspace/disk-agreement.d.ts +167 -0
- package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -0
- package/dist/src/modules/workspace/disk-agreement.js +259 -0
- package/dist/src/modules/workspace/disk-agreement.js.map +1 -0
- package/dist/src/modules/workspace/effects.d.ts +84 -0
- package/dist/src/modules/workspace/effects.d.ts.map +1 -0
- package/dist/src/modules/workspace/effects.js +368 -0
- package/dist/src/modules/workspace/effects.js.map +1 -0
- package/dist/src/modules/workspace/index.d.ts +155 -17
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +1101 -424
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +288 -0
- package/dist/src/modules/workspace/observe.d.ts.map +1 -0
- package/dist/src/modules/workspace/observe.js +825 -0
- package/dist/src/modules/workspace/observe.js.map +1 -0
- package/dist/src/modules/workspace/reconcile.d.ts +238 -0
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -0
- package/dist/src/modules/workspace/reconcile.js +909 -0
- package/dist/src/modules/workspace/reconcile.js.map +1 -0
- package/dist/src/modules/workspace/sync.d.ts +6 -59
- package/dist/src/modules/workspace/sync.d.ts.map +1 -1
- package/dist/src/modules/workspace/sync.js +6 -257
- package/dist/src/modules/workspace/sync.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +77 -10
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/operator-log.d.ts +41 -0
- package/dist/src/operator-log.d.ts.map +1 -1
- package/dist/src/operator-log.js.map +1 -1
- package/dist/src/provider-waits.d.ts +216 -0
- package/dist/src/provider-waits.d.ts.map +1 -0
- package/dist/src/provider-waits.js +482 -0
- package/dist/src/provider-waits.js.map +1 -0
- package/dist/src/record-journal.d.ts +127 -0
- package/dist/src/record-journal.d.ts.map +1 -0
- package/dist/src/record-journal.js +202 -0
- package/dist/src/record-journal.js.map +1 -0
- package/dist/src/refusal-reactions.d.ts +14 -12
- package/dist/src/refusal-reactions.d.ts.map +1 -1
- package/dist/src/refusal-reactions.js +14 -12
- package/dist/src/refusal-reactions.js.map +1 -1
- package/dist/src/shutdown.d.ts +13 -0
- package/dist/src/shutdown.d.ts.map +1 -0
- package/dist/src/shutdown.js +46 -0
- package/dist/src/shutdown.js.map +1 -0
- package/dist/src/silent-heartbeat.d.ts +74 -0
- package/dist/src/silent-heartbeat.d.ts.map +1 -0
- package/dist/src/silent-heartbeat.js +77 -0
- package/dist/src/silent-heartbeat.js.map +1 -0
- package/dist/src/tool-presentation.d.ts +50 -0
- package/dist/src/tool-presentation.d.ts.map +1 -0
- package/dist/src/tool-presentation.js +235 -0
- package/dist/src/tool-presentation.js.map +1 -0
- package/dist/src/tool-result-guard.d.ts +142 -6
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +357 -36
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tool-result-history.d.ts +13 -0
- package/dist/src/tool-result-history.d.ts.map +1 -1
- package/dist/src/tool-result-history.js +21 -1
- package/dist/src/tool-result-history.js.map +1 -1
- package/dist/src/types/agent.d.ts +18 -2
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +16 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +12 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +26 -4
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +87 -3
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/test/batched-wake-locus.test.d.ts +2 -0
- package/dist/test/batched-wake-locus.test.d.ts.map +1 -0
- package/dist/test/batched-wake-locus.test.js +166 -0
- package/dist/test/batched-wake-locus.test.js.map +1 -0
- package/dist/test/code-execution.test.js +386 -2
- package/dist/test/code-execution.test.js.map +1 -1
- package/dist/test/component-defaults.test.d.ts +2 -0
- package/dist/test/component-defaults.test.d.ts.map +1 -0
- package/dist/test/component-defaults.test.js +54 -0
- package/dist/test/component-defaults.test.js.map +1 -0
- package/dist/test/conversation-routing.test.js +57 -1
- package/dist/test/conversation-routing.test.js.map +1 -1
- package/dist/test/discord-awareness-outbox.test.js +3 -0
- package/dist/test/discord-awareness-outbox.test.js.map +1 -1
- package/dist/test/ephemeral-settle.test.js +3 -0
- package/dist/test/ephemeral-settle.test.js.map +1 -1
- package/dist/test/event-gate.test.js +118 -0
- package/dist/test/event-gate.test.js.map +1 -1
- package/dist/test/explicit-prose-routing.test.js +975 -0
- package/dist/test/explicit-prose-routing.test.js.map +1 -1
- package/dist/test/framework.test.js +160 -1
- package/dist/test/framework.test.js.map +1 -1
- package/dist/test/helpers/coalescing-fixture.d.ts.map +1 -1
- package/dist/test/helpers/coalescing-fixture.js +3 -2
- package/dist/test/helpers/coalescing-fixture.js.map +1 -1
- package/dist/test/helpers/pytc-stop-rows.d.ts +11 -0
- package/dist/test/helpers/pytc-stop-rows.d.ts.map +1 -0
- package/dist/test/helpers/pytc-stop-rows.js +143 -0
- package/dist/test/helpers/pytc-stop-rows.js.map +1 -0
- package/dist/test/inference-exhaustion-rewind.test.js +142 -14
- package/dist/test/inference-exhaustion-rewind.test.js.map +1 -1
- package/dist/test/inference-failure-bounds.test.d.ts +2 -0
- package/dist/test/inference-failure-bounds.test.d.ts.map +1 -0
- package/dist/test/inference-failure-bounds.test.js +1201 -0
- package/dist/test/inference-failure-bounds.test.js.map +1 -0
- package/dist/test/late-events-after-stop.test.d.ts +2 -0
- package/dist/test/late-events-after-stop.test.d.ts.map +1 -0
- package/dist/test/late-events-after-stop.test.js +122 -0
- package/dist/test/late-events-after-stop.test.js.map +1 -0
- package/dist/test/mcpl-awareness-barrier.test.js +141 -0
- package/dist/test/mcpl-awareness-barrier.test.js.map +1 -1
- package/dist/test/mcpl-coalescing.test.js +97 -22
- package/dist/test/mcpl-coalescing.test.js.map +1 -1
- package/dist/test/mcpl-config-diagnostics.test.d.ts +2 -0
- package/dist/test/mcpl-config-diagnostics.test.d.ts.map +1 -0
- package/dist/test/mcpl-config-diagnostics.test.js +452 -0
- package/dist/test/mcpl-config-diagnostics.test.js.map +1 -0
- package/dist/test/mcpl-empty-content.test.d.ts +2 -0
- package/dist/test/mcpl-empty-content.test.d.ts.map +1 -0
- package/dist/test/mcpl-empty-content.test.js +368 -0
- package/dist/test/mcpl-empty-content.test.js.map +1 -0
- package/dist/test/mcpl-manifest-changed.test.js +2 -1
- package/dist/test/mcpl-manifest-changed.test.js.map +1 -1
- package/dist/test/mcpl-reference-fetcher.test.js +232 -4
- package/dist/test/mcpl-reference-fetcher.test.js.map +1 -1
- package/dist/test/mcpl-reference-fields.test.js +69 -2
- package/dist/test/mcpl-reference-fields.test.js.map +1 -1
- package/dist/test/mcpl-tool-lifecycle-emitter.test.js +106 -1
- package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +1 -1
- package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +2 -0
- package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +1 -0
- package/dist/test/mcpl-tool-lifecycle-script.test.js +288 -0
- package/dist/test/mcpl-tool-lifecycle-script.test.js.map +1 -0
- package/dist/test/mcpl-ws-transport.test.js +103 -0
- package/dist/test/mcpl-ws-transport.test.js.map +1 -1
- package/dist/test/ops-alert.test.js +2 -1
- package/dist/test/ops-alert.test.js.map +1 -1
- package/dist/test/placed-reaction-baseline.test.d.ts +2 -0
- package/dist/test/placed-reaction-baseline.test.d.ts.map +1 -0
- package/dist/test/placed-reaction-baseline.test.js +312 -0
- package/dist/test/placed-reaction-baseline.test.js.map +1 -0
- package/dist/test/prose-stream-router.test.js +41 -0
- package/dist/test/prose-stream-router.test.js.map +1 -1
- package/dist/test/provider-acceleration-cooldown.test.js +5 -1
- package/dist/test/provider-acceleration-cooldown.test.js.map +1 -1
- package/dist/test/provider-waits.test.d.ts +2 -0
- package/dist/test/provider-waits.test.d.ts.map +1 -0
- package/dist/test/provider-waits.test.js +2196 -0
- package/dist/test/provider-waits.test.js.map +1 -0
- package/dist/test/reaction-baseline-platform.test.d.ts +2 -0
- package/dist/test/reaction-baseline-platform.test.d.ts.map +1 -0
- package/dist/test/reaction-baseline-platform.test.js +124 -0
- package/dist/test/reaction-baseline-platform.test.js.map +1 -0
- package/dist/test/record-journal.test.d.ts +6 -0
- package/dist/test/record-journal.test.d.ts.map +1 -0
- package/dist/test/record-journal.test.js +269 -0
- package/dist/test/record-journal.test.js.map +1 -0
- package/dist/test/refusal-reaction-baseline.test.js +10 -3
- package/dist/test/refusal-reaction-baseline.test.js.map +1 -1
- package/dist/test/save-recent-image.test.js +240 -2
- package/dist/test/save-recent-image.test.js.map +1 -1
- package/dist/test/shutdown-errors.test.d.ts +2 -0
- package/dist/test/shutdown-errors.test.d.ts.map +1 -0
- package/dist/test/shutdown-errors.test.js +662 -0
- package/dist/test/shutdown-errors.test.js.map +1 -0
- package/dist/test/silent-heartbeat-rows.test.d.ts +2 -0
- package/dist/test/silent-heartbeat-rows.test.d.ts.map +1 -0
- package/dist/test/silent-heartbeat-rows.test.js +451 -0
- package/dist/test/silent-heartbeat-rows.test.js.map +1 -0
- package/dist/test/silent-heartbeat.test.js +49 -38
- package/dist/test/silent-heartbeat.test.js.map +1 -1
- package/dist/test/stop-with-work-in-flight.test.d.ts +23 -0
- package/dist/test/stop-with-work-in-flight.test.d.ts.map +1 -0
- package/dist/test/stop-with-work-in-flight.test.js +672 -0
- package/dist/test/stop-with-work-in-flight.test.js.map +1 -0
- package/dist/test/surgery-marks-receipt.test.d.ts +2 -0
- package/dist/test/surgery-marks-receipt.test.d.ts.map +1 -0
- package/dist/test/surgery-marks-receipt.test.js +540 -0
- package/dist/test/surgery-marks-receipt.test.js.map +1 -0
- package/dist/test/tool-batch-hook.test.d.ts +8 -0
- package/dist/test/tool-batch-hook.test.d.ts.map +1 -0
- package/dist/test/tool-batch-hook.test.js +275 -0
- package/dist/test/tool-batch-hook.test.js.map +1 -0
- package/dist/test/tool-presentation.test.d.ts +2 -0
- package/dist/test/tool-presentation.test.d.ts.map +1 -0
- package/dist/test/tool-presentation.test.js +268 -0
- package/dist/test/tool-presentation.test.js.map +1 -0
- package/dist/test/tool-result-guard.test.js +2545 -19
- package/dist/test/tool-result-guard.test.js.map +1 -1
- package/dist/test/tool-result-history.test.js +2 -1
- package/dist/test/tool-result-history.test.js.map +1 -1
- package/dist/test/turn-trigger-provenance.test.js +35 -0
- package/dist/test/turn-trigger-provenance.test.js.map +1 -1
- package/dist/test/unconvertible-tool-result.test.d.ts +2 -0
- package/dist/test/unconvertible-tool-result.test.d.ts.map +1 -0
- package/dist/test/unconvertible-tool-result.test.js +358 -0
- package/dist/test/unconvertible-tool-result.test.js.map +1 -0
- package/dist/test/workspace-materialize-freshness.test.d.ts +16 -0
- package/dist/test/workspace-materialize-freshness.test.d.ts.map +1 -0
- package/dist/test/workspace-materialize-freshness.test.js +252 -0
- package/dist/test/workspace-materialize-freshness.test.js.map +1 -0
- package/dist/test/workspace-three-way.test.d.ts +12 -0
- package/dist/test/workspace-three-way.test.d.ts.map +1 -0
- package/dist/test/workspace-three-way.test.js +3194 -0
- package/dist/test/workspace-three-way.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/agent.ts +124 -45
- package/src/api/server.ts +43 -4
- package/src/code-execution/py-runner.ts +66 -9
- package/src/code-execution/runtime-py.ts +215 -38
- package/src/framework.ts +3191 -544
- package/src/gate/event-gate.ts +59 -7
- package/src/index.ts +18 -2
- package/src/mcpl/channel-registry.ts +24 -3
- package/src/mcpl/conversation-router.ts +34 -1
- package/src/mcpl/feature-set-manager.ts +36 -11
- package/src/mcpl/index.ts +4 -0
- package/src/mcpl/prose-stream-router.ts +79 -23
- package/src/mcpl/push-coalescer.ts +62 -13
- package/src/mcpl/push-handler.ts +41 -12
- package/src/mcpl/reference-fetcher.ts +18 -8
- package/src/mcpl/references.ts +57 -13
- package/src/mcpl/server-connection.ts +95 -13
- package/src/mcpl/server-registry.ts +24 -7
- package/src/mcpl/tool-lifecycle.ts +77 -2
- package/src/mcpl/tool-pattern-check.ts +106 -0
- package/src/mcpl/types.ts +15 -5
- package/src/mcpl/visible-content.ts +57 -0
- package/src/module-registry.ts +269 -25
- package/src/modules/workspace/branch-intent.ts +123 -0
- package/src/modules/workspace/disk-agreement.ts +325 -0
- package/src/modules/workspace/effects.ts +362 -0
- package/src/modules/workspace/index.ts +1098 -446
- package/src/modules/workspace/observe.ts +865 -0
- package/src/modules/workspace/reconcile.ts +1157 -0
- package/src/modules/workspace/sync.ts +6 -336
- package/src/modules/workspace/types.ts +75 -10
- package/src/operator-log.ts +30 -0
- package/src/provider-waits.ts +523 -0
- package/src/record-journal.ts +246 -0
- package/src/refusal-reactions.ts +14 -12
- package/src/shutdown.ts +49 -0
- package/src/silent-heartbeat.ts +141 -0
- package/src/tool-presentation.ts +188 -0
- package/src/tool-result-guard.ts +384 -34
- package/src/tool-result-history.ts +25 -1
- package/src/types/agent.ts +18 -2
- package/src/types/events.ts +16 -0
- package/src/types/framework.ts +12 -0
- package/src/types/module.ts +27 -4
- package/src/types/trace.ts +94 -3
|
@@ -0,0 +1,167 @@
|
|
|
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 type { JsStore } from '@animalabs/chronicle';
|
|
34
|
+
/** Everything `stat` says about a file that can vouch for unchanged bytes. */
|
|
35
|
+
export interface Fingerprint {
|
|
36
|
+
size: number;
|
|
37
|
+
mtimeNs: string;
|
|
38
|
+
ctimeNs: string;
|
|
39
|
+
ino: string;
|
|
40
|
+
dev: string;
|
|
41
|
+
/** When the bytes were hashed (ms). Timestamps within 2 s of it can't vouch. */
|
|
42
|
+
hashedAt: number;
|
|
43
|
+
}
|
|
44
|
+
export type Agreed = {
|
|
45
|
+
kind: 'absent';
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'content';
|
|
48
|
+
hash: string;
|
|
49
|
+
size: number;
|
|
50
|
+
fp?: Fingerprint;
|
|
51
|
+
};
|
|
52
|
+
export type Candidate = {
|
|
53
|
+
kind: 'absent';
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'content';
|
|
56
|
+
hash: string;
|
|
57
|
+
};
|
|
58
|
+
export interface Pending {
|
|
59
|
+
kind: 'pending';
|
|
60
|
+
/** What the module is about to do: change disk, or adopt disk into the tree. */
|
|
61
|
+
effect: 'disk' | 'adopt';
|
|
62
|
+
/** P before the intent (null: unknown). */
|
|
63
|
+
prior: Agreed | null;
|
|
64
|
+
expect: Candidate;
|
|
65
|
+
/** Branch the intent was recorded on (an adoption's meaning is branch-relative). */
|
|
66
|
+
branchId: string;
|
|
67
|
+
}
|
|
68
|
+
export interface Interrupted {
|
|
69
|
+
kind: 'interrupted';
|
|
70
|
+
/** The values disk could have held after the interrupted effect. */
|
|
71
|
+
candidates: Candidate[];
|
|
72
|
+
}
|
|
73
|
+
export type Physical = Agreed | Pending | Interrupted;
|
|
74
|
+
/** Which directory a mount root is: its device and inode, as decimal strings. */
|
|
75
|
+
export interface RootIdentity {
|
|
76
|
+
dev: string;
|
|
77
|
+
ino: string;
|
|
78
|
+
}
|
|
79
|
+
export declare function sameRootIdentity(a: RootIdentity, b: RootIdentity): boolean;
|
|
80
|
+
export declare const DISK_AGREEMENT_RECORD_TYPE = "workspace/disk-agreement";
|
|
81
|
+
export declare function candidateOf(value: Agreed): Candidate;
|
|
82
|
+
export declare function sameCandidate(a: Candidate, b: Candidate): boolean;
|
|
83
|
+
export declare class DiskAgreement {
|
|
84
|
+
private readonly store;
|
|
85
|
+
private readonly journal;
|
|
86
|
+
private readonly mounts;
|
|
87
|
+
private configured;
|
|
88
|
+
/**
|
|
89
|
+
* An append (or a rebuild) not yet made durable. Cleared only by a
|
|
90
|
+
* barrier that succeeds, so a failed barrier stays owed: a later pass with
|
|
91
|
+
* nothing new to record still settles it before deciding anything.
|
|
92
|
+
*/
|
|
93
|
+
private unsynced;
|
|
94
|
+
constructor(store: JsStore);
|
|
95
|
+
/**
|
|
96
|
+
* Rebuild P from the journal for the configured mounts. A mount whose
|
|
97
|
+
* recorded root differs from its configured one starts empty: evidence
|
|
98
|
+
* about another directory says nothing about this one.
|
|
99
|
+
*/
|
|
100
|
+
open(mounts: Array<{
|
|
101
|
+
name: string;
|
|
102
|
+
root: string;
|
|
103
|
+
}>): void;
|
|
104
|
+
/**
|
|
105
|
+
* Re-read the journal when an earlier write left it unreconciled, and throw
|
|
106
|
+
* once an append failed inside the store. P may then lack what that append
|
|
107
|
+
* recorded (the store may hold it where this process can't read it back),
|
|
108
|
+
* and a decision from stale P can record what reopening contradicts, such
|
|
109
|
+
* as a conflict that stays after the right evidence is back. So until the
|
|
110
|
+
* store is reopened, no evidence is read, written or checkpointed, and
|
|
111
|
+
* every pass and tool call that needs it fails.
|
|
112
|
+
*/
|
|
113
|
+
ensureReconciled(): void;
|
|
114
|
+
/** Whether evidence recorded so far still awaits a barrier. */
|
|
115
|
+
get needsBarrier(): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Replay the journal into memory. Records that read back are not proven
|
|
118
|
+
* durable — a previous owner may have appended them and died before any
|
|
119
|
+
* barrier — so evidence rebuilt here owes one, whether at open or after an
|
|
120
|
+
* ambiguous append.
|
|
121
|
+
*/
|
|
122
|
+
private rebuild;
|
|
123
|
+
/** The identity of the root this mount's evidence was gathered under, once any was. */
|
|
124
|
+
rootIdentity(mount: string): RootIdentity | undefined;
|
|
125
|
+
/**
|
|
126
|
+
* Record the root's identity just before the first evidence a mount gains:
|
|
127
|
+
* the caller saw it as the root while gathering that evidence. A no-op once
|
|
128
|
+
* one is recorded; changing it is acceptRoot's alone.
|
|
129
|
+
*/
|
|
130
|
+
recordRootIdentity(mount: string, identity: RootIdentity): void;
|
|
131
|
+
/**
|
|
132
|
+
* Take the root as it is now, at the operator's word: record its identity
|
|
133
|
+
* and forget every path's evidence, which described another directory.
|
|
134
|
+
*/
|
|
135
|
+
acceptRoot(mount: string, identity: RootIdentity): void;
|
|
136
|
+
get(mount: string, path: string): Physical | undefined;
|
|
137
|
+
/** Every path with evidence under a prefix ('' for the whole mount). */
|
|
138
|
+
paths(mount: string, prefix?: string): Array<[string, Physical]>;
|
|
139
|
+
/**
|
|
140
|
+
* Record an intent. `durable` makes it (and every append before it) stable
|
|
141
|
+
* before the caller performs the effect; batches pass it on their last one.
|
|
142
|
+
*/
|
|
143
|
+
intend(mount: string, path: string, intent: Omit<Pending, 'kind'>, opts?: {
|
|
144
|
+
durable?: boolean;
|
|
145
|
+
}): void;
|
|
146
|
+
/**
|
|
147
|
+
* Record agreed evidence. `afterCommittedState` when it asserts a tree
|
|
148
|
+
* commit made just before (the commit is synced first).
|
|
149
|
+
*/
|
|
150
|
+
set(mount: string, path: string, value: Agreed | Interrupted, opts?: {
|
|
151
|
+
afterCommittedState?: boolean;
|
|
152
|
+
}): void;
|
|
153
|
+
/** Forget a path: P becomes unknown, the most conservative evidence. */
|
|
154
|
+
forget(mount: string, path: string): void;
|
|
155
|
+
/**
|
|
156
|
+
* Put back exactly the evidence a path had before an intent whose effect
|
|
157
|
+
* never touched it — whatever kind it was (agreed, pending, interrupted, or
|
|
158
|
+
* unknown) — so the intent leaves no trace in what P says.
|
|
159
|
+
*/
|
|
160
|
+
restore(mount: string, path: string, value: Physical | undefined): void;
|
|
161
|
+
private write;
|
|
162
|
+
/** Checkpoint when the tail has outgrown the live evidence. */
|
|
163
|
+
maybeCheckpoint(): void;
|
|
164
|
+
/** Make every append so far stable. Throws, and stays owed, if the sync fails. */
|
|
165
|
+
barrier(): void;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=disk-agreement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk-agreement.d.ts","sourceRoot":"","sources":["../../../../src/modules/workspace/disk-agreement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAGpD,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB,gFAAgF;IAChF,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,aAAa,CAAC;IACpB,oEAAoE;IACpE,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;AAEtD,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAQD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,OAAO,CAE1E;AAmBD,eAAO,MAAM,0BAA0B,6BAA6B,CAAC;AAKrE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAEpD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAEjE;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwC;IAChE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,UAAU,CAA6C;IAC/D;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAS;gBAEb,KAAK,EAAE,OAAO;IAK1B;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAKzD;;;;;;;;OAQG;IACH,gBAAgB,IAAI,IAAI;IAUxB,+DAA+D;IAC/D,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;;OAKG;IACH,OAAO,CAAC,OAAO;IAmCf,uFAAuF;IACvF,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAKrD;;;;OAIG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI;IAK/D;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI;IAIvD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAKtD,wEAAwE;IACxE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAW5D;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,IAAI;IAI1G;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,IAAI,GAAE;QAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,IAAI;IAIjH,wEAAwE;IACxE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKzC;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI;IAKvE,OAAO,CAAC,KAAK;IAYb,+DAA+D;IAC/D,eAAe,IAAI,IAAI;IAiBvB,kFAAkF;IAClF,OAAO,IAAI,IAAI;CAIhB"}
|
|
@@ -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"}
|