@animalabs/agent-framework 0.22.1 → 0.22.3
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 +4 -2
- package/dist/src/agent.d.ts +266 -28
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +539 -104
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/mcp-server-core.d.ts +41 -0
- package/dist/src/api/mcp-server-core.d.ts.map +1 -0
- package/dist/src/api/mcp-server-core.js +593 -0
- package/dist/src/api/mcp-server-core.js.map +1 -0
- package/dist/src/api/mcp-server.d.ts +2 -36
- package/dist/src/api/mcp-server.d.ts.map +1 -1
- package/dist/src/api/mcp-server.js +15 -595
- package/dist/src/api/mcp-server.js.map +1 -1
- package/dist/src/api/server.d.ts +9 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +55 -27
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/api/types.d.ts +10 -2
- package/dist/src/api/types.d.ts.map +1 -1
- package/dist/src/branch-names.d.ts +14 -0
- package/dist/src/branch-names.d.ts.map +1 -0
- package/dist/src/branch-names.js +22 -0
- package/dist/src/branch-names.js.map +1 -0
- package/dist/src/closest-names.d.ts +28 -0
- package/dist/src/closest-names.d.ts.map +1 -0
- package/dist/src/closest-names.js +98 -0
- package/dist/src/closest-names.js.map +1 -0
- package/dist/src/code-execution/py-runner.d.ts +16 -5
- package/dist/src/code-execution/py-runner.d.ts.map +1 -1
- package/dist/src/code-execution/py-runner.js +186 -74
- package/dist/src/code-execution/py-runner.js.map +1 -1
- package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
- package/dist/src/code-execution/runtime-py.js +49 -3
- package/dist/src/code-execution/runtime-py.js.map +1 -1
- package/dist/src/code-execution/tool-definition.d.ts +12 -1
- package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
- package/dist/src/code-execution/tool-definition.js +21 -3
- package/dist/src/code-execution/tool-definition.js.map +1 -1
- package/dist/src/config-checks.d.ts +28 -0
- package/dist/src/config-checks.d.ts.map +1 -0
- package/dist/src/config-checks.js +52 -0
- package/dist/src/config-checks.js.map +1 -0
- package/dist/src/deadline-timer.d.ts +21 -0
- package/dist/src/deadline-timer.d.ts.map +1 -0
- package/dist/src/deadline-timer.js +43 -0
- package/dist/src/deadline-timer.js.map +1 -0
- package/dist/src/error-message.d.ts +30 -0
- package/dist/src/error-message.d.ts.map +1 -0
- package/dist/src/error-message.js +50 -0
- package/dist/src/error-message.js.map +1 -0
- package/dist/src/framework.d.ts +1212 -192
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +6686 -1835
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +8 -0
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +27 -3
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/gate-script.d.ts.map +1 -1
- package/dist/src/gate/gate-script.js.map +1 -1
- package/dist/src/index.d.ts +5 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/capability-grant.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +170 -13
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +499 -258
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts +21 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js +91 -30
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts +19 -3
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +23 -8
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/convert-block.d.ts +20 -0
- package/dist/src/mcpl/convert-block.d.ts.map +1 -0
- package/dist/src/mcpl/convert-block.js +89 -0
- package/dist/src/mcpl/convert-block.js.map +1 -0
- package/dist/src/mcpl/errors.d.ts +13 -0
- package/dist/src/mcpl/errors.d.ts.map +1 -1
- package/dist/src/mcpl/errors.js +28 -0
- package/dist/src/mcpl/errors.js.map +1 -1
- package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
- package/dist/src/mcpl/feature-set-manager.js +4 -3
- package/dist/src/mcpl/feature-set-manager.js.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts +23 -0
- package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.js +83 -72
- package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +114 -0
- package/dist/src/mcpl/inbound-source.d.ts.map +1 -0
- package/dist/src/mcpl/inbound-source.js +83 -0
- package/dist/src/mcpl/inbound-source.js.map +1 -0
- package/dist/src/mcpl/index.d.ts +3 -1
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js +3 -1
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts +15 -11
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js +153 -84
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/log-value.d.ts +54 -0
- package/dist/src/mcpl/log-value.d.ts.map +1 -0
- package/dist/src/mcpl/log-value.js +109 -0
- package/dist/src/mcpl/log-value.js.map +1 -0
- package/dist/src/mcpl/prose-stream-router.d.ts +6 -6
- package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
- package/dist/src/mcpl/prose-stream-router.js +4 -4
- package/dist/src/mcpl/prose-stream-router.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +64 -7
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +121 -24
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +38 -8
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +77 -78
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/reference-fetcher.d.ts +14 -2
- package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
- package/dist/src/mcpl/reference-fetcher.js +72 -30
- package/dist/src/mcpl/reference-fetcher.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +49 -14
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +74 -21
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/refusal-lines.d.ts +37 -0
- package/dist/src/mcpl/refusal-lines.d.ts.map +1 -0
- package/dist/src/mcpl/refusal-lines.js +61 -0
- package/dist/src/mcpl/refusal-lines.js.map +1 -0
- package/dist/src/mcpl/scope-manager.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +107 -12
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +470 -139
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/server-registry.d.ts +10 -1
- package/dist/src/mcpl/server-registry.d.ts.map +1 -1
- package/dist/src/mcpl/server-registry.js +14 -3
- package/dist/src/mcpl/server-registry.js.map +1 -1
- package/dist/src/mcpl/skip-reply-wake.d.ts +36 -0
- package/dist/src/mcpl/skip-reply-wake.d.ts.map +1 -0
- package/dist/src/mcpl/skip-reply-wake.js +55 -0
- package/dist/src/mcpl/skip-reply-wake.js.map +1 -0
- package/dist/src/mcpl/tool-classes.d.ts +1 -1
- package/dist/src/mcpl/tool-classes.d.ts.map +1 -1
- package/dist/src/mcpl/tool-classes.js +3 -2
- package/dist/src/mcpl/tool-classes.js.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
- package/dist/src/mcpl/transport.d.ts +74 -7
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +209 -22
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +43 -28
- 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 +15 -0
- package/dist/src/mcpl/visible-content.d.ts.map +1 -1
- package/dist/src/mcpl/visible-content.js +25 -0
- package/dist/src/mcpl/visible-content.js.map +1 -1
- package/dist/src/module-registry.d.ts +19 -1
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +54 -25
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/api/index.d.ts.map +1 -1
- package/dist/src/modules/api/index.js +1 -1
- package/dist/src/modules/api/index.js.map +1 -1
- package/dist/src/modules/discord/discord-js-client.d.ts +1 -0
- package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
- package/dist/src/modules/discord/discord-js-client.js +25 -9
- package/dist/src/modules/discord/discord-js-client.js.map +1 -1
- package/dist/src/modules/discord/index.d.ts +29 -3
- package/dist/src/modules/discord/index.d.ts.map +1 -1
- package/dist/src/modules/discord/index.js +383 -145
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/discord/types.d.ts +14 -1
- package/dist/src/modules/discord/types.d.ts.map +1 -1
- package/dist/src/modules/health/index.d.ts.map +1 -1
- package/dist/src/modules/health/index.js +2 -1
- package/dist/src/modules/health/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts +3 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +359 -127
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +28 -1
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +177 -32
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/unknown-tool.d.ts +10 -0
- package/dist/src/modules/unknown-tool.d.ts.map +1 -0
- package/dist/src/modules/unknown-tool.js +17 -0
- package/dist/src/modules/unknown-tool.js.map +1 -0
- package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
- package/dist/src/modules/workspace/branch-intent.js.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.d.ts +54 -6
- package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +93 -27
- package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
- package/dist/src/modules/workspace/effects.d.ts +64 -6
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/effects.js +291 -44
- package/dist/src/modules/workspace/effects.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +127 -27
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +821 -248
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +117 -15
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +436 -51
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts +75 -8
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +576 -68
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +42 -3
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/modules/workspace/watcher.d.ts +7 -2
- package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
- package/dist/src/modules/workspace/watcher.js +28 -4
- package/dist/src/modules/workspace/watcher.js.map +1 -1
- package/dist/src/operator-log.d.ts.map +1 -1
- package/dist/src/operator-log.js.map +1 -1
- package/dist/src/prose-drafts.d.ts +240 -0
- package/dist/src/prose-drafts.d.ts.map +1 -0
- package/dist/src/prose-drafts.js +362 -0
- package/dist/src/prose-drafts.js.map +1 -0
- package/dist/src/prose-segments.d.ts +21 -2
- package/dist/src/prose-segments.d.ts.map +1 -1
- package/dist/src/prose-segments.js +46 -9
- package/dist/src/prose-segments.js.map +1 -1
- package/dist/src/provider-waits.d.ts.map +1 -1
- package/dist/src/provider-waits.js.map +1 -1
- package/dist/src/queue.d.ts.map +1 -1
- package/dist/src/record-journal.d.ts.map +1 -1
- package/dist/src/record-journal.js.map +1 -1
- package/dist/src/recovery/discord-awareness-outbox.d.ts.map +1 -1
- package/dist/src/recovery/discord-awareness-outbox.js.map +1 -1
- package/dist/src/recovery/offline-branch.d.ts +1 -0
- package/dist/src/recovery/offline-branch.d.ts.map +1 -1
- package/dist/src/recovery/offline-branch.js +32 -25
- package/dist/src/recovery/offline-branch.js.map +1 -1
- package/dist/src/recovery/recover-cli.js.map +1 -1
- package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -1
- package/dist/src/runtime/liveness-watchdog.js.map +1 -1
- package/dist/src/safe-slice.d.ts +5 -1
- package/dist/src/safe-slice.d.ts.map +1 -1
- package/dist/src/safe-slice.js +9 -1
- package/dist/src/safe-slice.js.map +1 -1
- package/dist/src/shutdown.d.ts.map +1 -1
- package/dist/src/shutdown.js.map +1 -1
- package/dist/src/stored-removal.d.ts +73 -0
- package/dist/src/stored-removal.d.ts.map +1 -0
- package/dist/src/stored-removal.js +100 -0
- package/dist/src/stored-removal.js.map +1 -0
- package/dist/src/thinking-token-stamp.js.map +1 -1
- package/dist/src/tool-image-ledger.d.ts +87 -19
- package/dist/src/tool-image-ledger.d.ts.map +1 -1
- package/dist/src/tool-image-ledger.js +135 -32
- package/dist/src/tool-image-ledger.js.map +1 -1
- package/dist/src/tool-presentation.d.ts +9 -0
- package/dist/src/tool-presentation.d.ts.map +1 -1
- package/dist/src/tool-presentation.js +257 -60
- package/dist/src/tool-presentation.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +7 -10
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tool-result-history.d.ts +32 -3
- package/dist/src/tool-result-history.d.ts.map +1 -1
- package/dist/src/tool-result-history.js +106 -19
- package/dist/src/tool-result-history.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +25 -1
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +260 -65
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/tune-out/tools.d.ts +1 -1
- package/dist/src/tune-out/tools.d.ts.map +1 -1
- package/dist/src/types/agent.d.ts +59 -11
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +17 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +31 -5
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/inference-log.d.ts +1 -1
- package/dist/src/types/inference-log.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +30 -7
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +151 -11
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/src/usage/usage-tracker.d.ts +5 -0
- package/dist/src/usage/usage-tracker.d.ts.map +1 -1
- package/dist/src/usage/usage-tracker.js +35 -8
- package/dist/src/usage/usage-tracker.js.map +1 -1
- package/package.json +10 -9
- package/src/agent.ts +572 -105
- package/src/api/mcp-server-core.ts +681 -0
- package/src/api/mcp-server.ts +16 -685
- package/src/api/server.ts +55 -30
- package/src/api/types.ts +10 -2
- package/src/branch-names.ts +19 -0
- package/src/closest-names.ts +94 -0
- package/src/code-execution/py-runner.ts +200 -76
- package/src/code-execution/runtime-py.ts +49 -3
- package/src/code-execution/tool-definition.ts +28 -4
- package/src/config-checks.ts +48 -0
- package/src/deadline-timer.ts +44 -0
- package/src/error-message.ts +48 -0
- package/src/framework.ts +7158 -1936
- package/src/gate/event-gate.ts +28 -3
- package/src/index.ts +12 -3
- package/src/mcpl/channel-registry.ts +615 -289
- package/src/mcpl/checkpoint-manager.ts +104 -29
- package/src/mcpl/conversation-router.ts +28 -8
- package/src/mcpl/convert-block.ts +97 -0
- package/src/mcpl/errors.ts +28 -0
- package/src/mcpl/feature-set-manager.ts +4 -3
- package/src/mcpl/hook-orchestrator.ts +84 -77
- package/src/mcpl/inbound-source.ts +156 -0
- package/src/mcpl/index.ts +10 -1
- package/src/mcpl/inference-router.ts +174 -99
- package/src/mcpl/log-value.ts +116 -0
- package/src/mcpl/prose-stream-router.ts +6 -6
- package/src/mcpl/push-coalescer.ts +124 -24
- package/src/mcpl/push-handler.ts +86 -84
- package/src/mcpl/reference-fetcher.ts +71 -26
- package/src/mcpl/references.ts +81 -22
- package/src/mcpl/refusal-lines.ts +59 -0
- package/src/mcpl/server-connection.ts +496 -142
- package/src/mcpl/server-registry.ts +15 -4
- package/src/mcpl/skip-reply-wake.ts +73 -0
- package/src/mcpl/tool-classes.ts +3 -2
- package/src/mcpl/transport.ts +219 -26
- package/src/mcpl/types.ts +18 -3
- package/src/mcpl/visible-content.ts +27 -0
- package/src/module-registry.ts +58 -23
- package/src/modules/api/index.ts +1 -1
- package/src/modules/discord/discord-js-client.ts +26 -10
- package/src/modules/discord/index.ts +395 -160
- package/src/modules/discord/types.ts +15 -2
- package/src/modules/health/index.ts +2 -1
- package/src/modules/history/index.ts +410 -120
- package/src/modules/history/semantic.ts +189 -32
- package/src/modules/unknown-tool.ts +16 -0
- package/src/modules/workspace/disk-agreement.ts +125 -21
- package/src/modules/workspace/effects.ts +279 -35
- package/src/modules/workspace/index.ts +841 -261
- package/src/modules/workspace/observe.ts +456 -56
- package/src/modules/workspace/reconcile.ts +688 -76
- package/src/modules/workspace/types.ts +41 -2
- package/src/modules/workspace/watcher.ts +27 -4
- package/src/prose-drafts.ts +481 -0
- package/src/prose-segments.ts +51 -8
- package/src/recovery/offline-branch.ts +36 -29
- package/src/safe-slice.ts +9 -1
- package/src/stored-removal.ts +126 -0
- package/src/tool-image-ledger.ts +153 -34
- package/src/tool-presentation.ts +224 -40
- package/src/tool-result-guard.ts +7 -8
- package/src/tool-result-history.ts +124 -20
- package/src/tune-out/coordinator.ts +259 -65
- package/src/types/agent.ts +59 -13
- package/src/types/events.ts +17 -0
- package/src/types/framework.ts +31 -5
- package/src/types/inference-log.ts +1 -1
- package/src/types/module.ts +31 -7
- package/src/types/trace.ts +158 -12
- package/src/usage/usage-tracker.ts +32 -8
- package/dist/test/activation-request-sanitize.test.d.ts +0 -2
- package/dist/test/activation-request-sanitize.test.d.ts.map +0 -1
- package/dist/test/activation-request-sanitize.test.js +0 -89
- package/dist/test/activation-request-sanitize.test.js.map +0 -1
- package/dist/test/agent-cache-ttl.test.d.ts +0 -2
- package/dist/test/agent-cache-ttl.test.d.ts.map +0 -1
- package/dist/test/agent-cache-ttl.test.js +0 -41
- package/dist/test/agent-cache-ttl.test.js.map +0 -1
- package/dist/test/agent-runtime-settings.test.d.ts +0 -2
- package/dist/test/agent-runtime-settings.test.d.ts.map +0 -1
- package/dist/test/agent-runtime-settings.test.js +0 -462
- package/dist/test/agent-runtime-settings.test.js.map +0 -1
- package/dist/test/api-admin-gate.test.d.ts +0 -2
- package/dist/test/api-admin-gate.test.d.ts.map +0 -1
- package/dist/test/api-admin-gate.test.js +0 -116
- package/dist/test/api-admin-gate.test.js.map +0 -1
- package/dist/test/assistant-persist-refusal.test.d.ts +0 -2
- package/dist/test/assistant-persist-refusal.test.d.ts.map +0 -1
- package/dist/test/assistant-persist-refusal.test.js +0 -63
- package/dist/test/assistant-persist-refusal.test.js.map +0 -1
- package/dist/test/batched-wake-locus.test.d.ts +0 -2
- package/dist/test/batched-wake-locus.test.d.ts.map +0 -1
- package/dist/test/batched-wake-locus.test.js +0 -166
- package/dist/test/batched-wake-locus.test.js.map +0 -1
- package/dist/test/busy-agent-scheduler.test.d.ts +0 -2
- package/dist/test/busy-agent-scheduler.test.d.ts.map +0 -1
- package/dist/test/busy-agent-scheduler.test.js +0 -92
- package/dist/test/busy-agent-scheduler.test.js.map +0 -1
- package/dist/test/channel-incoming-targeting.test.d.ts +0 -2
- package/dist/test/channel-incoming-targeting.test.d.ts.map +0 -1
- package/dist/test/channel-incoming-targeting.test.js +0 -81
- package/dist/test/channel-incoming-targeting.test.js.map +0 -1
- package/dist/test/channel-label-history.test.d.ts +0 -2
- package/dist/test/channel-label-history.test.d.ts.map +0 -1
- package/dist/test/channel-label-history.test.js +0 -424
- package/dist/test/channel-label-history.test.js.map +0 -1
- package/dist/test/channel-lifecycle-provenance.test.d.ts +0 -2
- package/dist/test/channel-lifecycle-provenance.test.d.ts.map +0 -1
- package/dist/test/channel-lifecycle-provenance.test.js +0 -47
- package/dist/test/channel-lifecycle-provenance.test.js.map +0 -1
- package/dist/test/channel-registry-routing.test.d.ts +0 -2
- package/dist/test/channel-registry-routing.test.d.ts.map +0 -1
- package/dist/test/channel-registry-routing.test.js +0 -446
- package/dist/test/channel-registry-routing.test.js.map +0 -1
- package/dist/test/channel-registry-typing.test.d.ts +0 -2
- package/dist/test/channel-registry-typing.test.d.ts.map +0 -1
- package/dist/test/channel-registry-typing.test.js +0 -91
- package/dist/test/channel-registry-typing.test.js.map +0 -1
- package/dist/test/channels-changed-respond-first.test.d.ts +0 -2
- package/dist/test/channels-changed-respond-first.test.d.ts.map +0 -1
- package/dist/test/channels-changed-respond-first.test.js +0 -74
- package/dist/test/channels-changed-respond-first.test.js.map +0 -1
- package/dist/test/checkpoint-attribution.test.d.ts +0 -2
- package/dist/test/checkpoint-attribution.test.d.ts.map +0 -1
- package/dist/test/checkpoint-attribution.test.js +0 -200
- package/dist/test/checkpoint-attribution.test.js.map +0 -1
- package/dist/test/code-execution.test.d.ts +0 -11
- package/dist/test/code-execution.test.d.ts.map +0 -1
- package/dist/test/code-execution.test.js +0 -1272
- package/dist/test/code-execution.test.js.map +0 -1
- package/dist/test/component-defaults.test.d.ts +0 -2
- package/dist/test/component-defaults.test.d.ts.map +0 -1
- package/dist/test/component-defaults.test.js +0 -54
- package/dist/test/component-defaults.test.js.map +0 -1
- package/dist/test/conversation-fork-injection-scoping.test.d.ts +0 -2
- package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +0 -1
- package/dist/test/conversation-fork-injection-scoping.test.js +0 -200
- package/dist/test/conversation-fork-injection-scoping.test.js.map +0 -1
- package/dist/test/conversation-router.test.d.ts +0 -2
- package/dist/test/conversation-router.test.d.ts.map +0 -1
- package/dist/test/conversation-router.test.js +0 -174
- package/dist/test/conversation-router.test.js.map +0 -1
- package/dist/test/conversation-routing.test.d.ts +0 -7
- package/dist/test/conversation-routing.test.d.ts.map +0 -1
- package/dist/test/conversation-routing.test.js +0 -387
- package/dist/test/conversation-routing.test.js.map +0 -1
- package/dist/test/deferred-wake-integrity.test.d.ts +0 -29
- package/dist/test/deferred-wake-integrity.test.d.ts.map +0 -1
- package/dist/test/deferred-wake-integrity.test.js +0 -320
- package/dist/test/deferred-wake-integrity.test.js.map +0 -1
- package/dist/test/discord-awareness-outbox.test.d.ts +0 -2
- package/dist/test/discord-awareness-outbox.test.d.ts.map +0 -1
- package/dist/test/discord-awareness-outbox.test.js +0 -267
- package/dist/test/discord-awareness-outbox.test.js.map +0 -1
- package/dist/test/discord-history-delete-window.test.d.ts +0 -2
- package/dist/test/discord-history-delete-window.test.d.ts.map +0 -1
- package/dist/test/discord-history-delete-window.test.js +0 -81
- package/dist/test/discord-history-delete-window.test.js.map +0 -1
- package/dist/test/discord-history-sync.test.d.ts +0 -2
- package/dist/test/discord-history-sync.test.d.ts.map +0 -1
- package/dist/test/discord-history-sync.test.js +0 -56
- package/dist/test/discord-history-sync.test.js.map +0 -1
- package/dist/test/ephemeral-settle.test.d.ts +0 -2
- package/dist/test/ephemeral-settle.test.d.ts.map +0 -1
- package/dist/test/ephemeral-settle.test.js +0 -354
- package/dist/test/ephemeral-settle.test.js.map +0 -1
- package/dist/test/event-gate-passthrough.test.d.ts +0 -15
- package/dist/test/event-gate-passthrough.test.d.ts.map +0 -1
- package/dist/test/event-gate-passthrough.test.js +0 -289
- package/dist/test/event-gate-passthrough.test.js.map +0 -1
- package/dist/test/event-gate-policy-mutation.test.d.ts +0 -2
- package/dist/test/event-gate-policy-mutation.test.d.ts.map +0 -1
- package/dist/test/event-gate-policy-mutation.test.js +0 -137
- package/dist/test/event-gate-policy-mutation.test.js.map +0 -1
- package/dist/test/event-gate-rate-limit.test.d.ts +0 -2
- package/dist/test/event-gate-rate-limit.test.d.ts.map +0 -1
- package/dist/test/event-gate-rate-limit.test.js +0 -627
- package/dist/test/event-gate-rate-limit.test.js.map +0 -1
- package/dist/test/event-gate-script.test.d.ts +0 -2
- package/dist/test/event-gate-script.test.d.ts.map +0 -1
- package/dist/test/event-gate-script.test.js +0 -95
- package/dist/test/event-gate-script.test.js.map +0 -1
- package/dist/test/event-gate-selfwake.test.d.ts +0 -15
- package/dist/test/event-gate-selfwake.test.d.ts.map +0 -1
- package/dist/test/event-gate-selfwake.test.js +0 -125
- package/dist/test/event-gate-selfwake.test.js.map +0 -1
- package/dist/test/event-gate-tags.test.d.ts +0 -2
- package/dist/test/event-gate-tags.test.d.ts.map +0 -1
- package/dist/test/event-gate-tags.test.js +0 -76
- package/dist/test/event-gate-tags.test.js.map +0 -1
- package/dist/test/event-gate.test.d.ts +0 -2
- package/dist/test/event-gate.test.d.ts.map +0 -1
- package/dist/test/event-gate.test.js +0 -919
- package/dist/test/event-gate.test.js.map +0 -1
- package/dist/test/explicit-prose-routing.test.d.ts +0 -11
- package/dist/test/explicit-prose-routing.test.d.ts.map +0 -1
- package/dist/test/explicit-prose-routing.test.js +0 -1394
- package/dist/test/explicit-prose-routing.test.js.map +0 -1
- package/dist/test/framework.test.d.ts +0 -5
- package/dist/test/framework.test.d.ts.map +0 -1
- package/dist/test/framework.test.js +0 -1048
- package/dist/test/framework.test.js.map +0 -1
- package/dist/test/gather-context-timeout.test.d.ts +0 -2
- package/dist/test/gather-context-timeout.test.d.ts.map +0 -1
- package/dist/test/gather-context-timeout.test.js +0 -120
- package/dist/test/gather-context-timeout.test.js.map +0 -1
- package/dist/test/health-snapshot.test.d.ts +0 -2
- package/dist/test/health-snapshot.test.d.ts.map +0 -1
- package/dist/test/health-snapshot.test.js +0 -74
- package/dist/test/health-snapshot.test.js.map +0 -1
- package/dist/test/helpers/coalescing-fixture.d.ts +0 -72
- package/dist/test/helpers/coalescing-fixture.d.ts.map +0 -1
- package/dist/test/helpers/coalescing-fixture.js +0 -146
- package/dist/test/helpers/coalescing-fixture.js.map +0 -1
- package/dist/test/helpers/coalescing-kill-child.d.ts +0 -2
- package/dist/test/helpers/coalescing-kill-child.d.ts.map +0 -1
- package/dist/test/helpers/coalescing-kill-child.js +0 -24
- package/dist/test/helpers/coalescing-kill-child.js.map +0 -1
- package/dist/test/helpers/mock-membrane.d.ts +0 -53
- package/dist/test/helpers/mock-membrane.d.ts.map +0 -1
- package/dist/test/helpers/mock-membrane.js +0 -168
- package/dist/test/helpers/mock-membrane.js.map +0 -1
- package/dist/test/helpers/pytc-stop-rows.d.ts +0 -11
- package/dist/test/helpers/pytc-stop-rows.d.ts.map +0 -1
- package/dist/test/helpers/pytc-stop-rows.js +0 -143
- package/dist/test/helpers/pytc-stop-rows.js.map +0 -1
- package/dist/test/helpers/quiesce-crash-child.d.ts +0 -2
- package/dist/test/helpers/quiesce-crash-child.d.ts.map +0 -1
- package/dist/test/helpers/quiesce-crash-child.js +0 -156
- package/dist/test/helpers/quiesce-crash-child.js.map +0 -1
- package/dist/test/history-module-overview.test.d.ts +0 -2
- package/dist/test/history-module-overview.test.d.ts.map +0 -1
- package/dist/test/history-module-overview.test.js +0 -711
- package/dist/test/history-module-overview.test.js.map +0 -1
- package/dist/test/history-module-realstore.test.d.ts +0 -2
- package/dist/test/history-module-realstore.test.d.ts.map +0 -1
- package/dist/test/history-module-realstore.test.js +0 -363
- package/dist/test/history-module-realstore.test.js.map +0 -1
- package/dist/test/history-module-semantic.test.d.ts +0 -2
- package/dist/test/history-module-semantic.test.d.ts.map +0 -1
- package/dist/test/history-module-semantic.test.js +0 -213
- package/dist/test/history-module-semantic.test.js.map +0 -1
- package/dist/test/history-module-ux.test.d.ts +0 -2
- package/dist/test/history-module-ux.test.d.ts.map +0 -1
- package/dist/test/history-module-ux.test.js +0 -304
- package/dist/test/history-module-ux.test.js.map +0 -1
- package/dist/test/history-module.test.d.ts +0 -2
- package/dist/test/history-module.test.d.ts.map +0 -1
- package/dist/test/history-module.test.js +0 -619
- package/dist/test/history-module.test.js.map +0 -1
- package/dist/test/history-semantic-review-findings.test.d.ts +0 -2
- package/dist/test/history-semantic-review-findings.test.d.ts.map +0 -1
- package/dist/test/history-semantic-review-findings.test.js +0 -338
- package/dist/test/history-semantic-review-findings.test.js.map +0 -1
- package/dist/test/history-semantic-sync-converges.test.d.ts +0 -2
- package/dist/test/history-semantic-sync-converges.test.d.ts.map +0 -1
- package/dist/test/history-semantic-sync-converges.test.js +0 -109
- package/dist/test/history-semantic-sync-converges.test.js.map +0 -1
- package/dist/test/host-quiesce.test.d.ts +0 -2
- package/dist/test/host-quiesce.test.d.ts.map +0 -1
- package/dist/test/host-quiesce.test.js +0 -863
- package/dist/test/host-quiesce.test.js.map +0 -1
- package/dist/test/inference-exhaustion-rewind.test.d.ts +0 -2
- package/dist/test/inference-exhaustion-rewind.test.d.ts.map +0 -1
- package/dist/test/inference-exhaustion-rewind.test.js +0 -309
- package/dist/test/inference-exhaustion-rewind.test.js.map +0 -1
- package/dist/test/inference-failure-bounds.test.d.ts +0 -2
- package/dist/test/inference-failure-bounds.test.d.ts.map +0 -1
- package/dist/test/inference-failure-bounds.test.js +0 -1201
- package/dist/test/inference-failure-bounds.test.js.map +0 -1
- package/dist/test/inference-failure-observability.test.d.ts +0 -2
- package/dist/test/inference-failure-observability.test.d.ts.map +0 -1
- package/dist/test/inference-failure-observability.test.js +0 -84
- package/dist/test/inference-failure-observability.test.js.map +0 -1
- package/dist/test/journal-tool.test.d.ts +0 -2
- package/dist/test/journal-tool.test.d.ts.map +0 -1
- package/dist/test/journal-tool.test.js +0 -33
- package/dist/test/journal-tool.test.js.map +0 -1
- package/dist/test/kv-unified-open-flight-retry.test.d.ts +0 -2
- package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +0 -1
- package/dist/test/kv-unified-open-flight-retry.test.js +0 -189
- package/dist/test/kv-unified-open-flight-retry.test.js.map +0 -1
- package/dist/test/kv-unified-wiring.test.d.ts +0 -2
- package/dist/test/kv-unified-wiring.test.d.ts.map +0 -1
- package/dist/test/kv-unified-wiring.test.js +0 -95
- package/dist/test/kv-unified-wiring.test.js.map +0 -1
- package/dist/test/late-events-after-stop.test.d.ts +0 -2
- package/dist/test/late-events-after-stop.test.d.ts.map +0 -1
- package/dist/test/late-events-after-stop.test.js +0 -122
- package/dist/test/late-events-after-stop.test.js.map +0 -1
- package/dist/test/liveness-watchdog.test.d.ts +0 -2
- package/dist/test/liveness-watchdog.test.d.ts.map +0 -1
- package/dist/test/liveness-watchdog.test.js +0 -94
- package/dist/test/liveness-watchdog.test.js.map +0 -1
- package/dist/test/machine-close-provenance.test.d.ts +0 -2
- package/dist/test/machine-close-provenance.test.d.ts.map +0 -1
- package/dist/test/machine-close-provenance.test.js +0 -116
- package/dist/test/machine-close-provenance.test.js.map +0 -1
- package/dist/test/maintenance-timer.test.d.ts +0 -2
- package/dist/test/maintenance-timer.test.d.ts.map +0 -1
- package/dist/test/maintenance-timer.test.js +0 -265
- package/dist/test/maintenance-timer.test.js.map +0 -1
- package/dist/test/mcpl-access-provider.test.d.ts +0 -2
- package/dist/test/mcpl-access-provider.test.d.ts.map +0 -1
- package/dist/test/mcpl-access-provider.test.js +0 -20
- package/dist/test/mcpl-access-provider.test.js.map +0 -1
- package/dist/test/mcpl-awareness-barrier.test.d.ts +0 -2
- package/dist/test/mcpl-awareness-barrier.test.d.ts.map +0 -1
- package/dist/test/mcpl-awareness-barrier.test.js +0 -769
- package/dist/test/mcpl-awareness-barrier.test.js.map +0 -1
- package/dist/test/mcpl-capability-grant.test.d.ts +0 -10
- package/dist/test/mcpl-capability-grant.test.d.ts.map +0 -1
- package/dist/test/mcpl-capability-grant.test.js +0 -111
- package/dist/test/mcpl-capability-grant.test.js.map +0 -1
- package/dist/test/mcpl-capability-scoping.test.d.ts +0 -2
- package/dist/test/mcpl-capability-scoping.test.d.ts.map +0 -1
- package/dist/test/mcpl-capability-scoping.test.js +0 -266
- package/dist/test/mcpl-capability-scoping.test.js.map +0 -1
- package/dist/test/mcpl-child-env.test.d.ts +0 -2
- package/dist/test/mcpl-child-env.test.d.ts.map +0 -1
- package/dist/test/mcpl-child-env.test.js +0 -158
- package/dist/test/mcpl-child-env.test.js.map +0 -1
- package/dist/test/mcpl-coalescing.test.d.ts +0 -2
- package/dist/test/mcpl-coalescing.test.d.ts.map +0 -1
- package/dist/test/mcpl-coalescing.test.js +0 -1125
- package/dist/test/mcpl-coalescing.test.js.map +0 -1
- package/dist/test/mcpl-config-diagnostics.test.d.ts +0 -2
- package/dist/test/mcpl-config-diagnostics.test.d.ts.map +0 -1
- package/dist/test/mcpl-config-diagnostics.test.js +0 -452
- package/dist/test/mcpl-config-diagnostics.test.js.map +0 -1
- package/dist/test/mcpl-empty-content.test.d.ts +0 -2
- package/dist/test/mcpl-empty-content.test.d.ts.map +0 -1
- package/dist/test/mcpl-empty-content.test.js +0 -368
- package/dist/test/mcpl-empty-content.test.js.map +0 -1
- package/dist/test/mcpl-gate-roundtrip.test.d.ts +0 -2
- package/dist/test/mcpl-gate-roundtrip.test.d.ts.map +0 -1
- package/dist/test/mcpl-gate-roundtrip.test.js +0 -285
- package/dist/test/mcpl-gate-roundtrip.test.js.map +0 -1
- package/dist/test/mcpl-list-status.test.d.ts +0 -2
- package/dist/test/mcpl-list-status.test.d.ts.map +0 -1
- package/dist/test/mcpl-list-status.test.js +0 -82
- package/dist/test/mcpl-list-status.test.js.map +0 -1
- package/dist/test/mcpl-manifest-changed.test.d.ts +0 -2
- package/dist/test/mcpl-manifest-changed.test.d.ts.map +0 -1
- package/dist/test/mcpl-manifest-changed.test.js +0 -178
- package/dist/test/mcpl-manifest-changed.test.js.map +0 -1
- package/dist/test/mcpl-push-convert.test.d.ts +0 -2
- package/dist/test/mcpl-push-convert.test.d.ts.map +0 -1
- package/dist/test/mcpl-push-convert.test.js +0 -42
- package/dist/test/mcpl-push-convert.test.js.map +0 -1
- package/dist/test/mcpl-reconnect-events.test.d.ts +0 -2
- package/dist/test/mcpl-reconnect-events.test.d.ts.map +0 -1
- package/dist/test/mcpl-reconnect-events.test.js +0 -144
- package/dist/test/mcpl-reconnect-events.test.js.map +0 -1
- package/dist/test/mcpl-reconnect-reregistration.test.d.ts +0 -2
- package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +0 -1
- package/dist/test/mcpl-reconnect-reregistration.test.js +0 -236
- package/dist/test/mcpl-reconnect-reregistration.test.js.map +0 -1
- package/dist/test/mcpl-reference-fetcher.test.d.ts +0 -2
- package/dist/test/mcpl-reference-fetcher.test.d.ts.map +0 -1
- package/dist/test/mcpl-reference-fetcher.test.js +0 -427
- package/dist/test/mcpl-reference-fetcher.test.js.map +0 -1
- package/dist/test/mcpl-reference-fields.test.d.ts +0 -2
- package/dist/test/mcpl-reference-fields.test.d.ts.map +0 -1
- package/dist/test/mcpl-reference-fields.test.js +0 -149
- package/dist/test/mcpl-reference-fields.test.js.map +0 -1
- package/dist/test/mcpl-request-timeout.test.d.ts +0 -2
- package/dist/test/mcpl-request-timeout.test.d.ts.map +0 -1
- package/dist/test/mcpl-request-timeout.test.js +0 -138
- package/dist/test/mcpl-request-timeout.test.js.map +0 -1
- package/dist/test/mcpl-runtime-lifecycle.test.d.ts +0 -7
- package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +0 -1
- package/dist/test/mcpl-runtime-lifecycle.test.js +0 -142
- package/dist/test/mcpl-runtime-lifecycle.test.js.map +0 -1
- package/dist/test/mcpl-tool-class-listing.test.d.ts +0 -2
- package/dist/test/mcpl-tool-class-listing.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-class-listing.test.js +0 -147
- package/dist/test/mcpl-tool-class-listing.test.js.map +0 -1
- package/dist/test/mcpl-tool-classes.test.d.ts +0 -7
- package/dist/test/mcpl-tool-classes.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-classes.test.js +0 -80
- package/dist/test/mcpl-tool-classes.test.js.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts +0 -2
- package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-e2e.test.js +0 -237
- package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts +0 -11
- package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-emitter.test.js +0 -412
- package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +0 -2
- package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-script.test.js +0 -288
- package/dist/test/mcpl-tool-lifecycle-script.test.js.map +0 -1
- package/dist/test/mcpl-tool-lifecycle.test.d.ts +0 -11
- package/dist/test/mcpl-tool-lifecycle.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-lifecycle.test.js +0 -270
- package/dist/test/mcpl-tool-lifecycle.test.js.map +0 -1
- package/dist/test/mcpl-ws-transport.test.d.ts +0 -2
- package/dist/test/mcpl-ws-transport.test.d.ts.map +0 -1
- package/dist/test/mcpl-ws-transport.test.js +0 -258
- package/dist/test/mcpl-ws-transport.test.js.map +0 -1
- package/dist/test/module-context-integration.test.d.ts +0 -18
- package/dist/test/module-context-integration.test.d.ts.map +0 -1
- package/dist/test/module-context-integration.test.js +0 -181
- package/dist/test/module-context-integration.test.js.map +0 -1
- package/dist/test/offline-recovery-branch.test.d.ts +0 -2
- package/dist/test/offline-recovery-branch.test.d.ts.map +0 -1
- package/dist/test/offline-recovery-branch.test.js +0 -226
- package/dist/test/offline-recovery-branch.test.js.map +0 -1
- package/dist/test/operator-surgery.test.d.ts +0 -2
- package/dist/test/operator-surgery.test.d.ts.map +0 -1
- package/dist/test/operator-surgery.test.js +0 -398
- package/dist/test/operator-surgery.test.js.map +0 -1
- package/dist/test/ops-alert.test.d.ts +0 -2
- package/dist/test/ops-alert.test.d.ts.map +0 -1
- package/dist/test/ops-alert.test.js +0 -153
- package/dist/test/ops-alert.test.js.map +0 -1
- package/dist/test/overbudget-drain-breaker.test.d.ts +0 -2
- package/dist/test/overbudget-drain-breaker.test.d.ts.map +0 -1
- package/dist/test/overbudget-drain-breaker.test.js +0 -197
- package/dist/test/overbudget-drain-breaker.test.js.map +0 -1
- package/dist/test/per-agent-delivery.test.d.ts +0 -2
- package/dist/test/per-agent-delivery.test.d.ts.map +0 -1
- package/dist/test/per-agent-delivery.test.js +0 -108
- package/dist/test/per-agent-delivery.test.js.map +0 -1
- package/dist/test/physical-window-restart.test.d.ts +0 -14
- package/dist/test/physical-window-restart.test.d.ts.map +0 -1
- package/dist/test/physical-window-restart.test.js +0 -222
- package/dist/test/physical-window-restart.test.js.map +0 -1
- package/dist/test/placed-reaction-baseline.test.d.ts +0 -2
- package/dist/test/placed-reaction-baseline.test.d.ts.map +0 -1
- package/dist/test/placed-reaction-baseline.test.js +0 -312
- package/dist/test/placed-reaction-baseline.test.js.map +0 -1
- package/dist/test/present-while-acting.test.d.ts +0 -19
- package/dist/test/present-while-acting.test.d.ts.map +0 -1
- package/dist/test/present-while-acting.test.js +0 -706
- package/dist/test/present-while-acting.test.js.map +0 -1
- package/dist/test/prose-segments.test.d.ts +0 -2
- package/dist/test/prose-segments.test.d.ts.map +0 -1
- package/dist/test/prose-segments.test.js +0 -52
- package/dist/test/prose-segments.test.js.map +0 -1
- package/dist/test/prose-stream-router.test.d.ts +0 -2
- package/dist/test/prose-stream-router.test.d.ts.map +0 -1
- package/dist/test/prose-stream-router.test.js +0 -171
- package/dist/test/prose-stream-router.test.js.map +0 -1
- package/dist/test/provider-acceleration-cooldown.test.d.ts +0 -2
- package/dist/test/provider-acceleration-cooldown.test.d.ts.map +0 -1
- package/dist/test/provider-acceleration-cooldown.test.js +0 -435
- package/dist/test/provider-acceleration-cooldown.test.js.map +0 -1
- package/dist/test/provider-waits.test.d.ts +0 -2
- package/dist/test/provider-waits.test.d.ts.map +0 -1
- package/dist/test/provider-waits.test.js +0 -2196
- package/dist/test/provider-waits.test.js.map +0 -1
- package/dist/test/puppet-tool-call.test.d.ts +0 -2
- package/dist/test/puppet-tool-call.test.d.ts.map +0 -1
- package/dist/test/puppet-tool-call.test.js +0 -224
- package/dist/test/puppet-tool-call.test.js.map +0 -1
- package/dist/test/puppet-turn-reservation.test.d.ts +0 -2
- package/dist/test/puppet-turn-reservation.test.d.ts.map +0 -1
- package/dist/test/puppet-turn-reservation.test.js +0 -139
- package/dist/test/puppet-turn-reservation.test.js.map +0 -1
- package/dist/test/reaction-baseline-platform.test.d.ts +0 -2
- package/dist/test/reaction-baseline-platform.test.d.ts.map +0 -1
- package/dist/test/reaction-baseline-platform.test.js +0 -124
- package/dist/test/reaction-baseline-platform.test.js.map +0 -1
- package/dist/test/record-journal.test.d.ts +0 -6
- package/dist/test/record-journal.test.d.ts.map +0 -1
- package/dist/test/record-journal.test.js +0 -269
- package/dist/test/record-journal.test.js.map +0 -1
- package/dist/test/refusal-reaction-baseline.test.d.ts +0 -2
- package/dist/test/refusal-reaction-baseline.test.d.ts.map +0 -1
- package/dist/test/refusal-reaction-baseline.test.js +0 -81
- package/dist/test/refusal-reaction-baseline.test.js.map +0 -1
- package/dist/test/refusal-rewind.test.d.ts +0 -2
- package/dist/test/refusal-rewind.test.d.ts.map +0 -1
- package/dist/test/refusal-rewind.test.js +0 -108
- package/dist/test/refusal-rewind.test.js.map +0 -1
- package/dist/test/refusal-single-call.test.d.ts +0 -2
- package/dist/test/refusal-single-call.test.d.ts.map +0 -1
- package/dist/test/refusal-single-call.test.js +0 -177
- package/dist/test/refusal-single-call.test.js.map +0 -1
- package/dist/test/release-changelog.test.d.ts +0 -2
- package/dist/test/release-changelog.test.d.ts.map +0 -1
- package/dist/test/release-changelog.test.js +0 -159
- package/dist/test/release-changelog.test.js.map +0 -1
- package/dist/test/same-round-think-policy.test.d.ts +0 -2
- package/dist/test/same-round-think-policy.test.d.ts.map +0 -1
- package/dist/test/same-round-think-policy.test.js +0 -853
- package/dist/test/same-round-think-policy.test.js.map +0 -1
- package/dist/test/save-recent-image.test.d.ts +0 -26
- package/dist/test/save-recent-image.test.d.ts.map +0 -1
- package/dist/test/save-recent-image.test.js +0 -974
- package/dist/test/save-recent-image.test.js.map +0 -1
- package/dist/test/shutdown-errors.test.d.ts +0 -2
- package/dist/test/shutdown-errors.test.d.ts.map +0 -1
- package/dist/test/shutdown-errors.test.js +0 -662
- package/dist/test/shutdown-errors.test.js.map +0 -1
- package/dist/test/silent-heartbeat-rows.test.d.ts +0 -2
- package/dist/test/silent-heartbeat-rows.test.d.ts.map +0 -1
- package/dist/test/silent-heartbeat-rows.test.js +0 -451
- package/dist/test/silent-heartbeat-rows.test.js.map +0 -1
- package/dist/test/silent-heartbeat.test.d.ts +0 -2
- package/dist/test/silent-heartbeat.test.d.ts.map +0 -1
- package/dist/test/silent-heartbeat.test.js +0 -165
- package/dist/test/silent-heartbeat.test.js.map +0 -1
- package/dist/test/state-scaling-e2e.test.d.ts +0 -22
- package/dist/test/state-scaling-e2e.test.d.ts.map +0 -1
- package/dist/test/state-scaling-e2e.test.js +0 -206
- package/dist/test/state-scaling-e2e.test.js.map +0 -1
- package/dist/test/state-scaling.test.d.ts +0 -12
- package/dist/test/state-scaling.test.d.ts.map +0 -1
- package/dist/test/state-scaling.test.js +0 -251
- package/dist/test/state-scaling.test.js.map +0 -1
- package/dist/test/stop-with-work-in-flight.test.d.ts +0 -23
- package/dist/test/stop-with-work-in-flight.test.d.ts.map +0 -1
- package/dist/test/stop-with-work-in-flight.test.js +0 -672
- package/dist/test/stop-with-work-in-flight.test.js.map +0 -1
- package/dist/test/surgery-marks-receipt.test.d.ts +0 -2
- package/dist/test/surgery-marks-receipt.test.d.ts.map +0 -1
- package/dist/test/surgery-marks-receipt.test.js +0 -540
- package/dist/test/surgery-marks-receipt.test.js.map +0 -1
- package/dist/test/thinking-token-stamp.test.d.ts +0 -12
- package/dist/test/thinking-token-stamp.test.d.ts.map +0 -1
- package/dist/test/thinking-token-stamp.test.js +0 -129
- package/dist/test/thinking-token-stamp.test.js.map +0 -1
- package/dist/test/timezone.test.d.ts +0 -2
- package/dist/test/timezone.test.d.ts.map +0 -1
- package/dist/test/timezone.test.js +0 -18
- package/dist/test/timezone.test.js.map +0 -1
- package/dist/test/tool-batch-hook.test.d.ts +0 -8
- package/dist/test/tool-batch-hook.test.d.ts.map +0 -1
- package/dist/test/tool-batch-hook.test.js +0 -275
- package/dist/test/tool-batch-hook.test.js.map +0 -1
- package/dist/test/tool-policy.test.d.ts +0 -2
- package/dist/test/tool-policy.test.d.ts.map +0 -1
- package/dist/test/tool-policy.test.js +0 -51
- package/dist/test/tool-policy.test.js.map +0 -1
- package/dist/test/tool-presentation.test.d.ts +0 -2
- package/dist/test/tool-presentation.test.d.ts.map +0 -1
- package/dist/test/tool-presentation.test.js +0 -268
- package/dist/test/tool-presentation.test.js.map +0 -1
- package/dist/test/tool-result-content.test.d.ts +0 -2
- package/dist/test/tool-result-content.test.d.ts.map +0 -1
- package/dist/test/tool-result-content.test.js +0 -37
- package/dist/test/tool-result-content.test.js.map +0 -1
- package/dist/test/tool-result-guard.test.d.ts +0 -2
- package/dist/test/tool-result-guard.test.d.ts.map +0 -1
- package/dist/test/tool-result-guard.test.js +0 -3607
- package/dist/test/tool-result-guard.test.js.map +0 -1
- package/dist/test/tool-result-history.test.d.ts +0 -7
- package/dist/test/tool-result-history.test.d.ts.map +0 -1
- package/dist/test/tool-result-history.test.js +0 -156
- package/dist/test/tool-result-history.test.js.map +0 -1
- package/dist/test/tool-result-spill.test.d.ts +0 -13
- package/dist/test/tool-result-spill.test.d.ts.map +0 -1
- package/dist/test/tool-result-spill.test.js +0 -586
- package/dist/test/tool-result-spill.test.js.map +0 -1
- package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts +0 -2
- package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts.map +0 -1
- package/dist/test/tool-wrapper-prose-guard-integration.test.js +0 -485
- package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +0 -1
- package/dist/test/tool-wrapper-prose-guard.test.d.ts +0 -2
- package/dist/test/tool-wrapper-prose-guard.test.d.ts.map +0 -1
- package/dist/test/tool-wrapper-prose-guard.test.js +0 -34
- package/dist/test/tool-wrapper-prose-guard.test.js.map +0 -1
- package/dist/test/tools-observe-parser-conformance.test.d.ts +0 -2
- package/dist/test/tools-observe-parser-conformance.test.d.ts.map +0 -1
- package/dist/test/tools-observe-parser-conformance.test.js +0 -129
- package/dist/test/tools-observe-parser-conformance.test.js.map +0 -1
- package/dist/test/trunk-channel-routing.test.d.ts +0 -19
- package/dist/test/trunk-channel-routing.test.d.ts.map +0 -1
- package/dist/test/trunk-channel-routing.test.js +0 -329
- package/dist/test/trunk-channel-routing.test.js.map +0 -1
- package/dist/test/tune-out-e2e.test.d.ts +0 -2
- package/dist/test/tune-out-e2e.test.d.ts.map +0 -1
- package/dist/test/tune-out-e2e.test.js +0 -142
- package/dist/test/tune-out-e2e.test.js.map +0 -1
- package/dist/test/tune-out-gate-composition.test.d.ts +0 -2
- package/dist/test/tune-out-gate-composition.test.d.ts.map +0 -1
- package/dist/test/tune-out-gate-composition.test.js +0 -221
- package/dist/test/tune-out-gate-composition.test.js.map +0 -1
- package/dist/test/tune-out-lifecycle.test.d.ts +0 -2
- package/dist/test/tune-out-lifecycle.test.d.ts.map +0 -1
- package/dist/test/tune-out-lifecycle.test.js +0 -121
- package/dist/test/tune-out-lifecycle.test.js.map +0 -1
- package/dist/test/turn-trigger-provenance.test.d.ts +0 -2
- package/dist/test/turn-trigger-provenance.test.d.ts.map +0 -1
- package/dist/test/turn-trigger-provenance.test.js +0 -137
- package/dist/test/turn-trigger-provenance.test.js.map +0 -1
- package/dist/test/unconvertible-tool-result.test.d.ts +0 -2
- package/dist/test/unconvertible-tool-result.test.d.ts.map +0 -1
- package/dist/test/unconvertible-tool-result.test.js +0 -358
- package/dist/test/unconvertible-tool-result.test.js.map +0 -1
- package/dist/test/usage-accounting.test.d.ts +0 -2
- package/dist/test/usage-accounting.test.d.ts.map +0 -1
- package/dist/test/usage-accounting.test.js +0 -13
- package/dist/test/usage-accounting.test.js.map +0 -1
- package/dist/test/usage-tracker.test.d.ts +0 -2
- package/dist/test/usage-tracker.test.d.ts.map +0 -1
- package/dist/test/usage-tracker.test.js +0 -129
- package/dist/test/usage-tracker.test.js.map +0 -1
- package/dist/test/utils-surface.test.d.ts +0 -2
- package/dist/test/utils-surface.test.d.ts.map +0 -1
- package/dist/test/utils-surface.test.js +0 -145
- package/dist/test/utils-surface.test.js.map +0 -1
- package/dist/test/workspace-branch-continuation.test.d.ts +0 -15
- package/dist/test/workspace-branch-continuation.test.d.ts.map +0 -1
- package/dist/test/workspace-branch-continuation.test.js +0 -183
- package/dist/test/workspace-branch-continuation.test.js.map +0 -1
- package/dist/test/workspace-materialize-freshness.test.d.ts +0 -16
- package/dist/test/workspace-materialize-freshness.test.d.ts.map +0 -1
- package/dist/test/workspace-materialize-freshness.test.js +0 -252
- package/dist/test/workspace-materialize-freshness.test.js.map +0 -1
- package/dist/test/workspace-path-containment.test.d.ts +0 -19
- package/dist/test/workspace-path-containment.test.d.ts.map +0 -1
- package/dist/test/workspace-path-containment.test.js +0 -91
- package/dist/test/workspace-path-containment.test.js.map +0 -1
- package/dist/test/workspace-read-file-from-disk.test.d.ts +0 -17
- package/dist/test/workspace-read-file-from-disk.test.d.ts.map +0 -1
- package/dist/test/workspace-read-file-from-disk.test.js +0 -174
- package/dist/test/workspace-read-file-from-disk.test.js.map +0 -1
- package/dist/test/workspace-read-image.test.d.ts +0 -2
- package/dist/test/workspace-read-image.test.d.ts.map +0 -1
- package/dist/test/workspace-read-image.test.js +0 -597
- package/dist/test/workspace-read-image.test.js.map +0 -1
- package/dist/test/workspace-read-text.test.d.ts +0 -2
- package/dist/test/workspace-read-text.test.d.ts.map +0 -1
- package/dist/test/workspace-read-text.test.js +0 -101
- package/dist/test/workspace-read-text.test.js.map +0 -1
- package/dist/test/workspace-restore-order.test.d.ts +0 -16
- package/dist/test/workspace-restore-order.test.d.ts.map +0 -1
- package/dist/test/workspace-restore-order.test.js +0 -140
- package/dist/test/workspace-restore-order.test.js.map +0 -1
- package/dist/test/workspace-three-way.test.d.ts +0 -12
- package/dist/test/workspace-three-way.test.d.ts.map +0 -1
- package/dist/test/workspace-three-way.test.js +0 -3194
- package/dist/test/workspace-three-way.test.js.map +0 -1
- package/dist/test/workspace-watcher.test.d.ts +0 -2
- package/dist/test/workspace-watcher.test.d.ts.map +0 -1
- package/dist/test/workspace-watcher.test.js +0 -281
- package/dist/test/workspace-watcher.test.js.map +0 -1
- package/dist/test/xml-round-persist.test.d.ts +0 -19
- package/dist/test/xml-round-persist.test.d.ts.map +0 -1
- package/dist/test/xml-round-persist.test.js +0 -235
- package/dist/test/xml-round-persist.test.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -54,20 +54,26 @@ import {
|
|
|
54
54
|
} from './disk-agreement.js';
|
|
55
55
|
import type { BranchIntent, BranchIntents, ConflictKind, ConflictRecord, DiskCounterpart } from './branch-intent.js';
|
|
56
56
|
import {
|
|
57
|
+
IGNORED,
|
|
57
58
|
type MountView,
|
|
59
|
+
OTHER_MOUNT,
|
|
58
60
|
ROOT_REPLACED,
|
|
59
61
|
type Walk,
|
|
60
62
|
fingerprintVouches,
|
|
63
|
+
ignoreBeneath,
|
|
64
|
+
inOtherMount,
|
|
61
65
|
locateRoot,
|
|
62
66
|
looksBinary,
|
|
63
67
|
observePath,
|
|
68
|
+
passedOverAt,
|
|
69
|
+
passedOverBetween,
|
|
64
70
|
provenAbsent,
|
|
65
71
|
readHead,
|
|
66
72
|
readPath,
|
|
67
73
|
sniffImageMime,
|
|
68
74
|
walkScope,
|
|
69
75
|
} from './observe.js';
|
|
70
|
-
import { EffectFailed, type Expect, chainOf, syncDirectories, unlinkContained, writeContained } from './effects.js';
|
|
76
|
+
import { EffectFailed, type Expect, chainOf, chmodContained, narrowedTo, syncDirectories, unlinkContained, writeContained } from './effects.js';
|
|
71
77
|
|
|
72
78
|
export type EntryState =
|
|
73
79
|
| 'synced'
|
|
@@ -106,8 +112,11 @@ export interface PathReport {
|
|
|
106
112
|
export type TreeOp = 'created' | 'modified' | 'deleted';
|
|
107
113
|
|
|
108
114
|
export interface PassResult {
|
|
109
|
-
/**
|
|
110
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Tree changes adopted from disk. `modeOnly`: disk's mode was taken in on
|
|
117
|
+
* the same bytes, which makes no event.
|
|
118
|
+
*/
|
|
119
|
+
ops: Array<{ path: string; op: TreeOp; modeOnly?: true }>;
|
|
111
120
|
/** What a path sync gave up of the workspace's own; `op` is what it did to the
|
|
112
121
|
* workspace copy, absent when it changed none (a deletion or conflict given
|
|
113
122
|
* up for a disk copy the workspace can't hold). */
|
|
@@ -120,9 +129,11 @@ export interface PassResult {
|
|
|
120
129
|
incomplete: Array<{ path: string; reason: string }>;
|
|
121
130
|
/**
|
|
122
131
|
* What the walk passed over by design and reported nowhere else, counted by
|
|
123
|
-
* reason:
|
|
124
|
-
*
|
|
125
|
-
*
|
|
132
|
+
* reason: another mount's directory nested in this one, entries the mount's
|
|
133
|
+
* ignore list covers, and entries with an editor's swap or backup file
|
|
134
|
+
* name, when nothing beneath them is tracked (a tracked one is reported
|
|
135
|
+
* under `incomplete`). Such a directory counts
|
|
136
|
+
* once, everything in it included. Only listings the walk
|
|
126
137
|
* kept count: a directory changed during the walk, or one past the file
|
|
127
138
|
* cap, is reported under `incomplete` instead.
|
|
128
139
|
*/
|
|
@@ -152,6 +163,22 @@ export interface MountRuntime {
|
|
|
152
163
|
treeStateId: string;
|
|
153
164
|
intents: BranchIntents;
|
|
154
165
|
readOnly: boolean;
|
|
166
|
+
/** The most entries one walk of the mount takes, files and the other entries that aren't directories (`MountConfig.walkCap`). */
|
|
167
|
+
walkCap: number;
|
|
168
|
+
/**
|
|
169
|
+
* Whether the mount runs a catch-up scan (`watch: 'always'`, at start and
|
|
170
|
+
* when its watcher reattaches), which takes turns with its whole-mount
|
|
171
|
+
* syncs.
|
|
172
|
+
*/
|
|
173
|
+
catchUpScan: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Whether a file's mode is observed, taken in and applied: the module's,
|
|
176
|
+
* from the platform. Windows reports only 0o666 or 0o444, so there a store
|
|
177
|
+
* written on a POSIX host would lose its executable bits at the first look:
|
|
178
|
+
* modes go unobserved, as before they were recorded, and the store and the
|
|
179
|
+
* evidence keep their own for the next POSIX host.
|
|
180
|
+
*/
|
|
181
|
+
modesObserved: boolean;
|
|
155
182
|
}
|
|
156
183
|
|
|
157
184
|
export type Scope =
|
|
@@ -163,11 +190,21 @@ export interface PassOptions {
|
|
|
163
190
|
rehash?: boolean;
|
|
164
191
|
/** Paths whose disk state is adopted explicitly (a path sync). */
|
|
165
192
|
adopt?: (path: string) => boolean;
|
|
166
|
-
/** Walk cap for directory scopes. */
|
|
193
|
+
/** Walk cap for directory scopes; the mount's `walkCap` when absent. */
|
|
167
194
|
cap?: number;
|
|
168
195
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
196
|
+
* A recursive directory pass of a sync or a catch-up scan: its walk resumes
|
|
197
|
+
* after the last entry the previous one of this scope took, and records
|
|
198
|
+
* where it stopped, so repeated passes cover a scope larger than the cap.
|
|
199
|
+
* A listing's pass, and an agent-action scan's, start at their scope's
|
|
200
|
+
* start and record no place.
|
|
201
|
+
*/
|
|
202
|
+
resume?: boolean;
|
|
203
|
+
/**
|
|
204
|
+
* The scope was named, by a path sync or by a file tool's pass on the path
|
|
205
|
+
* it acts on: what the mount passes over by design that covers the scope,
|
|
206
|
+
* at it or above it, doesn't apply there (see walkScope). A listing's scope
|
|
207
|
+
* keeps it.
|
|
171
208
|
*/
|
|
172
209
|
named?: boolean;
|
|
173
210
|
/**
|
|
@@ -190,6 +227,12 @@ type DiskFact =
|
|
|
190
227
|
hash: string;
|
|
191
228
|
size: number;
|
|
192
229
|
mtimeMs: number;
|
|
230
|
+
/**
|
|
231
|
+
* Permission bits (`st_mode & 0o777`); none where modes go unobserved,
|
|
232
|
+
* as on Windows, which keeps no POSIX modes (MountRuntime's
|
|
233
|
+
* `modesObserved`): nothing there is taken in or applied.
|
|
234
|
+
*/
|
|
235
|
+
mode?: number;
|
|
193
236
|
fp: Fingerprint | null;
|
|
194
237
|
/** Text within limits, with its bytes: what disk→store sync may store. */
|
|
195
238
|
ingestible: Buffer | null;
|
|
@@ -210,7 +253,63 @@ function diskCandidate(d: DiskFact): Candidate | null {
|
|
|
210
253
|
|
|
211
254
|
function agreedFromDisk(d: DiskFact): Agreed {
|
|
212
255
|
if (d.kind !== 'file') return { kind: 'absent' };
|
|
213
|
-
return { kind: 'content', hash: d.hash, size: d.size, ...(d.fp ? { fp: d.fp } : {}) };
|
|
256
|
+
return { kind: 'content', hash: d.hash, size: d.size, ...(d.mode !== undefined ? { mode: d.mode } : {}), ...(d.fp ? { fp: d.fp } : {}) };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** The file-type bits git records with a regular file's mode. */
|
|
260
|
+
const REGULAR_FILE = 0o100000;
|
|
261
|
+
|
|
262
|
+
/** A tree entry's mode for permission bits: with the regular-file type bits, as git records one. */
|
|
263
|
+
export function treeMode(perm: number): number {
|
|
264
|
+
return REGULAR_FILE | (perm & 0o777);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* The mode a tree entry takes for disk's permission bits, or, where disk's
|
|
269
|
+
* aren't observed, the entry's own. With neither, the placeholder an entry
|
|
270
|
+
* without a recorded mode has (see entryMode).
|
|
271
|
+
*/
|
|
272
|
+
function treeModeOf(perm: number | undefined, s: StoreSide | null): { mode: number } {
|
|
273
|
+
if (perm !== undefined) return { mode: treeMode(perm) };
|
|
274
|
+
if (s?.mode !== undefined) return { mode: treeMode(s.mode) };
|
|
275
|
+
return { mode: 0o644 };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* The permission bits a tree entry records, or undefined when it records
|
|
280
|
+
* none. An entry without the file-type bits was written before modes were
|
|
281
|
+
* recorded, which wrote 0o644 whatever disk had: a placeholder, not a mode.
|
|
282
|
+
*/
|
|
283
|
+
export function entryMode(mode: number): number | undefined {
|
|
284
|
+
return (mode & 0o170000) === REGULAR_FILE ? mode & 0o777 : undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** The store's side of a path: its entry's bytes, and its mode when recorded. */
|
|
288
|
+
type StoreSide = { hash: string; size: number; mode?: number };
|
|
289
|
+
|
|
290
|
+
/** The store's side of a tree entry. */
|
|
291
|
+
export function storeSide(entry: { blobHash: string; size: number; mode: number } | null): StoreSide | null {
|
|
292
|
+
if (!entry) return null;
|
|
293
|
+
const mode = entryMode(entry.mode);
|
|
294
|
+
return { hash: entry.blobHash, size: entry.size, ...(mode !== undefined ? { mode } : {}) };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Where disk and the store hold the same bytes, what their modes ask for.
|
|
299
|
+
* A mode the store doesn't record agrees with any, and takes disk's
|
|
300
|
+
* (`fill`). The store's mode goes to disk (`push`) only where the evidence
|
|
301
|
+
* shows disk still has the mode they last agreed on; otherwise disk's mode
|
|
302
|
+
* is taken in as a change (`adopt`): disk changed it, or nothing says which
|
|
303
|
+
* side did, and an unknown never moves disk.
|
|
304
|
+
*/
|
|
305
|
+
function modeAction(disk: number, store: number | undefined, agreed: number | undefined): 'fill' | 'adopt' | 'push' | undefined {
|
|
306
|
+
if (store === undefined) return 'fill';
|
|
307
|
+
if (store === disk) return undefined;
|
|
308
|
+
return agreed !== undefined && agreed === disk ? 'push' : 'adopt';
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function octal(mode: number): string {
|
|
312
|
+
return `0${mode.toString(8).padStart(3, '0')}`;
|
|
214
313
|
}
|
|
215
314
|
|
|
216
315
|
function inScope(path: string, scope: Scope): boolean {
|
|
@@ -237,7 +336,7 @@ async function learnDisk(
|
|
|
237
336
|
const p = tracked.p;
|
|
238
337
|
const trusted = (fp: { dev: string }) => mount.ctimeTrustedDev !== null && fp.dev === mount.ctimeTrustedDev;
|
|
239
338
|
if (!opts.rehash && p?.kind === 'content' && fingerprintVouches(p.fp, seen.fp, trusted(seen.fp))) {
|
|
240
|
-
return { kind: 'file', hash: p.hash, size: seen.size, mtimeMs: seen.mtimeMs, fp: p.fp ?? null, ingestible: null };
|
|
339
|
+
return { kind: 'file', hash: p.hash, size: seen.size, mtimeMs: seen.mtimeMs, ...(mount.modesObserved ? { mode: seen.mode } : {}), fp: p.fp ?? null, ingestible: null };
|
|
241
340
|
}
|
|
242
341
|
const nothingTracked = p === undefined && !tracked.hasStore && !tracked.hasIntent;
|
|
243
342
|
if (nothingTracked && seen.size > mount.view.maxFileSize) {
|
|
@@ -255,6 +354,7 @@ async function learnDisk(
|
|
|
255
354
|
hash: read.hash,
|
|
256
355
|
size: read.size,
|
|
257
356
|
mtimeMs: read.mtimeMs,
|
|
357
|
+
...(mount.modesObserved ? { mode: read.mode } : {}),
|
|
258
358
|
// A fingerprint is recorded only where it can later vouch for the bytes.
|
|
259
359
|
fp: trusted(read.fp) ? read.fp : null,
|
|
260
360
|
ingestible,
|
|
@@ -280,12 +380,40 @@ interface Verdict {
|
|
|
280
380
|
* is decided from it, and nothing at all is recorded (see module doc).
|
|
281
381
|
*/
|
|
282
382
|
unconfirmed?: true;
|
|
383
|
+
/**
|
|
384
|
+
* With `unconfirmed`: disk's mode is one of the stopped push's own
|
|
385
|
+
* (pushedMode), so the push that completes it gives the store's mode
|
|
386
|
+
* there, as it would where disk has the mode they last agreed on.
|
|
387
|
+
*/
|
|
388
|
+
ownMode?: true;
|
|
389
|
+
/**
|
|
390
|
+
* A path sync's adoption over a push of ours that stopped partway: the
|
|
391
|
+
* mode the tree takes, which is the one the push meant the bytes on disk
|
|
392
|
+
* to have, not the one it left them at. The evidence keeps disk's actual
|
|
393
|
+
* mode, so a later push moves disk to the tree's.
|
|
394
|
+
*/
|
|
395
|
+
diskMode?: number;
|
|
283
396
|
/** A path sync's adoption gives up this workspace state (see decide). */
|
|
284
397
|
discards?: EntryState;
|
|
398
|
+
/**
|
|
399
|
+
* Disk and the store hold the same bytes, and the store's mode is unknown
|
|
400
|
+
* or differs (see modeAction): `fill` and `adopt` record disk's mode on
|
|
401
|
+
* the store's entry, `adopt` as a change; `push` leaves it for a push.
|
|
402
|
+
*/
|
|
403
|
+
mode?: 'fill' | 'adopt' | 'push';
|
|
285
404
|
/**
|
|
286
405
|
* The last confirmed evidence decide resolved for the path, which an
|
|
287
406
|
* adoption's intent carries as its prior: a settled same-branch adoption's
|
|
288
407
|
* outcome, where the recorded P is still that adoption's pending record.
|
|
408
|
+
* Or the evidence a push's narrowing left when it stopped before its
|
|
409
|
+
* bytes, which decide uses without recording it, so the intent stays: a
|
|
410
|
+
* push plans from it and goes on as the stopped push, its intent carrying
|
|
411
|
+
* that push's prior and modes (pushPaths), and a sync that adopts disk's
|
|
412
|
+
* copy carries it as its adoption's prior. An adoption that doesn't land
|
|
413
|
+
* returns to it, as every adoption returns to its prior: the store's
|
|
414
|
+
* change is owed again, and the next push completes it at the store's
|
|
415
|
+
* mode. The old bytes' own mode doesn't survive that, since the adoption's
|
|
416
|
+
* intent replaced the push's.
|
|
289
417
|
*/
|
|
290
418
|
prior?: Agreed;
|
|
291
419
|
}
|
|
@@ -316,14 +444,71 @@ function keepStoreSide(hasStore: boolean, bi: BranchIntent | null): EntryState {
|
|
|
316
444
|
* mutation boundary): a later write of the same content can't pass for the
|
|
317
445
|
* old adoption.
|
|
318
446
|
*/
|
|
319
|
-
function settleAdoption(p: Pending, s:
|
|
447
|
+
function settleAdoption(p: Pending, s: StoreSide | null): Agreed | 'forget' {
|
|
320
448
|
const sc: Candidate = s ? { kind: 'content', hash: s.hash } : ABSENT;
|
|
321
449
|
if (sameCandidate(sc, p.expect)) {
|
|
322
|
-
|
|
450
|
+
// Where modes are observed, the adoption took disk's mode with its bytes,
|
|
451
|
+
// so the entry's mode is the one they agreed on, but for an adoption that
|
|
452
|
+
// took the mode a stopped push of ours meant the old bytes to have: its
|
|
453
|
+
// entry holds that mode while disk holds the push's narrowed one, which a
|
|
454
|
+
// later sync then takes in. Where modes aren't observed, the entry's mode
|
|
455
|
+
// is the store's own, and the evidence records none.
|
|
456
|
+
return p.expect.kind === 'absent'
|
|
457
|
+
? { kind: 'absent' }
|
|
458
|
+
: { kind: 'content', hash: p.expect.hash, size: s!.size, ...(s!.mode !== undefined ? { mode: s!.mode } : {}) };
|
|
323
459
|
}
|
|
324
460
|
return p.prior ?? 'forget';
|
|
325
461
|
}
|
|
326
462
|
|
|
463
|
+
/**
|
|
464
|
+
* What's in the way, and the way out, when disk's shape keeps a push from a
|
|
465
|
+
* path: no push writes a file over a directory or unlinks one, unlinks
|
|
466
|
+
* anything but a regular file or a followed symlink to one, or writes below
|
|
467
|
+
* a file or a symlink the walk won't take (`writeContained`,
|
|
468
|
+
* `unlinkContained`), forced or not. Null for any other reason, which is
|
|
469
|
+
* passed on as it is.
|
|
470
|
+
*/
|
|
471
|
+
function inTheWay(reason: string | undefined, deletion: boolean): string | null {
|
|
472
|
+
// Observation's words for what a deletion never unlinks, which the unlink
|
|
473
|
+
// refuses in the same words.
|
|
474
|
+
if (deletion && (reason === 'not a regular file' || reason === 'a symlink to a directory, not walked')) {
|
|
475
|
+
const what = reason === 'not a regular file'
|
|
476
|
+
? 'a pipe, socket or device at this path, or a symlink to one'
|
|
477
|
+
: 'a symlink to a directory at this path';
|
|
478
|
+
return `disk has ${what}, and no push unlinks it, forced or not — ask whoever runs this host if it should go`;
|
|
479
|
+
}
|
|
480
|
+
if (reason === 'a directory') {
|
|
481
|
+
return deletion
|
|
482
|
+
? 'disk has a directory at this path, and no push unlinks one, forced or not — if it should go, move or remove it on disk'
|
|
483
|
+
: 'disk has a directory at this path, and no push writes a file over one, forced or not — ' +
|
|
484
|
+
'move or remove that directory on disk and materialize again, or write the file under another path';
|
|
485
|
+
}
|
|
486
|
+
// A deletion below a file finds nothing there to unlink, so only a write
|
|
487
|
+
// meets this one.
|
|
488
|
+
if (reason === 'a parent path is not a directory') {
|
|
489
|
+
return 'disk has a file or a symlink where this path needs a directory, and no push writes below one, forced or not — ' +
|
|
490
|
+
'move or remove it on disk and materialize again, or write the file under another path';
|
|
491
|
+
}
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* The forced way out the refusal of a disk copy that couldn't be verified
|
|
497
|
+
* names, if any. A forced push skips the verification, so it gets past the
|
|
498
|
+
* note only where its effect can go on. A file the module can't read (EACCES)
|
|
499
|
+
* is overwritten, or unlinked, where disk lets the workspace write it, or
|
|
500
|
+
* unlink it, which a read's EACCES doesn't show: force is named on that
|
|
501
|
+
* condition. No other note names force: a forced push stops at some (a
|
|
502
|
+
* symlink the mount doesn't follow, what isn't a regular file, a parent it
|
|
503
|
+
* can't use); another writer still at the file ("the file changed while it
|
|
504
|
+
* was read") is answered by materializing again; and the rest aren't known
|
|
505
|
+
* to pass.
|
|
506
|
+
*/
|
|
507
|
+
function forcedWay(note: string | undefined, deletion: boolean, forced: string): string {
|
|
508
|
+
if (note === 'cannot open (EACCES)') return `, or, if disk lets the workspace ${deletion ? 'unlink' : 'write'} the file, ${forced}`;
|
|
509
|
+
return '';
|
|
510
|
+
}
|
|
511
|
+
|
|
327
512
|
/**
|
|
328
513
|
* The synchronous precondition of every workspace mutation: evidence owed a
|
|
329
514
|
* barrier is made durable first, and an adoption left pending on the
|
|
@@ -345,12 +530,30 @@ export function settleAdoptionBeforeMutation(
|
|
|
345
530
|
const p = agreement.get(mount.name, path);
|
|
346
531
|
if (p?.kind !== 'pending' || p.effect !== 'adopt' || p.branchId !== store.currentBranch().id) return;
|
|
347
532
|
const entry = store.treeGet(mount.treeStateId, path);
|
|
348
|
-
const settled = settleAdoption(p, entry ? { hash: entry.blobHash, size: entry.size } : null);
|
|
533
|
+
const settled = settleAdoption(p, entry ? { hash: entry.blobHash, size: entry.size, ...(entryMode(entry.mode) !== undefined ? { mode: entryMode(entry.mode) } : {}) } : null);
|
|
349
534
|
if (settled === 'forget') agreement.forget(mount.name, path);
|
|
350
535
|
else agreement.set(mount.name, path, settled, { afterCommittedState: true });
|
|
351
536
|
agreement.barrier();
|
|
352
537
|
}
|
|
353
538
|
|
|
539
|
+
/**
|
|
540
|
+
* What a push of ours that stopped partway left on disk, read by its own
|
|
541
|
+
* modes (Pending's `modes`): the new bytes at `from & to` or `to` were meant
|
|
542
|
+
* to have `to`; the old bytes at `from & to`, narrower than `from`, still
|
|
543
|
+
* have `from` as theirs. New bytes at `from` are a push's only where
|
|
544
|
+
* `from & to` is `from`: a push that narrows does so before its bytes, so
|
|
545
|
+
* otherwise they were given `from` since. Undefined where disk shows
|
|
546
|
+
* anything else: no push of ours, or a mode set since it stopped.
|
|
547
|
+
*/
|
|
548
|
+
function pushedMode(p: Pending, dc: Candidate, d: DiskFact): number | undefined {
|
|
549
|
+
const m = p.modes;
|
|
550
|
+
if (p.effect !== 'disk' || m === undefined || d.kind !== 'file' || d.mode === undefined) return undefined;
|
|
551
|
+
const narrowed = m.from & m.to;
|
|
552
|
+
if (sameCandidate(dc, p.expect)) return d.mode === narrowed || d.mode === m.to ? m.to : undefined;
|
|
553
|
+
const prior = p.prior ? candidateOf(p.prior) : null;
|
|
554
|
+
return prior && sameCandidate(dc, prior) && narrowed !== m.from && d.mode === narrowed ? m.from : undefined;
|
|
555
|
+
}
|
|
556
|
+
|
|
354
557
|
/**
|
|
355
558
|
* Resolve a pending intent against what was observed. Returns the evidence
|
|
356
559
|
* the rule should use, and whether that resolution should be recorded.
|
|
@@ -358,20 +561,34 @@ export function settleAdoptionBeforeMutation(
|
|
|
358
561
|
function resolvePending(
|
|
359
562
|
p: Pending,
|
|
360
563
|
dc: Candidate,
|
|
361
|
-
s:
|
|
564
|
+
s: StoreSide | null,
|
|
362
565
|
d: DiskFact,
|
|
363
566
|
branchId: string,
|
|
364
|
-
): { p: Physical | undefined; record?: Agreed | Interrupted | 'forget'; uncaptured?: true; unconfirmed?: true } {
|
|
567
|
+
): { p: Physical | undefined; record?: Agreed | Interrupted | 'forget'; uncaptured?: true; unconfirmed?: true; ownMode?: true } {
|
|
365
568
|
const prior = p.prior;
|
|
366
569
|
const priorCandidate = prior ? candidateOf(prior) : null;
|
|
367
570
|
if (p.effect === 'disk') {
|
|
368
571
|
if (sameCandidate(dc, p.expect)) {
|
|
369
572
|
// Our own effect shows on disk, but nothing proves it durable: decided
|
|
370
573
|
// on, never recorded. Only a push whose barriers succeed completes it.
|
|
574
|
+
// Where disk's mode is one of the push's own, the push that completes
|
|
575
|
+
// it gives the store's mode there.
|
|
371
576
|
const shown = p.expect.kind === 'absent' ? ({ kind: 'absent' } as Agreed) : agreedFromDisk(d);
|
|
372
|
-
return { p: shown, unconfirmed: true };
|
|
577
|
+
return { p: shown, unconfirmed: true, ...(pushedMode(p, dc, d) !== undefined ? { ownMode: true as const } : {}) };
|
|
578
|
+
}
|
|
579
|
+
if (priorCandidate && sameCandidate(dc, priorCandidate)) {
|
|
580
|
+
// The push narrowed the mode and stopped before its bytes: disk's mode
|
|
581
|
+
// is the push's, so the rule decides from it, and the next push writes
|
|
582
|
+
// the bytes at it and then widens to the store's. The old bytes are
|
|
583
|
+
// never given the store's mode. Nothing is recorded: the intent, with
|
|
584
|
+
// the modes that map disk's narrowed one back to the old bytes' own,
|
|
585
|
+
// stays, carried by a push that goes on with it (pushPaths), until a
|
|
586
|
+
// push completes it or a sync adopts disk's copy.
|
|
587
|
+
if (prior!.kind === 'content' && d.kind === 'file' && pushedMode(p, dc, d) !== undefined) {
|
|
588
|
+
return { p: { ...prior!, mode: d.mode } };
|
|
589
|
+
}
|
|
590
|
+
return { p: prior!, record: prior! };
|
|
373
591
|
}
|
|
374
|
-
if (priorCandidate && sameCandidate(dc, priorCandidate)) return { p: prior!, record: prior! };
|
|
375
592
|
// A write whose prior was unknown left no file: it never created one (a
|
|
376
593
|
// partial write leaves a file behind), so P is as unknown as before.
|
|
377
594
|
if (!prior && dc.kind === 'absent' && p.expect.kind === 'content') return { p: undefined, record: 'forget' };
|
|
@@ -402,13 +619,17 @@ function resolvePending(
|
|
|
402
619
|
* An explicit path sync: the store takes disk's state, whatever the evidence
|
|
403
620
|
* — a pending push's included. That chooses disk; it doesn't certify the push.
|
|
404
621
|
*/
|
|
405
|
-
function adoptDisk(d: Extract<DiskFact, { kind: 'absent' | 'file' }>, s:
|
|
622
|
+
function adoptDisk(d: Extract<DiskFact, { kind: 'absent' | 'file' }>, s: StoreSide | null): Verdict {
|
|
406
623
|
if (d.kind === 'absent') {
|
|
407
624
|
return { state: 'synced', ...(s ? { adopt: 'remove' as const, op: 'deleted' as const } : {}), p: { kind: 'absent' }, intent: {} };
|
|
408
625
|
}
|
|
409
626
|
// Disk agreeing with the store is disk's state already, whatever the file
|
|
410
|
-
// is: a binary or oversize file the workspace holds stays held.
|
|
411
|
-
|
|
627
|
+
// is: a binary or oversize file the workspace holds stays held. Its mode
|
|
628
|
+
// is disk's too.
|
|
629
|
+
if (s && s.hash === d.hash) {
|
|
630
|
+
const mode = d.mode === undefined ? undefined : s.mode === undefined ? 'fill' as const : s.mode !== d.mode ? 'adopt' as const : undefined;
|
|
631
|
+
return { state: 'synced', p: agreedFromDisk(d), intent: {}, ...(mode ? { mode } : {}), ...(mode === 'adopt' ? { op: 'modified' as const } : {}) };
|
|
632
|
+
}
|
|
412
633
|
if (d.ingestible) return { state: 'synced', adopt: 'ingest', op: s ? 'modified' : 'created', p: agreedFromDisk(d), intent: {} };
|
|
413
634
|
return { state: 'disk-only', ...(s ? { adopt: 'drop' as const, op: 'deleted' as const } : {}), p: 'forget', intent: {} };
|
|
414
635
|
}
|
|
@@ -416,7 +637,7 @@ function adoptDisk(d: Extract<DiskFact, { kind: 'absent' | 'file' }>, s: { hash:
|
|
|
416
637
|
/** The rule (see module doc). Pure. */
|
|
417
638
|
function decide(
|
|
418
639
|
d: DiskFact,
|
|
419
|
-
s:
|
|
640
|
+
s: StoreSide | null,
|
|
420
641
|
pIn: Physical | undefined,
|
|
421
642
|
bi: BranchIntent | null,
|
|
422
643
|
branchId: string,
|
|
@@ -445,12 +666,20 @@ function decide(
|
|
|
445
666
|
if (adopt) {
|
|
446
667
|
// What a path sync gives up is exactly what a sync without a path would
|
|
447
668
|
// have kept of the workspace's own (a draft, a deletion, a conflict's
|
|
448
|
-
// workspace side), and the verdict says which.
|
|
449
|
-
|
|
669
|
+
// workspace side), and the verdict says which. Where a push of ours
|
|
670
|
+
// stopped partway, disk's mode is the push's: the store takes the one
|
|
671
|
+
// the push meant those bytes to have, and the evidence keeps disk's, so
|
|
672
|
+
// a later push moves disk there.
|
|
673
|
+
const meant = pIn?.kind === 'pending' ? pushedMode(pIn, dc, d) : undefined;
|
|
674
|
+
const taken = adoptDisk(meant === undefined ? d : { ...d, mode: meant } as typeof d, s);
|
|
675
|
+
if (meant !== undefined) {
|
|
676
|
+
if (taken.p !== undefined && taken.p !== 'forget' && taken.p.kind === 'content') taken.p = agreedFromDisk(d);
|
|
677
|
+
if (taken.adopt === 'ingest' || taken.mode !== undefined) taken.diskMode = meant;
|
|
678
|
+
}
|
|
450
679
|
// A give-up is an adoption, or the intent it clears: a deletion or a
|
|
451
680
|
// conflict given up for a disk copy the workspace can't hold changes no
|
|
452
681
|
// tree entry, and is listed all the same.
|
|
453
|
-
if (taken.adopt || (bi && taken.intent !== undefined)) {
|
|
682
|
+
if (taken.adopt || taken.mode === 'adopt' || (bi && taken.intent !== undefined)) {
|
|
454
683
|
const kept = decide(d, s, pIn, bi, branchId, false);
|
|
455
684
|
if (WORKSPACE_KEPT.has(kept.state)) taken.discards = kept.state;
|
|
456
685
|
if (taken.adopt && kept.prior) taken.prior = kept.prior;
|
|
@@ -462,6 +691,9 @@ function decide(
|
|
|
462
691
|
// verdict below records newer evidence.
|
|
463
692
|
let p = pIn;
|
|
464
693
|
let resolution: Agreed | Interrupted | 'forget' | undefined;
|
|
694
|
+
// Evidence a resolution decides from without recording it: a stopped
|
|
695
|
+
// push's narrowing, whose intent stays (resolvePending).
|
|
696
|
+
let unrecorded: Agreed | undefined;
|
|
465
697
|
let uncaptured = false;
|
|
466
698
|
if (p?.kind === 'pending') {
|
|
467
699
|
const resolved = resolvePending(p, dc, s, d, branchId);
|
|
@@ -470,13 +702,18 @@ function decide(
|
|
|
470
702
|
// follows from that, and nothing is recorded — no completion, no
|
|
471
703
|
// agreement, no branch-intent change — until a push confirms it.
|
|
472
704
|
const state: EntryState = sameCandidate(dc, sc) ? 'synced' : bi?.conflict ? 'conflict' : keepStoreSide(s !== null, bi);
|
|
473
|
-
return {
|
|
705
|
+
return {
|
|
706
|
+
state, note: UNCONFIRMED_NOTE, unconfirmed: true,
|
|
707
|
+
...(resolved.ownMode ? { ownMode: true as const } : {}),
|
|
708
|
+
};
|
|
474
709
|
}
|
|
475
710
|
p = resolved.p;
|
|
476
711
|
resolution = resolved.record;
|
|
712
|
+
if (resolution === undefined && p !== undefined && (p.kind === 'content' || p.kind === 'absent')) unrecorded = p;
|
|
477
713
|
uncaptured = resolved.uncaptured === true;
|
|
478
714
|
}
|
|
479
|
-
const verdict = (v: Verdict): Verdict => (resolution && v.p === undefined ? { ...v, p: resolution }
|
|
715
|
+
const verdict = (v: Verdict): Verdict => (resolution && v.p === undefined ? { ...v, p: resolution }
|
|
716
|
+
: unrecorded && v.p === undefined && v.prior === undefined ? { ...v, prior: unrecorded } : v);
|
|
480
717
|
|
|
481
718
|
// Agreement settles everything, a recorded conflict included.
|
|
482
719
|
if (sameCandidate(dc, sc)) {
|
|
@@ -492,11 +729,23 @@ function decide(
|
|
|
492
729
|
const recorded = p?.kind === 'content' ? p : null;
|
|
493
730
|
// Re-record only when P says something else, or a fresh fingerprint can
|
|
494
731
|
// replace one that no longer vouches. A fresh one exists only where it's
|
|
495
|
-
// trusted, so both are judged as trusted here.
|
|
496
|
-
|
|
732
|
+
// trusted, so both are judged as trusted here. Evidence a resolution left
|
|
733
|
+
// unrecorded, a stopped push's narrowing, is stale too: the agreement
|
|
734
|
+
// replaces that push's intent, and the mode rule gives the store's mode.
|
|
735
|
+
const stale = unrecorded !== undefined || !recorded || recorded.hash !== agreed.hash || recorded.size !== agreed.size || recorded.mode !== agreed.mode ||
|
|
497
736
|
(agreed.fp !== undefined && fingerprintVouches(agreed.fp, agreed.fp, true) &&
|
|
498
737
|
!fingerprintVouches(recorded.fp, agreed.fp, true));
|
|
499
|
-
|
|
738
|
+
const mode = agreed.mode === undefined ? undefined : modeAction(agreed.mode, s!.mode, recorded?.mode);
|
|
739
|
+
if (mode === 'push') {
|
|
740
|
+
return verdict({
|
|
741
|
+
state: 'workspace-draft',
|
|
742
|
+
mode,
|
|
743
|
+
note: `the workspace's mode (${octal(s!.mode!)}) differs from disk's (${octal(agreed.mode!)}): materialize to apply it`,
|
|
744
|
+
...(stale ? { p: agreed } : {}),
|
|
745
|
+
...settles,
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
return verdict({ state: 'synced', ...(mode ? { mode } : {}), ...(mode === 'adopt' ? { op: 'modified' as const } : {}), ...(stale ? { p: agreed } : {}), ...settles });
|
|
500
749
|
}
|
|
501
750
|
|
|
502
751
|
// A recorded conflict stays until it is resolved explicitly or disk and store converge.
|
|
@@ -618,10 +867,16 @@ async function gather(store: JsStore, agreement: DiskAgreement, mount: MountRunt
|
|
|
618
867
|
for (const [p] of agreement.paths(mount.name, scope.dir)) if (inScope(p, scope)) candidates.add(p);
|
|
619
868
|
for (const [p] of mount.intents.list(scope.dir)) if (inScope(p, scope)) candidates.add(p);
|
|
620
869
|
if (rootUnavailable === null) {
|
|
621
|
-
|
|
870
|
+
const resumeAfter = opts.resume ? agreement.walkResumesAfter(mount.name, scope.dir) : undefined;
|
|
871
|
+
walk = await walkScope(mount.view, mount.rootReal, scope.dir, scope.recursive, opts.cap ?? mount.walkCap, {
|
|
872
|
+
named: opts.named,
|
|
873
|
+
...(resumeAfter !== undefined ? { resumeAfter } : {}),
|
|
874
|
+
});
|
|
622
875
|
for (const f of walk.files) if (inScope(f, scope)) candidates.add(f);
|
|
623
|
-
//
|
|
624
|
-
|
|
876
|
+
// The other entries it took (a link, a socket…). Ignored names, and the
|
|
877
|
+
// entries the cap left, are listed only when something tracks them
|
|
878
|
+
// (above).
|
|
879
|
+
for (const p of walk.othersTaken) if (inScope(p, scope)) candidates.add(p);
|
|
625
880
|
for (const d of walk.dirs) if (inScope(d, { kind: 'dir', dir: scope.dir, recursive: false })) gathered.dirs.push(d);
|
|
626
881
|
}
|
|
627
882
|
}
|
|
@@ -633,18 +888,54 @@ async function gather(store: JsStore, agreement: DiskAgreement, mount: MountRunt
|
|
|
633
888
|
gathered.facts.set(path, { kind: 'unobserved', reason: rootUnavailable });
|
|
634
889
|
continue;
|
|
635
890
|
}
|
|
891
|
+
// What the mount passes over by design stays unobserved by a pass that
|
|
892
|
+
// names no path, however it reaches it, so no listing's absence proves a
|
|
893
|
+
// copy there gone. In another mount's directory, no pass of this mount
|
|
894
|
+
// observes anything, named or not.
|
|
895
|
+
// Beneath a directory a by-design rule covers, a pass that isn't named
|
|
896
|
+
// observes nothing. A path a rule covers by its own name is observed as a
|
|
897
|
+
// walk observes it: passed over where the walk lists it, and absent where
|
|
898
|
+
// the walk lists its directory without it; a pass that reaches it by its
|
|
899
|
+
// path alone, as a listing, glob or grep naming it does, passes over it.
|
|
900
|
+
// A pass that names its scope, as a sync naming a path and a file tool's
|
|
901
|
+
// pass on the path it acts on do, sets aside only what covers the scope,
|
|
902
|
+
// at it or above it, as its walk does (#276): a directory passed over by
|
|
903
|
+
// design beneath a named directory stays passed over, and so does what
|
|
904
|
+
// lies beneath it.
|
|
905
|
+
let byDesign: string | undefined;
|
|
906
|
+
if (inOtherMount(path, mount.view.others)) {
|
|
907
|
+
byDesign = OTHER_MOUNT;
|
|
908
|
+
} else if (!opts.named) {
|
|
909
|
+
const slash = path.lastIndexOf('/');
|
|
910
|
+
const above = slash < 0 ? undefined : passedOverAt(path.slice(0, slash), mount.view.ignore, mount.view.others);
|
|
911
|
+
const own = above === undefined && !walk ? passedOverAt(path, mount.view.ignore, mount.view.others) : undefined;
|
|
912
|
+
byDesign = above ?? own;
|
|
913
|
+
} else if (scope.kind === 'dir') {
|
|
914
|
+
byDesign = passedOverBetween(scope.dir, path, ignoreBeneath(scope.dir, mount.view.ignore), mount.view.others);
|
|
915
|
+
}
|
|
916
|
+
if (byDesign !== undefined) {
|
|
917
|
+
gathered.facts.set(path, { kind: 'unobserved', reason: byDesign });
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
636
920
|
if (walk && provenAbsent(walk, path)) {
|
|
637
921
|
gathered.facts.set(path, { kind: 'absent' });
|
|
638
922
|
continue;
|
|
639
923
|
}
|
|
640
|
-
if (walk
|
|
641
|
-
|
|
642
|
-
const region = walk.incomplete.find((r) => r.path === '' || path === r.path || path.startsWith(r.path + '/'));
|
|
643
|
-
gathered.facts.set(path, { kind: 'unobserved', reason: region ? region.reason : 'not reached by the walk' });
|
|
924
|
+
if (walk?.others.get(path) === IGNORED) {
|
|
925
|
+
gathered.facts.set(path, { kind: 'unobserved', reason: IGNORED });
|
|
644
926
|
continue;
|
|
645
927
|
}
|
|
646
|
-
if (walk
|
|
647
|
-
|
|
928
|
+
if (walk && !walk.files.has(path) && !walk.othersTaken.has(path)) {
|
|
929
|
+
// Not taken by the walk, or beneath a region it didn't reach: unproven
|
|
930
|
+
// either way. The nearest region holding it says why.
|
|
931
|
+
let region: Walk['incomplete'][number] | undefined;
|
|
932
|
+
for (const r of walk.incomplete) {
|
|
933
|
+
if (r.path !== '' && path !== r.path && !path.startsWith(r.path + '/')) continue;
|
|
934
|
+
// A link's own region is about what lies beneath it, not the link.
|
|
935
|
+
if (r.kind === 'symlink' && r.path === path) continue;
|
|
936
|
+
if (region === undefined || r.path.length > region.path.length) region = r;
|
|
937
|
+
}
|
|
938
|
+
gathered.facts.set(path, { kind: 'unobserved', reason: region ? region.reason : 'not reached by the walk' });
|
|
648
939
|
continue;
|
|
649
940
|
}
|
|
650
941
|
const p = agreement.get(mount.name, path);
|
|
@@ -719,11 +1010,10 @@ export async function reconcilePass(
|
|
|
719
1010
|
// Decide and apply — synchronously, so nothing interleaves.
|
|
720
1011
|
const branchId = store.currentBranch().id;
|
|
721
1012
|
result.branchId = branchId;
|
|
722
|
-
type Planned = { path: string; verdict: Verdict; d: DiskFact; s:
|
|
1013
|
+
type Planned = { path: string; verdict: Verdict; d: DiskFact; s: StoreSide | null; p: Physical | undefined; bi: BranchIntent | null };
|
|
723
1014
|
const planned: Planned[] = [];
|
|
724
1015
|
for (const [path, d] of facts) {
|
|
725
|
-
const
|
|
726
|
-
const s = entry ? { hash: entry.blobHash, size: entry.size } : null;
|
|
1016
|
+
const s = storeSide(store.treeGet(mount.treeStateId, path));
|
|
727
1017
|
const p = agreement.get(mount.name, path);
|
|
728
1018
|
const bi = mount.intents.get(path);
|
|
729
1019
|
const verdict = decide(d, s, p, bi, branchId, opts.adopt?.(path) === true);
|
|
@@ -758,11 +1048,17 @@ export async function reconcilePass(
|
|
|
758
1048
|
const v = x.verdict;
|
|
759
1049
|
if (v.adopt === 'ingest' && x.d.kind === 'file' && x.d.ingestible) {
|
|
760
1050
|
const blobHash = store.storeBlob(x.d.ingestible, 'text/plain');
|
|
761
|
-
store.treeSet(mount.treeStateId, x.path, { blobHash, size: x.d.ingestible.byteLength, mode
|
|
1051
|
+
store.treeSet(mount.treeStateId, x.path, { blobHash, size: x.d.ingestible.byteLength, ...treeModeOf(v.diskMode ?? x.d.mode, x.s) });
|
|
762
1052
|
committed = true;
|
|
763
1053
|
} else if ((v.adopt === 'remove' || v.adopt === 'drop') && x.s) {
|
|
764
1054
|
store.treeRemove(mount.treeStateId, x.path);
|
|
765
1055
|
committed = true;
|
|
1056
|
+
} else if ((v.mode === 'fill' || v.mode === 'adopt') && x.d.kind === 'file' && x.s) {
|
|
1057
|
+
// The same bytes, with disk's mode. A fill records a mode where the
|
|
1058
|
+
// entry had none, which is no change: an undo or a branch whose entry
|
|
1059
|
+
// still has none agrees with any mode, so neither reads it as one.
|
|
1060
|
+
store.treeSet(mount.treeStateId, x.path, { blobHash: x.s.hash, size: x.s.size, ...treeModeOf(v.diskMode ?? x.d.mode, x.s) });
|
|
1061
|
+
committed = true;
|
|
766
1062
|
}
|
|
767
1063
|
if (v.conflict) {
|
|
768
1064
|
const record: ConflictRecord = { kind: v.conflict, at: Date.now(), disk: counterpartOf(store, x.d) };
|
|
@@ -773,8 +1069,9 @@ export async function reconcilePass(
|
|
|
773
1069
|
mount.intents.put(x.path, v.intent);
|
|
774
1070
|
committed = true;
|
|
775
1071
|
}
|
|
776
|
-
|
|
777
|
-
if (v.
|
|
1072
|
+
const changed = v.adopt !== undefined || v.mode === 'adopt';
|
|
1073
|
+
if (v.op && changed) result.ops.push({ path: x.path, op: v.op, ...(v.adopt === undefined ? { modeOnly: true as const } : {}) });
|
|
1074
|
+
if (v.discards) result.discarded.push({ path: x.path, was: v.discards, ...(v.op && changed ? { op: v.op } : {}) });
|
|
778
1075
|
}
|
|
779
1076
|
|
|
780
1077
|
// 4. Completions and agreements, after the commits they assert are synced —
|
|
@@ -793,6 +1090,18 @@ export async function reconcilePass(
|
|
|
793
1090
|
agreement.set(mount.name, x.path, next, { afterCommittedState: first });
|
|
794
1091
|
first = false;
|
|
795
1092
|
}
|
|
1093
|
+
// Where the walk stopped, once what it observed is decided: the next pass
|
|
1094
|
+
// of this scope resumes after it, or starts afresh once this one covered
|
|
1095
|
+
// the scope. A walk that couldn't list its scope's own directory covered
|
|
1096
|
+
// none of it, and leaves the place as it was. Unlike evidence, a place
|
|
1097
|
+
// binds no root: it only says where the next walk starts, and that walk
|
|
1098
|
+
// takes its turn after it in whatever directory is the root by then.
|
|
1099
|
+
let resumeAfter: string | null | undefined;
|
|
1100
|
+
if (opts.resume && walk && scope.kind === 'dir' && scope.recursive && walk.complete.has(scope.dir)) {
|
|
1101
|
+
resumeAfter = walk.stoppedAfter ?? null;
|
|
1102
|
+
agreement.recordWalk(mount.name, scope.dir, resumeAfter);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
796
1105
|
// 5. Durable before returning. An agreement advance (D = S) has no pending
|
|
797
1106
|
// intent to recover from, so losing its append would leave an older P that
|
|
798
1107
|
// another branch would misread as a disk edit.
|
|
@@ -816,15 +1125,39 @@ export async function reconcilePass(
|
|
|
816
1125
|
}
|
|
817
1126
|
if (walk) {
|
|
818
1127
|
const tracked = [...result.reports.values()].filter((r) => r.state === 'unverified').map((r) => r.path);
|
|
1128
|
+
if (typeof resumeAfter === 'string' && scope.kind === 'dir') {
|
|
1129
|
+
// A watched mount's catch-up scan takes turns with its whole-mount
|
|
1130
|
+
// syncs; nothing else moves a place.
|
|
1131
|
+
const next = scope.dir !== ''
|
|
1132
|
+
? 'the next sync of this directory'
|
|
1133
|
+
: mount.catchUpScan
|
|
1134
|
+
? 'the next sync of the mount, or its catch-up scan if that comes first,'
|
|
1135
|
+
: 'the next sync of the mount';
|
|
1136
|
+
result.incomplete.push({
|
|
1137
|
+
path: scope.dir,
|
|
1138
|
+
reason: `the walk cap (${opts.cap ?? mount.walkCap}) was reached: ${next} resumes after ${mount.name}/${resumeAfter}`,
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
819
1141
|
for (const region of walk.incomplete) {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
1142
|
+
// A link's own region hides only what lies beneath it: the link itself
|
|
1143
|
+
// is reported by path, with its own reason.
|
|
1144
|
+
const hides = tracked.some((p) => (region.kind !== 'symlink' && p === region.path) || p.startsWith(region.path + '/'));
|
|
1145
|
+
// A scope passed over whole is the answer to the pass that asked for
|
|
1146
|
+
// it, so it's always named. Another mount's directory met beneath the
|
|
1147
|
+
// scope is passed over by design even where this mount's tree holds
|
|
1148
|
+
// copies there: no tool of this mount syncs, pushes or clears them
|
|
1149
|
+
// (listings show them, naming their holder), so naming the directory
|
|
1150
|
+
// as unreached would never clear.
|
|
1151
|
+
const wholeScope = scope.kind === 'dir' && scope.dir !== '' && region.path === scope.dir;
|
|
1152
|
+
const unreached = hides && region.reason !== OTHER_MOUNT;
|
|
1153
|
+
if (region.kind === 'error' || region.kind === 'cap' || unreached || wholeScope) result.incomplete.push({ path: region.path, reason: region.reason });
|
|
1154
|
+
// A name passed over by design that hides nothing tracked is reported nowhere else:
|
|
823
1155
|
// counted, so a sync that says 0 also says what it didn't look at
|
|
824
|
-
// (agent-framework #276). A symlink region isn't counted:
|
|
825
|
-
//
|
|
1156
|
+
// (agent-framework #276). A symlink region isn't counted: a link the
|
|
1157
|
+
// walk took is a candidate the pass reports by path, taken through the
|
|
826
1158
|
// link when it's a file on a mount that follows symlinks, and
|
|
827
|
-
// otherwise listed under `skipped
|
|
1159
|
+
// otherwise listed under `skipped`; a tracked link the cap left is
|
|
1160
|
+
// unverified for the cap.
|
|
828
1161
|
else if (region.kind === 'ignored') result.passedOver.set(region.reason, (result.passedOver.get(region.reason) ?? 0) + 1);
|
|
829
1162
|
}
|
|
830
1163
|
}
|
|
@@ -839,7 +1172,9 @@ export interface PushOptions {
|
|
|
839
1172
|
/**
|
|
840
1173
|
* Overwrite (or unlink) a disk copy in conflict, or one that can't be
|
|
841
1174
|
* verified. A disk copy nothing has evidence about is overwritten only when
|
|
842
|
-
* `named` too: a bare push can't tell it from a newer copy.
|
|
1175
|
+
* `named` too: a bare push can't tell it from a newer copy. Forced or not,
|
|
1176
|
+
* nothing but a regular file, or a followed symlink to one, is written over
|
|
1177
|
+
* or unlinked (effects.ts).
|
|
843
1178
|
*/
|
|
844
1179
|
force?: boolean;
|
|
845
1180
|
/**
|
|
@@ -867,6 +1202,11 @@ export interface PushResult {
|
|
|
867
1202
|
deleted: string[];
|
|
868
1203
|
/** Refused paths, each with its reason. */
|
|
869
1204
|
skipped: Array<{ path: string; reason: string }>;
|
|
1205
|
+
/**
|
|
1206
|
+
* Written paths whose mode stayed as disk had it, each with why: their
|
|
1207
|
+
* bytes reached disk, and the workspace's mode didn't.
|
|
1208
|
+
*/
|
|
1209
|
+
modesKept: Array<{ path: string; reason: string }>;
|
|
870
1210
|
/** Workspace deletions left on disk because applyDeletions wasn't given. */
|
|
871
1211
|
pendingDeletions: string[];
|
|
872
1212
|
/** The branch and sequence the push planned on: what disk now reflects.
|
|
@@ -877,11 +1217,16 @@ export interface PushResult {
|
|
|
877
1217
|
sequence?: number;
|
|
878
1218
|
}
|
|
879
1219
|
|
|
1220
|
+
/** Why a push skips a path in another mount's directory nested in its own. */
|
|
1221
|
+
export const NOT_PUSHED_IN_OTHER_MOUNT = "it lies in another mount's directory, which no push of this mount writes";
|
|
1222
|
+
|
|
880
1223
|
/**
|
|
881
1224
|
* Make disk hold the store's version of each path, under the same rule:
|
|
882
1225
|
* drafts are written, agreement is left alone, conflicts and unverifiable
|
|
883
1226
|
* disk copies are refused unless `force`, and a workspace deletion reaches
|
|
884
1227
|
* disk only with `applyDeletions` (with `force` too when disk changed since).
|
|
1228
|
+
* Forced or not, nothing but a regular file, or a followed symlink to one, is
|
|
1229
|
+
* written over or unlinked (effects.ts).
|
|
885
1230
|
* A path nothing has evidence about is written only when `named`.
|
|
886
1231
|
* A path whose earlier push shows on disk unconfirmed is pushed again, so its
|
|
887
1232
|
* completion rests on barriers that succeeded.
|
|
@@ -905,7 +1250,7 @@ export async function pushPaths(
|
|
|
905
1250
|
): Promise<PushResult> {
|
|
906
1251
|
agreement.ensureReconciled();
|
|
907
1252
|
if (agreement.needsBarrier) agreement.barrier(); // as a pass does: nothing decided from unsynced evidence
|
|
908
|
-
const result: PushResult = { written: [], unchanged: [], deleted: [], skipped: [], pendingDeletions: [] };
|
|
1253
|
+
const result: PushResult = { written: [], unchanged: [], deleted: [], skipped: [], modesKept: [], pendingDeletions: [] };
|
|
909
1254
|
if (mount.readOnly) return result;
|
|
910
1255
|
// A root that isn't available takes no writes, forced or not: restoring the
|
|
911
1256
|
// store's files into an empty mountpoint would fill the disk the drive was
|
|
@@ -915,6 +1260,18 @@ export async function pushPaths(
|
|
|
915
1260
|
for (const path of paths) result.skipped.push({ path, reason: root.reason });
|
|
916
1261
|
return result;
|
|
917
1262
|
}
|
|
1263
|
+
// Another mount's directory nested in this one is that mount's: no push
|
|
1264
|
+
// of this mount writes or deletes a path in it, forced or not. (On a mount
|
|
1265
|
+
// that follows links, a link of this mount's that leads there is read as
|
|
1266
|
+
// this mount's own path; on one that doesn't, the link is refused.)
|
|
1267
|
+
if (mount.view.others.length > 0) {
|
|
1268
|
+
const own: string[] = [];
|
|
1269
|
+
for (const path of paths) {
|
|
1270
|
+
if (inOtherMount(path, mount.view.others)) result.skipped.push({ path, reason: NOT_PUSHED_IN_OTHER_MOUNT });
|
|
1271
|
+
else own.push(path);
|
|
1272
|
+
}
|
|
1273
|
+
paths = own;
|
|
1274
|
+
}
|
|
918
1275
|
|
|
919
1276
|
type Plan = {
|
|
920
1277
|
path: string;
|
|
@@ -927,7 +1284,39 @@ export async function pushPaths(
|
|
|
927
1284
|
prior: Agreed | null;
|
|
928
1285
|
/** What the effect may find at the path. */
|
|
929
1286
|
expect: Expect;
|
|
930
|
-
} & (
|
|
1287
|
+
} & (
|
|
1288
|
+
| {
|
|
1289
|
+
kind: 'write';
|
|
1290
|
+
blob: Buffer;
|
|
1291
|
+
hash: string;
|
|
1292
|
+
/** Permission bits the write gives the file, if any. */
|
|
1293
|
+
apply?: number;
|
|
1294
|
+
/** The mode disk had when it was checked, which `apply` replaces only if disk still has it. */
|
|
1295
|
+
from?: number;
|
|
1296
|
+
/** The store's mode, and the evidence's before the push. */
|
|
1297
|
+
storeMode?: number;
|
|
1298
|
+
priorMode?: number;
|
|
1299
|
+
/** The mode disk had when it was checked, forced or not: where the push's modes start. */
|
|
1300
|
+
seenMode?: number;
|
|
1301
|
+
/** `apply` is forced over a mode disk changed since it last agreed, or one nothing records. */
|
|
1302
|
+
overDisk?: true;
|
|
1303
|
+
/**
|
|
1304
|
+
* For a push that re-plans over our own narrowing push stopped before
|
|
1305
|
+
* its bytes, the modes its intent carries: from the old bytes' own
|
|
1306
|
+
* mode, the stopped push's `from`, to the store's mode now, or where
|
|
1307
|
+
* the store records none, the mode the stopped push narrowed to.
|
|
1308
|
+
*/
|
|
1309
|
+
ownModes?: { from: number; to: number };
|
|
1310
|
+
}
|
|
1311
|
+
| { kind: 'unlink' }
|
|
1312
|
+
/**
|
|
1313
|
+
* The store's mode, applied to bytes disk and the store share; `from`,
|
|
1314
|
+
* when given, the mode disk must still have. `overDisk`: a forced chmod
|
|
1315
|
+
* over a mode disk changed since it last agreed, or one nothing records
|
|
1316
|
+
* which side changed, rather than a push of the store's own change.
|
|
1317
|
+
*/
|
|
1318
|
+
| { kind: 'chmod'; mode: number; hash: string; size: number; from?: number; overDisk?: true }
|
|
1319
|
+
);
|
|
931
1320
|
const plans: Plan[] = [];
|
|
932
1321
|
|
|
933
1322
|
// Observe disk.
|
|
@@ -958,11 +1347,20 @@ export async function pushPaths(
|
|
|
958
1347
|
result.sequence = store.currentSequence();
|
|
959
1348
|
let recorded = false;
|
|
960
1349
|
for (const [path, d] of facts) {
|
|
961
|
-
const
|
|
962
|
-
const s = entry ? { hash: entry.blobHash, size: entry.size } : null;
|
|
1350
|
+
const s = storeSide(store.treeGet(mount.treeStateId, path));
|
|
963
1351
|
const p = agreement.get(mount.name, path);
|
|
964
1352
|
const bi = mount.intents.get(path);
|
|
965
1353
|
const v = decide(d, s, p, bi, branchId, false);
|
|
1354
|
+
// The ways out a refusal names, as materialize takes them: a deletion
|
|
1355
|
+
// reaches disk only with applyDeletions.
|
|
1356
|
+
const again = s ? 'materialize again' : 'materialize again with applyDeletions';
|
|
1357
|
+
// Where disk has deleted a file the workspace holds, a sync takes the
|
|
1358
|
+
// deletion, which removes the workspace's copy, and a forced push writes
|
|
1359
|
+
// the workspace's copy back: there is no disk copy to adopt or overwrite.
|
|
1360
|
+
const gone = s !== null && d.kind === 'absent';
|
|
1361
|
+
const forced = !s ? 'materialize it with force and applyDeletions to delete it'
|
|
1362
|
+
: gone ? "materialize it with force to write the workspace's copy back" : 'materialize it with force to overwrite it';
|
|
1363
|
+
const takeDisk = gone ? "sync this path to take disk's deletion, which removes the workspace's copy" : 'sync this path to adopt the disk version';
|
|
966
1364
|
// What the effect may find: what this observation saw, unless forced.
|
|
967
1365
|
const expect: Expect = opts.force || d.kind === 'unobserved' || d.kind === 'unhashed'
|
|
968
1366
|
? { kind: 'any' }
|
|
@@ -976,14 +1374,47 @@ export async function pushPaths(
|
|
|
976
1374
|
result.skipped.push({
|
|
977
1375
|
path,
|
|
978
1376
|
reason: 'stale copy: disk changed since it last agreed with the workspace, which did not — ' +
|
|
979
|
-
|
|
1377
|
+
`${takeDisk}, or ${forced}`,
|
|
980
1378
|
});
|
|
981
1379
|
continue;
|
|
982
1380
|
}
|
|
983
1381
|
const priorP = p && (p.kind === 'absent' || p.kind === 'content') ? p : null;
|
|
984
1382
|
const blob = store.getBlob(s.hash);
|
|
985
|
-
|
|
986
|
-
|
|
1383
|
+
const forcedMode = mount.modesObserved && s.mode !== undefined && s.mode !== (d.kind === 'file' ? d.mode : undefined) ? s.mode : undefined;
|
|
1384
|
+
const agreedMode = priorP?.kind === 'content' ? priorP.mode : undefined;
|
|
1385
|
+
if (blob) {
|
|
1386
|
+
plans.push({
|
|
1387
|
+
path, kind: 'write', blob, hash: s.hash, prior: priorP, before: agreement.get(mount.name, path), expect,
|
|
1388
|
+
...(forcedMode !== undefined ? { apply: forcedMode } : {}),
|
|
1389
|
+
...(forcedMode !== undefined && d.kind === 'file' && agreedMode !== d.mode ? { overDisk: true as const } : {}),
|
|
1390
|
+
...(s.mode !== undefined ? { storeMode: s.mode } : {}),
|
|
1391
|
+
// Refused over disk's mode, the completion records what an unforced
|
|
1392
|
+
// write would: the mode disk last agreed with.
|
|
1393
|
+
...(agreedMode !== undefined ? { priorMode: agreedMode } : {}),
|
|
1394
|
+
...(d.kind === 'file' && d.mode !== undefined ? { seenMode: d.mode } : {}),
|
|
1395
|
+
});
|
|
1396
|
+
} else result.skipped.push({ path, reason: 'the workspace copy is missing from the store' });
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
if (v.mode === 'adopt' && s && d.kind === 'file') {
|
|
1400
|
+
// Disk's mode changed since it last agreed, on the bytes the store
|
|
1401
|
+
// holds, and the store's didn't. A sync takes it in. Recording disk's
|
|
1402
|
+
// mode here would read as the store's change, which the next push
|
|
1403
|
+
// would revert; forced, disk takes the store's mode, as forced bytes do.
|
|
1404
|
+
if (!opts.force) {
|
|
1405
|
+
// modeAction adopts where disk changed the mode since they agreed, and
|
|
1406
|
+
// where nothing records a mode they agreed on.
|
|
1407
|
+
const agreedMode = p?.kind === 'content' ? p.mode : undefined;
|
|
1408
|
+
result.skipped.push({
|
|
1409
|
+
path,
|
|
1410
|
+
reason: (agreedMode === undefined
|
|
1411
|
+
? `disk's mode (${octal(d.mode!)}) differs from the workspace's (${octal(s.mode!)}), and nothing records which side changed it`
|
|
1412
|
+
: `disk's mode (${octal(d.mode!)}) changed since it last agreed with the workspace's (${octal(s.mode!)})`) +
|
|
1413
|
+
' — sync this path to take disk\'s mode in, or materialize this path with force to apply the workspace\'s',
|
|
1414
|
+
});
|
|
1415
|
+
continue;
|
|
1416
|
+
}
|
|
1417
|
+
plans.push({ path, kind: 'chmod', mode: s.mode!, hash: s.hash, size: s.size, prior: null, before: p, expect, overDisk: true });
|
|
987
1418
|
continue;
|
|
988
1419
|
}
|
|
989
1420
|
// Whatever the verdict learned about disk (an agreement, a resolved
|
|
@@ -996,8 +1427,22 @@ export async function pushPaths(
|
|
|
996
1427
|
// The prior a new intent carries is the last confirmed evidence: for a
|
|
997
1428
|
// path whose earlier push is unconfirmed, that push's own prior, never
|
|
998
1429
|
// the bytes it wrote.
|
|
999
|
-
const known = v.unconfirmed ? (p as Pending).prior ?? undefined : v.p === 'forget' ? undefined : v.p !== undefined ? v.p : p;
|
|
1430
|
+
const known = v.unconfirmed ? (p as Pending).prior ?? undefined : v.p === 'forget' ? undefined : v.p !== undefined ? v.p : v.prior ?? p;
|
|
1000
1431
|
const prior = known && (known.kind === 'absent' || known.kind === 'content') ? known : null;
|
|
1432
|
+
// A push that re-plans over our own narrowing push, stopped before its
|
|
1433
|
+
// bytes, plans from disk's narrowed mode (the verdict's prior, which
|
|
1434
|
+
// resolvePending leaves unrecorded), as above, and goes on as that push:
|
|
1435
|
+
// its intent carries the stopped push's prior, the old bytes at their own
|
|
1436
|
+
// mode, never the mode it narrowed them to, and modes from that mode, so a
|
|
1437
|
+
// stop of this push too reads disk's narrowed mode as the push's own.
|
|
1438
|
+
// Past the adoptions handled above, a verdict carries a prior with P still
|
|
1439
|
+
// pending only from that narrowing, whose push recorded its modes.
|
|
1440
|
+
const stopped = v.prior !== undefined && p?.kind === 'pending' ? p : undefined;
|
|
1441
|
+
if (v.mode === 'push' && s && d.kind === 'file') {
|
|
1442
|
+
// Forced, disk takes the store's mode whatever it holds now, as it takes its bytes.
|
|
1443
|
+
plans.push({ path, kind: 'chmod', mode: s.mode!, ...(opts.force ? {} : { from: d.mode }), hash: s.hash, size: s.size, prior, before: agreement.get(mount.name, path), expect });
|
|
1444
|
+
continue;
|
|
1445
|
+
}
|
|
1001
1446
|
if (v.state === 'synced' && !v.unconfirmed) {
|
|
1002
1447
|
if (v.intent !== undefined) {
|
|
1003
1448
|
mount.intents.put(path, v.intent);
|
|
@@ -1008,7 +1453,25 @@ export async function pushPaths(
|
|
|
1008
1453
|
}
|
|
1009
1454
|
if (v.state === 'unverified') {
|
|
1010
1455
|
if (!opts.force) {
|
|
1011
|
-
|
|
1456
|
+
// An intent that can't be read is the one note fixing doesn't
|
|
1457
|
+
// answer: nothing is decided over it until a sync of the path
|
|
1458
|
+
// replaces it (decide()), and a sync replaces it only where it sees
|
|
1459
|
+
// disk's copy, so a copy it can't see is named first. For the rest,
|
|
1460
|
+
// fixing what the note names holds, and forcedWay says where force
|
|
1461
|
+
// does too. Where disk has deleted the file, that sync takes the
|
|
1462
|
+
// deletion.
|
|
1463
|
+
const sync = gone
|
|
1464
|
+
? "sync this path to take disk's deletion, which removes the workspace's copy and replaces the intent"
|
|
1465
|
+
: 'sync this path to adopt the disk version, which replaces the intent';
|
|
1466
|
+
result.skipped.push({
|
|
1467
|
+
path,
|
|
1468
|
+
reason: bi?.unreadable !== undefined
|
|
1469
|
+
? d.kind === 'absent' || d.kind === 'file'
|
|
1470
|
+
? `${v.note} — ${sync}`
|
|
1471
|
+
: `${v.note}, and disk's copy can't be verified (${d.kind === 'unobserved' ? d.reason : 'not hashed'}) — once it can be, ${sync}`
|
|
1472
|
+
: inTheWay(v.note, !s) ??
|
|
1473
|
+
`cannot verify disk copy (${v.note ?? 'not observed'}) — fix it and ${again}${forcedWay(v.note, !s, forced)}`,
|
|
1474
|
+
});
|
|
1012
1475
|
continue;
|
|
1013
1476
|
}
|
|
1014
1477
|
}
|
|
@@ -1027,15 +1490,20 @@ export async function pushPaths(
|
|
|
1027
1490
|
}
|
|
1028
1491
|
result.skipped.push({
|
|
1029
1492
|
path,
|
|
1030
|
-
|
|
1493
|
+
// Whatever was recorded, a disk with no copy now is answered for what
|
|
1494
|
+
// it holds; a path with no evidence still needs its push named.
|
|
1495
|
+
reason: gone
|
|
1496
|
+
? `disk has no copy of this file (conflict: ${kind}) — ${takeDisk}, or ` +
|
|
1497
|
+
`materialize ${noEvidence ? 'this path' : 'it'} with force to write the workspace's copy back`
|
|
1498
|
+
: noEvidence
|
|
1031
1499
|
? `disk holds a different copy, and nothing records which is newer (conflict: ${kind}) — ` +
|
|
1032
|
-
|
|
1500
|
+
`sync this path to adopt the disk version, or materialize this path with force ${s ? 'to overwrite it' : 'and applyDeletions to delete it'}`
|
|
1033
1501
|
: kind === 'interrupted'
|
|
1034
1502
|
? 'disk holds bytes that match neither what was being written there nor what disk last agreed with ' +
|
|
1035
1503
|
'(conflict: interrupted), as a write that failed or was cut off partway leaves — check the file, then ' +
|
|
1036
|
-
|
|
1504
|
+
`${forced}, or sync this path to adopt the disk copy as it is`
|
|
1037
1505
|
: `stale copy: disk changed since it last agreed with the workspace (conflict: ${kind}) — ` +
|
|
1038
|
-
|
|
1506
|
+
`sync this path to adopt the disk version, or ${forced}`,
|
|
1039
1507
|
});
|
|
1040
1508
|
continue;
|
|
1041
1509
|
}
|
|
@@ -1064,7 +1532,12 @@ export async function pushPaths(
|
|
|
1064
1532
|
continue;
|
|
1065
1533
|
}
|
|
1066
1534
|
if (!opts.applyDeletions) {
|
|
1067
|
-
|
|
1535
|
+
// A deletion no push makes, forced or not (inTheWay), is refused for
|
|
1536
|
+
// that, as an unforced push refuses it, rather than left for
|
|
1537
|
+
// applyDeletions, which couldn't make it either.
|
|
1538
|
+
const lasting = inTheWay(v.note, true);
|
|
1539
|
+
if (lasting !== null) result.skipped.push({ path, reason: lasting });
|
|
1540
|
+
else result.pendingDeletions.push(path);
|
|
1068
1541
|
continue;
|
|
1069
1542
|
}
|
|
1070
1543
|
if (d.kind === 'absent') continue;
|
|
@@ -1076,28 +1549,115 @@ export async function pushPaths(
|
|
|
1076
1549
|
result.skipped.push({ path, reason: 'the workspace copy is missing from the store' });
|
|
1077
1550
|
continue;
|
|
1078
1551
|
}
|
|
1079
|
-
|
|
1552
|
+
// The store's mode goes to disk with its bytes where the file is new,
|
|
1553
|
+
// the push is forced, or disk still has the mode they last agreed on. A
|
|
1554
|
+
// push that completes our own unconfirmed one, whose bytes landed, is a
|
|
1555
|
+
// push like any other: it gives the store's mode where disk shows one of
|
|
1556
|
+
// the stopped push's own (pushedMode) or the one they last agreed on, and
|
|
1557
|
+
// keeps any other, set since it stopped (Ada-1017, room-203 #131062,
|
|
1558
|
+
// #131518). A mode disk changed since stays, and so does the evidence
|
|
1559
|
+
// that it did, up to the moment of the write.
|
|
1560
|
+
const dm = d.kind === 'file' ? d.mode : undefined;
|
|
1561
|
+
const priorMode = prior?.kind === 'content' ? prior.mode : undefined;
|
|
1562
|
+
const own = v.unconfirmed === true && v.ownMode === true;
|
|
1563
|
+
const apply = mount.modesObserved && s.mode !== undefined && s.mode !== dm &&
|
|
1564
|
+
(dm === undefined || opts.force === true || own || priorMode === dm) ? s.mode : undefined;
|
|
1565
|
+
plans.push({
|
|
1566
|
+
path, kind: 'write', blob, hash: s.hash, prior: stopped ? stopped.prior : prior, before: agreement.get(mount.name, path), expect,
|
|
1567
|
+
// With no mode in the store's entry, its `to` is the mode the push
|
|
1568
|
+
// leaves, the one the stopped push narrowed to: it claims none it lacks.
|
|
1569
|
+
...(stopped ? { ownModes: { from: stopped.modes!.from, to: s.mode ?? (stopped.modes!.from & stopped.modes!.to) } } : {}),
|
|
1570
|
+
...(apply !== undefined ? { apply } : {}),
|
|
1571
|
+
...(apply !== undefined && dm !== undefined && opts.force !== true ? { from: dm } : {}),
|
|
1572
|
+
// Forced over a mode disk changed: without force, the bytes would keep it.
|
|
1573
|
+
...(apply !== undefined && dm !== undefined && priorMode !== dm && !own ? { overDisk: true as const } : {}),
|
|
1574
|
+
...(s.mode !== undefined ? { storeMode: s.mode } : {}),
|
|
1575
|
+
...(priorMode !== undefined ? { priorMode } : {}),
|
|
1576
|
+
...(dm !== undefined ? { seenMode: dm } : {}),
|
|
1577
|
+
});
|
|
1080
1578
|
}
|
|
1081
1579
|
|
|
1082
1580
|
// 1. Durable intents before any disk effect, the root they write under first.
|
|
1581
|
+
// A chmod announces none: it leaves the bytes as they are. Stopped before
|
|
1582
|
+
// its completion, it leaves disk at the store's mode or still at the one
|
|
1583
|
+
// the evidence agreed, and the next observation decides from that: synced
|
|
1584
|
+
// and recorded, or pushed again. Nothing is lost either way. A forced
|
|
1585
|
+
// chmod has no guard: stopped before it, disk keeps whatever mode it found,
|
|
1586
|
+
// which the next observation reads as disk's, and forced again, it applies
|
|
1587
|
+
// the store's.
|
|
1083
1588
|
if (plans.length > 0) agreement.recordRootIdentity(mount.name, root.identity);
|
|
1084
|
-
plans.
|
|
1589
|
+
const announced = plans.filter((step) => step.kind !== 'chmod');
|
|
1590
|
+
announced.forEach((step, i) => {
|
|
1085
1591
|
agreement.intend(mount.name, step.path, {
|
|
1086
1592
|
effect: 'disk',
|
|
1087
1593
|
prior: step.prior,
|
|
1088
1594
|
expect: step.kind === 'write' ? { kind: 'content', hash: step.hash } : ABSENT,
|
|
1089
1595
|
branchId,
|
|
1090
|
-
|
|
1596
|
+
// A write whose plan gives a mode (`apply`) records the modes it may
|
|
1597
|
+
// leave, so a stop is read by them: the mode it saw, or for a file it
|
|
1598
|
+
// creates the one it creates it at, and the one it gives. A file a
|
|
1599
|
+
// forced push creates at its entry's mode with no mode planned records
|
|
1600
|
+
// none. One that goes on as our own stopped narrowing records that
|
|
1601
|
+
// push's, from the old bytes' own mode, whether or not it gives a mode
|
|
1602
|
+
// itself.
|
|
1603
|
+
...(step.kind === 'write' && step.ownModes !== undefined ? { modes: step.ownModes }
|
|
1604
|
+
: step.kind === 'write' && step.apply !== undefined
|
|
1605
|
+
? { modes: { from: step.seenMode ?? step.apply, to: step.apply } } : {}),
|
|
1606
|
+
}, { durable: i === announced.length - 1 });
|
|
1091
1607
|
});
|
|
1092
1608
|
|
|
1093
1609
|
// 2. Effects, each bound at its point of use.
|
|
1094
|
-
const effected: Array<{ step: Plan; dirs: string[] }> = [];
|
|
1610
|
+
const effected: Array<{ step: Plan; dirs: string[]; mode?: number; widenRefused?: boolean; modeKept?: boolean }> = [];
|
|
1095
1611
|
for (const step of plans) {
|
|
1096
1612
|
opts.beforeEffect?.(step.path);
|
|
1097
1613
|
try {
|
|
1098
1614
|
if (step.kind === 'write') {
|
|
1099
|
-
|
|
1100
|
-
|
|
1615
|
+
const { perm, widenRefused, modeKept } = await writeContained(
|
|
1616
|
+
mount.view, mount.rootReal, step.path, step.blob, step.expect, step.apply, step.from, step.overDisk === true,
|
|
1617
|
+
// Where modes go unobserved, nothing is applied, a created file's included.
|
|
1618
|
+
mount.modesObserved ? step.storeMode : undefined,
|
|
1619
|
+
);
|
|
1620
|
+
effected.push({ step, dirs: chainOf(step.path), mode: perm, widenRefused, modeKept });
|
|
1621
|
+
} else if (step.kind === 'chmod') {
|
|
1622
|
+
// An inode change, made durable by the file's own fsync.
|
|
1623
|
+
const { refused, narrowing } = await chmodContained(mount.view, mount.rootReal, step.path, step.mode, step.expect, step.from);
|
|
1624
|
+
// A chmod announces no intent, so a refused one leaves the evidence
|
|
1625
|
+
// as planning read it, unless it says otherwise below.
|
|
1626
|
+
if (refused && narrowing && !step.overDisk) {
|
|
1627
|
+
// A narrowing of the store's own change that the kernel refused
|
|
1628
|
+
// stays owed, as a write's does: taking disk's wider mode in would
|
|
1629
|
+
// let a later write put new bytes out at it.
|
|
1630
|
+
result.skipped.push({
|
|
1631
|
+
path: step.path,
|
|
1632
|
+
reason: `not given the workspace's mode (${octal(step.mode)}): the kernel refused to narrow it, as it does for a file another user owns, ` +
|
|
1633
|
+
'so it stays owed: a retry is refused the same way until the file\'s owner or an administrator narrows it',
|
|
1634
|
+
});
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
if (refused && step.overDisk) {
|
|
1638
|
+
// Forced over a mode disk changed, or one nothing records: the
|
|
1639
|
+
// path is as it was before the push, so a sync takes disk's mode
|
|
1640
|
+
// in, as it would have without force.
|
|
1641
|
+
result.skipped.push({
|
|
1642
|
+
path: step.path,
|
|
1643
|
+
reason: `not given the workspace's mode (${octal(step.mode)}): the kernel refused the chmod, so disk keeps its mode, and a sync takes it in`,
|
|
1644
|
+
});
|
|
1645
|
+
continue;
|
|
1646
|
+
}
|
|
1647
|
+
if (refused) {
|
|
1648
|
+
// A widening of the store's own change that the kernel refused:
|
|
1649
|
+
// disk keeps its mode, and the evidence records none, so a sync
|
|
1650
|
+
// takes disk's in by the rule for an unknown rather than owing a
|
|
1651
|
+
// mode this process can never give.
|
|
1652
|
+
agreement.set(mount.name, step.path, { kind: 'content', hash: step.hash, size: step.size });
|
|
1653
|
+
recorded = true;
|
|
1654
|
+
result.skipped.push({
|
|
1655
|
+
path: step.path,
|
|
1656
|
+
reason: `not given the workspace's mode (${octal(step.mode)}): the kernel refused the chmod, and a sync takes disk's mode in`,
|
|
1657
|
+
});
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
effected.push({ step, dirs: [] });
|
|
1101
1661
|
} else {
|
|
1102
1662
|
// Its directory is synced whether this unlink removed the entry or
|
|
1103
1663
|
// found it gone: either way the completion claims a durable absence.
|
|
@@ -1110,7 +1670,8 @@ export async function pushPaths(
|
|
|
1110
1670
|
// exactly what it was, whatever kind that was.
|
|
1111
1671
|
agreement.restore(mount.name, step.path, step.before);
|
|
1112
1672
|
recorded = true;
|
|
1113
|
-
|
|
1673
|
+
const why = inTheWay(failure.message, step.kind === 'unlink') ?? failure.message;
|
|
1674
|
+
result.skipped.push({ path: step.path, reason: `not ${step.kind === 'unlink' ? 'unlinked' : step.kind === 'chmod' ? 'given the workspace\'s mode' : 'written'}: ${why}` });
|
|
1114
1675
|
} else {
|
|
1115
1676
|
// No completion: the intent stays pending, and the next observation
|
|
1116
1677
|
// resolves it from whatever disk then holds.
|
|
@@ -1127,7 +1688,8 @@ export async function pushPaths(
|
|
|
1127
1688
|
// 4. Completions. P is physical and global. Branch intent belongs to the
|
|
1128
1689
|
// branch this push planned on: it is settled only while that branch is
|
|
1129
1690
|
// still selected, and otherwise by convergence when the branch returns.
|
|
1130
|
-
for (const { step, dirs } of effected) {
|
|
1691
|
+
for (const { step, dirs, mode: written, widenRefused, modeKept } of effected) {
|
|
1692
|
+
const mode = mount.modesObserved ? written : undefined;
|
|
1131
1693
|
const failed = dirs.find((dir) => unsynced.has(dir));
|
|
1132
1694
|
if (failed !== undefined) {
|
|
1133
1695
|
result.skipped.push({
|
|
@@ -1137,11 +1699,61 @@ export async function pushPaths(
|
|
|
1137
1699
|
});
|
|
1138
1700
|
continue;
|
|
1139
1701
|
}
|
|
1140
|
-
|
|
1702
|
+
// The mode the evidence records: the one the push gave the file, if the
|
|
1703
|
+
// file has it; disk's own, where the store records none or the same; and
|
|
1704
|
+
// otherwise the one disk last agreed with, so a mode disk changed since
|
|
1705
|
+
// reads as disk's. (Where modes go unobserved, the evidence's writer
|
|
1706
|
+
// records none.)
|
|
1707
|
+
// A widening of the workspace's own change that the kernel refused
|
|
1708
|
+
// records none: a sync takes disk's mode in, rather than owing one this
|
|
1709
|
+
// process can never give. Forced over a mode disk changed, or one nothing
|
|
1710
|
+
// records (overDisk), any refused chmod ends where an unforced write
|
|
1711
|
+
// would: the evidence records the mode disk last agreed with, if any, and
|
|
1712
|
+
// a sync takes disk's mode in.
|
|
1713
|
+
const agreedMode = step.kind === 'chmod'
|
|
1714
|
+
? step.mode
|
|
1715
|
+
: step.kind === 'write'
|
|
1716
|
+
? widenRefused && !step.overDisk
|
|
1717
|
+
? undefined
|
|
1718
|
+
: step.apply !== undefined && step.apply === mode
|
|
1719
|
+
? mode
|
|
1720
|
+
: step.storeMode === undefined || step.storeMode === mode ? mode : step.priorMode
|
|
1721
|
+
: undefined;
|
|
1722
|
+
const value: Agreed = step.kind === 'unlink'
|
|
1723
|
+
? { kind: 'absent' }
|
|
1724
|
+
: { kind: 'content', hash: step.hash, size: step.kind === 'write' ? step.blob.byteLength : step.size, ...(agreedMode !== undefined ? { mode: agreedMode } : {}) };
|
|
1141
1725
|
agreement.set(mount.name, step.path, value);
|
|
1142
1726
|
recorded = true;
|
|
1727
|
+
// Bytes written where the kernel refused the mode, or where disk's mode
|
|
1728
|
+
// changed since it last agreed, keep disk's mode, and the push says so
|
|
1729
|
+
// beside what it wrote: the bytes reached disk, so it's no refusal.
|
|
1730
|
+
if (step.kind === 'write' && widenRefused && mode !== undefined) {
|
|
1731
|
+
result.modesKept.push({
|
|
1732
|
+
path: step.path,
|
|
1733
|
+
reason: `written, its mode left at ${octal(mode)}: the kernel refused the chmod, ` +
|
|
1734
|
+
`so the workspace's (${octal(step.apply ?? step.storeMode ?? mode)}) isn't given, and a sync takes disk's mode in`,
|
|
1735
|
+
});
|
|
1736
|
+
} else if (step.kind === 'write' && modeKept && mode !== undefined) {
|
|
1737
|
+
// Forced over a mode disk changed, and refused: the bytes are written
|
|
1738
|
+
// at it, the evidence records it as an unforced write's would.
|
|
1739
|
+
result.modesKept.push({
|
|
1740
|
+
path: step.path,
|
|
1741
|
+
reason: `written, its mode left at ${octal(mode)}: the kernel refused to narrow it ` +
|
|
1742
|
+
`${narrowedTo(mode & (step.apply ?? mode), step.apply ?? mode)}, so disk keeps its mode, and a sync takes it in`,
|
|
1743
|
+
});
|
|
1744
|
+
} else if (step.kind === 'write' && mode !== undefined && step.storeMode !== undefined && mode !== step.storeMode) {
|
|
1745
|
+
result.modesKept.push({
|
|
1746
|
+
path: step.path,
|
|
1747
|
+
reason: (step.priorMode === undefined
|
|
1748
|
+
? `written, keeping disk's mode (${octal(mode)}), which differs from the workspace's (${octal(step.storeMode)}), and nothing records which side changed it`
|
|
1749
|
+
: `written, keeping disk's mode (${octal(mode)}), which changed since it last agreed with the workspace's (${octal(step.storeMode)})`) +
|
|
1750
|
+
' — sync this path to take disk\'s mode in, or materialize this path with force to apply the workspace\'s',
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1143
1753
|
const planned = store.currentBranch().id === branchId;
|
|
1144
|
-
if (step.kind === '
|
|
1754
|
+
if (step.kind === 'chmod') {
|
|
1755
|
+
result.written.push(step.path);
|
|
1756
|
+
} else if (step.kind === 'write') {
|
|
1145
1757
|
if (planned) mount.intents.update(step.path, (cur) => { const next = { ...cur }; delete next.conflict; delete next.origin; return next; });
|
|
1146
1758
|
result.written.push(step.path);
|
|
1147
1759
|
} else {
|