@animalabs/agent-framework 0.22.2 → 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 +3 -1
- package/dist/src/agent.d.ts +77 -12
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +136 -40
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/mcp-server-core.js +1 -1
- package/dist/src/api/mcp-server-core.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +5 -1
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/api/types.d.ts +1 -1
- 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 +828 -88
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +5011 -1326
- 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/index.d.ts +4 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +144 -6
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +424 -263
- 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/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/push-coalescer.d.ts +33 -11
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +52 -20
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +16 -9
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +18 -83
- 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 +47 -12
- 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/server-connection.d.ts +89 -10
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +405 -129
- 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/transport.d.ts +51 -2
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +114 -20
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +14 -3
- 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.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 +379 -143
- 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/disk-agreement.d.ts +32 -2
- package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +30 -2
- 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 +119 -23
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +793 -247
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +75 -8
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +174 -14
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts +58 -7
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +521 -56
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +18 -4
- 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/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/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 +3 -1
- package/dist/src/recovery/offline-branch.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/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/types/agent.d.ts +17 -5
- 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 +21 -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 +23 -4
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +95 -6
- 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 +3 -4
- package/src/agent.ts +162 -40
- package/src/api/mcp-server-core.ts +1 -1
- package/src/api/server.ts +4 -1
- package/src/api/types.ts +1 -1
- 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 +5352 -1388
- package/src/gate/event-gate.ts +28 -3
- package/src/index.ts +11 -2
- package/src/mcpl/channel-registry.ts +517 -286
- 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/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/push-coalescer.ts +60 -24
- package/src/mcpl/push-handler.ts +20 -90
- package/src/mcpl/reference-fetcher.ts +71 -26
- package/src/mcpl/references.ts +81 -22
- package/src/mcpl/server-connection.ts +427 -132
- package/src/mcpl/server-registry.ts +15 -4
- package/src/mcpl/skip-reply-wake.ts +73 -0
- package/src/mcpl/transport.ts +120 -18
- package/src/mcpl/types.ts +14 -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 +391 -158
- 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 +51 -3
- package/src/modules/workspace/effects.ts +279 -35
- package/src/modules/workspace/index.ts +810 -260
- package/src/modules/workspace/observe.ts +194 -20
- package/src/modules/workspace/reconcile.ts +619 -64
- package/src/modules/workspace/types.ts +18 -4
- 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 +4 -1
- package/src/safe-slice.ts +9 -1
- 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 +17 -7
- package/src/types/events.ts +17 -0
- package/src/types/framework.ts +21 -5
- package/src/types/inference-log.ts +1 -1
- package/src/types/module.ts +24 -4
- package/src/types/trace.ts +101 -6
- 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 -1598
- 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 -621
- 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 -371
- 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/discord-typing-keepalive.test.d.ts +0 -2
- package/dist/test/discord-typing-keepalive.test.d.ts.map +0 -1
- package/dist/test/discord-typing-keepalive.test.js +0 -77
- package/dist/test/discord-typing-keepalive.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 -1715
- 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 -74
- package/dist/test/helpers/coalescing-fixture.d.ts.map +0 -1
- package/dist/test/helpers/coalescing-fixture.js +0 -152
- 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/hide-then-turn.test.d.ts +0 -2
- package/dist/test/hide-then-turn.test.d.ts.map +0 -1
- package/dist/test/hide-then-turn.test.js +0 -101
- package/dist/test/hide-then-turn.test.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 -988
- 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 -387
- 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 -1202
- 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 -1827
- package/dist/test/maintenance-timer.test.js.map +0 -1
- package/dist/test/mcp-server-bin.test.d.ts +0 -2
- package/dist/test/mcp-server-bin.test.d.ts.map +0 -1
- package/dist/test/mcp-server-bin.test.js +0 -142
- package/dist/test/mcp-server-bin.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-coalesced-content-parity.test.d.ts +0 -2
- package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +0 -1
- package/dist/test/mcpl-coalesced-content-parity.test.js +0 -532
- package/dist/test/mcpl-coalesced-content-parity.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 -238
- 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-rejection-log-lines.test.d.ts +0 -2
- package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +0 -1
- package/dist/test/mcpl-rejection-log-lines.test.js +0 -349
- package/dist/test/mcpl-rejection-log-lines.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-stderr-tail.test.d.ts +0 -2
- package/dist/test/mcpl-stderr-tail.test.d.ts.map +0 -1
- package/dist/test/mcpl-stderr-tail.test.js +0 -210
- package/dist/test/mcpl-stderr-tail.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-transport-close.test.d.ts +0 -2
- package/dist/test/mcpl-transport-close.test.d.ts.map +0 -1
- package/dist/test/mcpl-transport-close.test.js +0 -373
- package/dist/test/mcpl-transport-close.test.js.map +0 -1
- package/dist/test/mcpl-transport-spawn.test.d.ts +0 -2
- package/dist/test/mcpl-transport-spawn.test.d.ts.map +0 -1
- package/dist/test/mcpl-transport-spawn.test.js +0 -58
- package/dist/test/mcpl-transport-spawn.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 -259
- 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 -570
- 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 -595
- 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 -198
- package/dist/test/overbudget-drain-breaker.test.js.map +0 -1
- package/dist/test/pack-installs-alone.test.d.ts +0 -2
- package/dist/test/pack-installs-alone.test.d.ts.map +0 -1
- package/dist/test/pack-installs-alone.test.js +0 -284
- package/dist/test/pack-installs-alone.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 -16
- package/dist/test/physical-window-restart.test.d.ts.map +0 -1
- package/dist/test/physical-window-restart.test.js +0 -487
- 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 -1649
- 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 -2259
- 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 -226
- 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 -117
- 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-bump.test.d.ts +0 -2
- package/dist/test/release-bump.test.d.ts.map +0 -1
- package/dist/test/release-bump.test.js +0 -248
- package/dist/test/release-bump.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/release-holds.test.d.ts +0 -2
- package/dist/test/release-holds.test.d.ts.map +0 -1
- package/dist/test/release-holds.test.js +0 -456
- package/dist/test/release-holds.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-host-operations.test.d.ts +0 -20
- package/dist/test/stop-with-host-operations.test.d.ts.map +0 -1
- package/dist/test/stop-with-host-operations.test.js +0 -1174
- package/dist/test/stop-with-host-operations.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 -784
- 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 -3729
- 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 -652
- 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/unsent-compiles.test.d.ts +0 -2
- package/dist/test/unsent-compiles.test.d.ts.map +0 -1
- package/dist/test/unsent-compiles.test.js +0 -139
- package/dist/test/unsent-compiles.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 -4094
- 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
|
*/
|
|
@@ -160,6 +171,14 @@ export interface MountRuntime {
|
|
|
160
171
|
* syncs.
|
|
161
172
|
*/
|
|
162
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;
|
|
163
182
|
}
|
|
164
183
|
|
|
165
184
|
export type Scope =
|
|
@@ -182,8 +201,10 @@ export interface PassOptions {
|
|
|
182
201
|
*/
|
|
183
202
|
resume?: boolean;
|
|
184
203
|
/**
|
|
185
|
-
* The scope was named by a path sync
|
|
186
|
-
*
|
|
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.
|
|
187
208
|
*/
|
|
188
209
|
named?: boolean;
|
|
189
210
|
/**
|
|
@@ -206,6 +227,12 @@ type DiskFact =
|
|
|
206
227
|
hash: string;
|
|
207
228
|
size: number;
|
|
208
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;
|
|
209
236
|
fp: Fingerprint | null;
|
|
210
237
|
/** Text within limits, with its bytes: what disk→store sync may store. */
|
|
211
238
|
ingestible: Buffer | null;
|
|
@@ -226,7 +253,63 @@ function diskCandidate(d: DiskFact): Candidate | null {
|
|
|
226
253
|
|
|
227
254
|
function agreedFromDisk(d: DiskFact): Agreed {
|
|
228
255
|
if (d.kind !== 'file') return { kind: 'absent' };
|
|
229
|
-
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')}`;
|
|
230
313
|
}
|
|
231
314
|
|
|
232
315
|
function inScope(path: string, scope: Scope): boolean {
|
|
@@ -253,7 +336,7 @@ async function learnDisk(
|
|
|
253
336
|
const p = tracked.p;
|
|
254
337
|
const trusted = (fp: { dev: string }) => mount.ctimeTrustedDev !== null && fp.dev === mount.ctimeTrustedDev;
|
|
255
338
|
if (!opts.rehash && p?.kind === 'content' && fingerprintVouches(p.fp, seen.fp, trusted(seen.fp))) {
|
|
256
|
-
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 };
|
|
257
340
|
}
|
|
258
341
|
const nothingTracked = p === undefined && !tracked.hasStore && !tracked.hasIntent;
|
|
259
342
|
if (nothingTracked && seen.size > mount.view.maxFileSize) {
|
|
@@ -271,6 +354,7 @@ async function learnDisk(
|
|
|
271
354
|
hash: read.hash,
|
|
272
355
|
size: read.size,
|
|
273
356
|
mtimeMs: read.mtimeMs,
|
|
357
|
+
...(mount.modesObserved ? { mode: read.mode } : {}),
|
|
274
358
|
// A fingerprint is recorded only where it can later vouch for the bytes.
|
|
275
359
|
fp: trusted(read.fp) ? read.fp : null,
|
|
276
360
|
ingestible,
|
|
@@ -296,12 +380,40 @@ interface Verdict {
|
|
|
296
380
|
* is decided from it, and nothing at all is recorded (see module doc).
|
|
297
381
|
*/
|
|
298
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;
|
|
299
396
|
/** A path sync's adoption gives up this workspace state (see decide). */
|
|
300
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';
|
|
301
404
|
/**
|
|
302
405
|
* The last confirmed evidence decide resolved for the path, which an
|
|
303
406
|
* adoption's intent carries as its prior: a settled same-branch adoption's
|
|
304
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.
|
|
305
417
|
*/
|
|
306
418
|
prior?: Agreed;
|
|
307
419
|
}
|
|
@@ -332,14 +444,71 @@ function keepStoreSide(hasStore: boolean, bi: BranchIntent | null): EntryState {
|
|
|
332
444
|
* mutation boundary): a later write of the same content can't pass for the
|
|
333
445
|
* old adoption.
|
|
334
446
|
*/
|
|
335
|
-
function settleAdoption(p: Pending, s:
|
|
447
|
+
function settleAdoption(p: Pending, s: StoreSide | null): Agreed | 'forget' {
|
|
336
448
|
const sc: Candidate = s ? { kind: 'content', hash: s.hash } : ABSENT;
|
|
337
449
|
if (sameCandidate(sc, p.expect)) {
|
|
338
|
-
|
|
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 } : {}) };
|
|
339
459
|
}
|
|
340
460
|
return p.prior ?? 'forget';
|
|
341
461
|
}
|
|
342
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
|
+
|
|
343
512
|
/**
|
|
344
513
|
* The synchronous precondition of every workspace mutation: evidence owed a
|
|
345
514
|
* barrier is made durable first, and an adoption left pending on the
|
|
@@ -361,12 +530,30 @@ export function settleAdoptionBeforeMutation(
|
|
|
361
530
|
const p = agreement.get(mount.name, path);
|
|
362
531
|
if (p?.kind !== 'pending' || p.effect !== 'adopt' || p.branchId !== store.currentBranch().id) return;
|
|
363
532
|
const entry = store.treeGet(mount.treeStateId, path);
|
|
364
|
-
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);
|
|
365
534
|
if (settled === 'forget') agreement.forget(mount.name, path);
|
|
366
535
|
else agreement.set(mount.name, path, settled, { afterCommittedState: true });
|
|
367
536
|
agreement.barrier();
|
|
368
537
|
}
|
|
369
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
|
+
|
|
370
557
|
/**
|
|
371
558
|
* Resolve a pending intent against what was observed. Returns the evidence
|
|
372
559
|
* the rule should use, and whether that resolution should be recorded.
|
|
@@ -374,20 +561,34 @@ export function settleAdoptionBeforeMutation(
|
|
|
374
561
|
function resolvePending(
|
|
375
562
|
p: Pending,
|
|
376
563
|
dc: Candidate,
|
|
377
|
-
s:
|
|
564
|
+
s: StoreSide | null,
|
|
378
565
|
d: DiskFact,
|
|
379
566
|
branchId: string,
|
|
380
|
-
): { 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 } {
|
|
381
568
|
const prior = p.prior;
|
|
382
569
|
const priorCandidate = prior ? candidateOf(prior) : null;
|
|
383
570
|
if (p.effect === 'disk') {
|
|
384
571
|
if (sameCandidate(dc, p.expect)) {
|
|
385
572
|
// Our own effect shows on disk, but nothing proves it durable: decided
|
|
386
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.
|
|
387
576
|
const shown = p.expect.kind === 'absent' ? ({ kind: 'absent' } as Agreed) : agreedFromDisk(d);
|
|
388
|
-
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! };
|
|
389
591
|
}
|
|
390
|
-
if (priorCandidate && sameCandidate(dc, priorCandidate)) return { p: prior!, record: prior! };
|
|
391
592
|
// A write whose prior was unknown left no file: it never created one (a
|
|
392
593
|
// partial write leaves a file behind), so P is as unknown as before.
|
|
393
594
|
if (!prior && dc.kind === 'absent' && p.expect.kind === 'content') return { p: undefined, record: 'forget' };
|
|
@@ -418,13 +619,17 @@ function resolvePending(
|
|
|
418
619
|
* An explicit path sync: the store takes disk's state, whatever the evidence
|
|
419
620
|
* — a pending push's included. That chooses disk; it doesn't certify the push.
|
|
420
621
|
*/
|
|
421
|
-
function adoptDisk(d: Extract<DiskFact, { kind: 'absent' | 'file' }>, s:
|
|
622
|
+
function adoptDisk(d: Extract<DiskFact, { kind: 'absent' | 'file' }>, s: StoreSide | null): Verdict {
|
|
422
623
|
if (d.kind === 'absent') {
|
|
423
624
|
return { state: 'synced', ...(s ? { adopt: 'remove' as const, op: 'deleted' as const } : {}), p: { kind: 'absent' }, intent: {} };
|
|
424
625
|
}
|
|
425
626
|
// Disk agreeing with the store is disk's state already, whatever the file
|
|
426
|
-
// is: a binary or oversize file the workspace holds stays held.
|
|
427
|
-
|
|
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
|
+
}
|
|
428
633
|
if (d.ingestible) return { state: 'synced', adopt: 'ingest', op: s ? 'modified' : 'created', p: agreedFromDisk(d), intent: {} };
|
|
429
634
|
return { state: 'disk-only', ...(s ? { adopt: 'drop' as const, op: 'deleted' as const } : {}), p: 'forget', intent: {} };
|
|
430
635
|
}
|
|
@@ -432,7 +637,7 @@ function adoptDisk(d: Extract<DiskFact, { kind: 'absent' | 'file' }>, s: { hash:
|
|
|
432
637
|
/** The rule (see module doc). Pure. */
|
|
433
638
|
function decide(
|
|
434
639
|
d: DiskFact,
|
|
435
|
-
s:
|
|
640
|
+
s: StoreSide | null,
|
|
436
641
|
pIn: Physical | undefined,
|
|
437
642
|
bi: BranchIntent | null,
|
|
438
643
|
branchId: string,
|
|
@@ -461,12 +666,20 @@ function decide(
|
|
|
461
666
|
if (adopt) {
|
|
462
667
|
// What a path sync gives up is exactly what a sync without a path would
|
|
463
668
|
// have kept of the workspace's own (a draft, a deletion, a conflict's
|
|
464
|
-
// workspace side), and the verdict says which.
|
|
465
|
-
|
|
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
|
+
}
|
|
466
679
|
// A give-up is an adoption, or the intent it clears: a deletion or a
|
|
467
680
|
// conflict given up for a disk copy the workspace can't hold changes no
|
|
468
681
|
// tree entry, and is listed all the same.
|
|
469
|
-
if (taken.adopt || (bi && taken.intent !== undefined)) {
|
|
682
|
+
if (taken.adopt || taken.mode === 'adopt' || (bi && taken.intent !== undefined)) {
|
|
470
683
|
const kept = decide(d, s, pIn, bi, branchId, false);
|
|
471
684
|
if (WORKSPACE_KEPT.has(kept.state)) taken.discards = kept.state;
|
|
472
685
|
if (taken.adopt && kept.prior) taken.prior = kept.prior;
|
|
@@ -478,6 +691,9 @@ function decide(
|
|
|
478
691
|
// verdict below records newer evidence.
|
|
479
692
|
let p = pIn;
|
|
480
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;
|
|
481
697
|
let uncaptured = false;
|
|
482
698
|
if (p?.kind === 'pending') {
|
|
483
699
|
const resolved = resolvePending(p, dc, s, d, branchId);
|
|
@@ -486,13 +702,18 @@ function decide(
|
|
|
486
702
|
// follows from that, and nothing is recorded — no completion, no
|
|
487
703
|
// agreement, no branch-intent change — until a push confirms it.
|
|
488
704
|
const state: EntryState = sameCandidate(dc, sc) ? 'synced' : bi?.conflict ? 'conflict' : keepStoreSide(s !== null, bi);
|
|
489
|
-
return {
|
|
705
|
+
return {
|
|
706
|
+
state, note: UNCONFIRMED_NOTE, unconfirmed: true,
|
|
707
|
+
...(resolved.ownMode ? { ownMode: true as const } : {}),
|
|
708
|
+
};
|
|
490
709
|
}
|
|
491
710
|
p = resolved.p;
|
|
492
711
|
resolution = resolved.record;
|
|
712
|
+
if (resolution === undefined && p !== undefined && (p.kind === 'content' || p.kind === 'absent')) unrecorded = p;
|
|
493
713
|
uncaptured = resolved.uncaptured === true;
|
|
494
714
|
}
|
|
495
|
-
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);
|
|
496
717
|
|
|
497
718
|
// Agreement settles everything, a recorded conflict included.
|
|
498
719
|
if (sameCandidate(dc, sc)) {
|
|
@@ -508,11 +729,23 @@ function decide(
|
|
|
508
729
|
const recorded = p?.kind === 'content' ? p : null;
|
|
509
730
|
// Re-record only when P says something else, or a fresh fingerprint can
|
|
510
731
|
// replace one that no longer vouches. A fresh one exists only where it's
|
|
511
|
-
// trusted, so both are judged as trusted here.
|
|
512
|
-
|
|
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 ||
|
|
513
736
|
(agreed.fp !== undefined && fingerprintVouches(agreed.fp, agreed.fp, true) &&
|
|
514
737
|
!fingerprintVouches(recorded.fp, agreed.fp, true));
|
|
515
|
-
|
|
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 });
|
|
516
749
|
}
|
|
517
750
|
|
|
518
751
|
// A recorded conflict stays until it is resolved explicitly or disk and store converge.
|
|
@@ -655,12 +888,41 @@ async function gather(store: JsStore, agreement: DiskAgreement, mount: MountRunt
|
|
|
655
888
|
gathered.facts.set(path, { kind: 'unobserved', reason: rootUnavailable });
|
|
656
889
|
continue;
|
|
657
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
|
+
}
|
|
658
920
|
if (walk && provenAbsent(walk, path)) {
|
|
659
921
|
gathered.facts.set(path, { kind: 'absent' });
|
|
660
922
|
continue;
|
|
661
923
|
}
|
|
662
|
-
if (walk?.others.get(path) ===
|
|
663
|
-
gathered.facts.set(path, { kind: 'unobserved', reason:
|
|
924
|
+
if (walk?.others.get(path) === IGNORED) {
|
|
925
|
+
gathered.facts.set(path, { kind: 'unobserved', reason: IGNORED });
|
|
664
926
|
continue;
|
|
665
927
|
}
|
|
666
928
|
if (walk && !walk.files.has(path) && !walk.othersTaken.has(path)) {
|
|
@@ -748,11 +1010,10 @@ export async function reconcilePass(
|
|
|
748
1010
|
// Decide and apply — synchronously, so nothing interleaves.
|
|
749
1011
|
const branchId = store.currentBranch().id;
|
|
750
1012
|
result.branchId = branchId;
|
|
751
|
-
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 };
|
|
752
1014
|
const planned: Planned[] = [];
|
|
753
1015
|
for (const [path, d] of facts) {
|
|
754
|
-
const
|
|
755
|
-
const s = entry ? { hash: entry.blobHash, size: entry.size } : null;
|
|
1016
|
+
const s = storeSide(store.treeGet(mount.treeStateId, path));
|
|
756
1017
|
const p = agreement.get(mount.name, path);
|
|
757
1018
|
const bi = mount.intents.get(path);
|
|
758
1019
|
const verdict = decide(d, s, p, bi, branchId, opts.adopt?.(path) === true);
|
|
@@ -787,11 +1048,17 @@ export async function reconcilePass(
|
|
|
787
1048
|
const v = x.verdict;
|
|
788
1049
|
if (v.adopt === 'ingest' && x.d.kind === 'file' && x.d.ingestible) {
|
|
789
1050
|
const blobHash = store.storeBlob(x.d.ingestible, 'text/plain');
|
|
790
|
-
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) });
|
|
791
1052
|
committed = true;
|
|
792
1053
|
} else if ((v.adopt === 'remove' || v.adopt === 'drop') && x.s) {
|
|
793
1054
|
store.treeRemove(mount.treeStateId, x.path);
|
|
794
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;
|
|
795
1062
|
}
|
|
796
1063
|
if (v.conflict) {
|
|
797
1064
|
const record: ConflictRecord = { kind: v.conflict, at: Date.now(), disk: counterpartOf(store, x.d) };
|
|
@@ -802,8 +1069,9 @@ export async function reconcilePass(
|
|
|
802
1069
|
mount.intents.put(x.path, v.intent);
|
|
803
1070
|
committed = true;
|
|
804
1071
|
}
|
|
805
|
-
|
|
806
|
-
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 } : {}) });
|
|
807
1075
|
}
|
|
808
1076
|
|
|
809
1077
|
// 4. Completions and agreements, after the commits they assert are synced —
|
|
@@ -874,8 +1142,16 @@ export async function reconcilePass(
|
|
|
874
1142
|
// A link's own region hides only what lies beneath it: the link itself
|
|
875
1143
|
// is reported by path, with its own reason.
|
|
876
1144
|
const hides = tracked.some((p) => (region.kind !== 'symlink' && p === region.path) || p.startsWith(region.path + '/'));
|
|
877
|
-
|
|
878
|
-
//
|
|
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:
|
|
879
1155
|
// counted, so a sync that says 0 also says what it didn't look at
|
|
880
1156
|
// (agent-framework #276). A symlink region isn't counted: a link the
|
|
881
1157
|
// walk took is a candidate the pass reports by path, taken through the
|
|
@@ -896,7 +1172,9 @@ export interface PushOptions {
|
|
|
896
1172
|
/**
|
|
897
1173
|
* Overwrite (or unlink) a disk copy in conflict, or one that can't be
|
|
898
1174
|
* verified. A disk copy nothing has evidence about is overwritten only when
|
|
899
|
-
* `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).
|
|
900
1178
|
*/
|
|
901
1179
|
force?: boolean;
|
|
902
1180
|
/**
|
|
@@ -924,6 +1202,11 @@ export interface PushResult {
|
|
|
924
1202
|
deleted: string[];
|
|
925
1203
|
/** Refused paths, each with its reason. */
|
|
926
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 }>;
|
|
927
1210
|
/** Workspace deletions left on disk because applyDeletions wasn't given. */
|
|
928
1211
|
pendingDeletions: string[];
|
|
929
1212
|
/** The branch and sequence the push planned on: what disk now reflects.
|
|
@@ -934,11 +1217,16 @@ export interface PushResult {
|
|
|
934
1217
|
sequence?: number;
|
|
935
1218
|
}
|
|
936
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
|
+
|
|
937
1223
|
/**
|
|
938
1224
|
* Make disk hold the store's version of each path, under the same rule:
|
|
939
1225
|
* drafts are written, agreement is left alone, conflicts and unverifiable
|
|
940
1226
|
* disk copies are refused unless `force`, and a workspace deletion reaches
|
|
941
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).
|
|
942
1230
|
* A path nothing has evidence about is written only when `named`.
|
|
943
1231
|
* A path whose earlier push shows on disk unconfirmed is pushed again, so its
|
|
944
1232
|
* completion rests on barriers that succeeded.
|
|
@@ -962,7 +1250,7 @@ export async function pushPaths(
|
|
|
962
1250
|
): Promise<PushResult> {
|
|
963
1251
|
agreement.ensureReconciled();
|
|
964
1252
|
if (agreement.needsBarrier) agreement.barrier(); // as a pass does: nothing decided from unsynced evidence
|
|
965
|
-
const result: PushResult = { written: [], unchanged: [], deleted: [], skipped: [], pendingDeletions: [] };
|
|
1253
|
+
const result: PushResult = { written: [], unchanged: [], deleted: [], skipped: [], modesKept: [], pendingDeletions: [] };
|
|
966
1254
|
if (mount.readOnly) return result;
|
|
967
1255
|
// A root that isn't available takes no writes, forced or not: restoring the
|
|
968
1256
|
// store's files into an empty mountpoint would fill the disk the drive was
|
|
@@ -972,6 +1260,18 @@ export async function pushPaths(
|
|
|
972
1260
|
for (const path of paths) result.skipped.push({ path, reason: root.reason });
|
|
973
1261
|
return result;
|
|
974
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
|
+
}
|
|
975
1275
|
|
|
976
1276
|
type Plan = {
|
|
977
1277
|
path: string;
|
|
@@ -984,7 +1284,39 @@ export async function pushPaths(
|
|
|
984
1284
|
prior: Agreed | null;
|
|
985
1285
|
/** What the effect may find at the path. */
|
|
986
1286
|
expect: Expect;
|
|
987
|
-
} & (
|
|
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
|
+
);
|
|
988
1320
|
const plans: Plan[] = [];
|
|
989
1321
|
|
|
990
1322
|
// Observe disk.
|
|
@@ -1015,11 +1347,20 @@ export async function pushPaths(
|
|
|
1015
1347
|
result.sequence = store.currentSequence();
|
|
1016
1348
|
let recorded = false;
|
|
1017
1349
|
for (const [path, d] of facts) {
|
|
1018
|
-
const
|
|
1019
|
-
const s = entry ? { hash: entry.blobHash, size: entry.size } : null;
|
|
1350
|
+
const s = storeSide(store.treeGet(mount.treeStateId, path));
|
|
1020
1351
|
const p = agreement.get(mount.name, path);
|
|
1021
1352
|
const bi = mount.intents.get(path);
|
|
1022
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';
|
|
1023
1364
|
// What the effect may find: what this observation saw, unless forced.
|
|
1024
1365
|
const expect: Expect = opts.force || d.kind === 'unobserved' || d.kind === 'unhashed'
|
|
1025
1366
|
? { kind: 'any' }
|
|
@@ -1033,14 +1374,47 @@ export async function pushPaths(
|
|
|
1033
1374
|
result.skipped.push({
|
|
1034
1375
|
path,
|
|
1035
1376
|
reason: 'stale copy: disk changed since it last agreed with the workspace, which did not — ' +
|
|
1036
|
-
|
|
1377
|
+
`${takeDisk}, or ${forced}`,
|
|
1037
1378
|
});
|
|
1038
1379
|
continue;
|
|
1039
1380
|
}
|
|
1040
1381
|
const priorP = p && (p.kind === 'absent' || p.kind === 'content') ? p : null;
|
|
1041
1382
|
const blob = store.getBlob(s.hash);
|
|
1042
|
-
|
|
1043
|
-
|
|
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 });
|
|
1044
1418
|
continue;
|
|
1045
1419
|
}
|
|
1046
1420
|
// Whatever the verdict learned about disk (an agreement, a resolved
|
|
@@ -1053,8 +1427,22 @@ export async function pushPaths(
|
|
|
1053
1427
|
// The prior a new intent carries is the last confirmed evidence: for a
|
|
1054
1428
|
// path whose earlier push is unconfirmed, that push's own prior, never
|
|
1055
1429
|
// the bytes it wrote.
|
|
1056
|
-
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;
|
|
1057
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
|
+
}
|
|
1058
1446
|
if (v.state === 'synced' && !v.unconfirmed) {
|
|
1059
1447
|
if (v.intent !== undefined) {
|
|
1060
1448
|
mount.intents.put(path, v.intent);
|
|
@@ -1065,7 +1453,25 @@ export async function pushPaths(
|
|
|
1065
1453
|
}
|
|
1066
1454
|
if (v.state === 'unverified') {
|
|
1067
1455
|
if (!opts.force) {
|
|
1068
|
-
|
|
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
|
+
});
|
|
1069
1475
|
continue;
|
|
1070
1476
|
}
|
|
1071
1477
|
}
|
|
@@ -1084,15 +1490,20 @@ export async function pushPaths(
|
|
|
1084
1490
|
}
|
|
1085
1491
|
result.skipped.push({
|
|
1086
1492
|
path,
|
|
1087
|
-
|
|
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
|
|
1088
1499
|
? `disk holds a different copy, and nothing records which is newer (conflict: ${kind}) — ` +
|
|
1089
|
-
|
|
1500
|
+
`sync this path to adopt the disk version, or materialize this path with force ${s ? 'to overwrite it' : 'and applyDeletions to delete it'}`
|
|
1090
1501
|
: kind === 'interrupted'
|
|
1091
1502
|
? 'disk holds bytes that match neither what was being written there nor what disk last agreed with ' +
|
|
1092
1503
|
'(conflict: interrupted), as a write that failed or was cut off partway leaves — check the file, then ' +
|
|
1093
|
-
|
|
1504
|
+
`${forced}, or sync this path to adopt the disk copy as it is`
|
|
1094
1505
|
: `stale copy: disk changed since it last agreed with the workspace (conflict: ${kind}) — ` +
|
|
1095
|
-
|
|
1506
|
+
`sync this path to adopt the disk version, or ${forced}`,
|
|
1096
1507
|
});
|
|
1097
1508
|
continue;
|
|
1098
1509
|
}
|
|
@@ -1121,7 +1532,12 @@ export async function pushPaths(
|
|
|
1121
1532
|
continue;
|
|
1122
1533
|
}
|
|
1123
1534
|
if (!opts.applyDeletions) {
|
|
1124
|
-
|
|
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);
|
|
1125
1541
|
continue;
|
|
1126
1542
|
}
|
|
1127
1543
|
if (d.kind === 'absent') continue;
|
|
@@ -1133,28 +1549,115 @@ export async function pushPaths(
|
|
|
1133
1549
|
result.skipped.push({ path, reason: 'the workspace copy is missing from the store' });
|
|
1134
1550
|
continue;
|
|
1135
1551
|
}
|
|
1136
|
-
|
|
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
|
+
});
|
|
1137
1578
|
}
|
|
1138
1579
|
|
|
1139
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.
|
|
1140
1588
|
if (plans.length > 0) agreement.recordRootIdentity(mount.name, root.identity);
|
|
1141
|
-
plans.
|
|
1589
|
+
const announced = plans.filter((step) => step.kind !== 'chmod');
|
|
1590
|
+
announced.forEach((step, i) => {
|
|
1142
1591
|
agreement.intend(mount.name, step.path, {
|
|
1143
1592
|
effect: 'disk',
|
|
1144
1593
|
prior: step.prior,
|
|
1145
1594
|
expect: step.kind === 'write' ? { kind: 'content', hash: step.hash } : ABSENT,
|
|
1146
1595
|
branchId,
|
|
1147
|
-
|
|
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 });
|
|
1148
1607
|
});
|
|
1149
1608
|
|
|
1150
1609
|
// 2. Effects, each bound at its point of use.
|
|
1151
|
-
const effected: Array<{ step: Plan; dirs: string[] }> = [];
|
|
1610
|
+
const effected: Array<{ step: Plan; dirs: string[]; mode?: number; widenRefused?: boolean; modeKept?: boolean }> = [];
|
|
1152
1611
|
for (const step of plans) {
|
|
1153
1612
|
opts.beforeEffect?.(step.path);
|
|
1154
1613
|
try {
|
|
1155
1614
|
if (step.kind === 'write') {
|
|
1156
|
-
|
|
1157
|
-
|
|
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: [] });
|
|
1158
1661
|
} else {
|
|
1159
1662
|
// Its directory is synced whether this unlink removed the entry or
|
|
1160
1663
|
// found it gone: either way the completion claims a durable absence.
|
|
@@ -1167,7 +1670,8 @@ export async function pushPaths(
|
|
|
1167
1670
|
// exactly what it was, whatever kind that was.
|
|
1168
1671
|
agreement.restore(mount.name, step.path, step.before);
|
|
1169
1672
|
recorded = true;
|
|
1170
|
-
|
|
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}` });
|
|
1171
1675
|
} else {
|
|
1172
1676
|
// No completion: the intent stays pending, and the next observation
|
|
1173
1677
|
// resolves it from whatever disk then holds.
|
|
@@ -1184,7 +1688,8 @@ export async function pushPaths(
|
|
|
1184
1688
|
// 4. Completions. P is physical and global. Branch intent belongs to the
|
|
1185
1689
|
// branch this push planned on: it is settled only while that branch is
|
|
1186
1690
|
// still selected, and otherwise by convergence when the branch returns.
|
|
1187
|
-
for (const { step, dirs } of effected) {
|
|
1691
|
+
for (const { step, dirs, mode: written, widenRefused, modeKept } of effected) {
|
|
1692
|
+
const mode = mount.modesObserved ? written : undefined;
|
|
1188
1693
|
const failed = dirs.find((dir) => unsynced.has(dir));
|
|
1189
1694
|
if (failed !== undefined) {
|
|
1190
1695
|
result.skipped.push({
|
|
@@ -1194,11 +1699,61 @@ export async function pushPaths(
|
|
|
1194
1699
|
});
|
|
1195
1700
|
continue;
|
|
1196
1701
|
}
|
|
1197
|
-
|
|
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 } : {}) };
|
|
1198
1725
|
agreement.set(mount.name, step.path, value);
|
|
1199
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
|
+
}
|
|
1200
1753
|
const planned = store.currentBranch().id === branchId;
|
|
1201
|
-
if (step.kind === '
|
|
1754
|
+
if (step.kind === 'chmod') {
|
|
1755
|
+
result.written.push(step.path);
|
|
1756
|
+
} else if (step.kind === 'write') {
|
|
1202
1757
|
if (planned) mount.intents.update(step.path, (cur) => { const next = { ...cur }; delete next.conflict; delete next.origin; return next; });
|
|
1203
1758
|
result.written.push(step.path);
|
|
1204
1759
|
} else {
|