@north-light/crouter 0.3.312 → 0.3.313
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/dist/api/client.d.ts +27 -30
- package/dist/api/client.js +80 -67
- package/dist/api/dto/broker-ops.d.ts +10 -20
- package/dist/api/dto/canvas.d.ts +1 -10
- package/dist/api/dto/health.d.ts +113 -0
- package/dist/api/dto/human-requests.d.ts +14 -3
- package/dist/api/dto/human.d.ts +2 -48
- package/dist/api/dto/human.js +3 -2
- package/dist/api/dto/inbox.d.ts +14 -0
- package/dist/api/dto/mail.d.ts +43 -0
- package/dist/api/dto/nodes.d.ts +11 -8
- package/dist/api/dto/recovery.d.ts +96 -0
- package/dist/api/dto/reviews.d.ts +2 -2
- package/dist/api/routes.d.ts +9 -6
- package/dist/api/routes.js +11 -11
- package/dist/build-root.d.ts +4 -0
- package/dist/build-root.js +11 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
- package/dist/cli.js +10 -1
- package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
- package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
- package/dist/clients/attach/chrome/canvas-panels.js +1 -1
- package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
- package/dist/clients/attach/chrome/inbox-strip.js +17 -9
- package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
- package/dist/clients/attach/chrome/ticket-panel.js +77 -46
- package/dist/clients/attach/overlays/graph.js +6 -9
- package/dist/clients/attach/render/chat-view.d.ts +5 -4
- package/dist/clients/attach/render/chat-view.js +22 -13
- package/dist/clients/attach/render/page-block.d.ts +11 -7
- package/dist/clients/attach/render/page-block.js +64 -55
- package/dist/clients/attach/viewer.js +746 -735
- package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
- package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
- package/dist/clients/inbox/controller.d.ts +42 -14
- package/dist/clients/inbox/controller.js +240 -124
- package/dist/clients/inbox/dto-adapt.d.ts +28 -0
- package/dist/clients/inbox/dto-adapt.js +93 -0
- package/dist/clients/inbox/page-adapter.d.ts +5 -4
- package/dist/clients/inbox/page-adapter.js +12 -12
- package/dist/clients/inbox/resolve.d.ts +10 -5
- package/dist/clients/inbox/resolve.js +13 -12
- package/dist/clients/inbox/review/review-client.d.ts +0 -1
- package/dist/clients/inbox/review/review-client.js +0 -4
- package/dist/clients/inbox/review-adapter.d.ts +2 -1
- package/dist/clients/inbox/review-adapter.js +3 -5
- package/dist/clients/inbox/surface.d.ts +2 -2
- package/dist/clients/inbox/surface.js +19 -8
- package/dist/clients/inbox/tui.d.ts +3 -3
- package/dist/commands/__tests__/human.test.js +31 -15
- package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
- package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
- package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
- package/dist/commands/api-client.d.ts +5 -4
- package/dist/commands/api-client.js +13 -7
- package/dist/commands/canvas-history/shared.js +1 -1
- package/dist/commands/canvas-history.js +1 -1
- package/dist/commands/canvas-roster.js +1 -1
- package/dist/commands/canvas.js +1 -2
- package/dist/commands/human/feedback.js +20 -17
- package/dist/commands/human/prompts.js +45 -36
- package/dist/commands/human/queue.js +73 -123
- package/dist/commands/human/review.js +6 -16
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
- package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
- package/dist/commands/node-inspect-artifacts.js +1 -1
- package/dist/commands/node-lifecycle-revive.d.ts +2 -2
- package/dist/commands/node-lifecycle-revive.js +9 -19
- package/dist/commands/pkg/plugin-manage.js +0 -1
- package/dist/commands/profile/show.js +4 -12
- package/dist/commands/surface-tmux-spread.js +1 -5
- package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
- package/dist/commands/sys/branch.d.ts +2 -0
- package/dist/commands/sys/branch.js +19 -0
- package/dist/commands/sys/config.js +106 -33
- package/dist/commands/sys/daemon.d.ts +2 -0
- package/dist/commands/sys/daemon.js +130 -7
- package/dist/commands/sys/doctor.js +5 -15
- package/dist/commands/sys/migrate.js +21 -92
- package/dist/commands/sys/setup-settings-runner.js +4 -2
- package/dist/commands/sys.js +2 -15
- package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
- package/dist/core/__tests__/boot.test.js +32 -25
- package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
- package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
- package/dist/core/__tests__/canvas.test.js +18 -47
- package/dist/core/__tests__/child-death-wake.test.js +29 -7
- package/dist/core/__tests__/cli-help-contract.test.js +2 -0
- package/dist/core/__tests__/close.test.js +29 -10
- package/dist/core/__tests__/collectible.test.js +2 -1
- package/dist/core/__tests__/config-change-delta.test.js +2 -0
- package/dist/core/__tests__/context-intro.test.js +13 -1
- package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
- package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
- package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
- package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
- package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
- package/dist/core/__tests__/daemon-boot.test.js +51 -14
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
- package/dist/core/__tests__/focuses.test.js +10 -5
- package/dist/core/__tests__/helpers/harness.js +50 -18
- package/dist/core/__tests__/history-inbox.test.js +12 -16
- package/dist/core/__tests__/human-action-delivery.test.js +48 -28
- package/dist/core/__tests__/human-actions.test.js +7 -3
- package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
- package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
- package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
- package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
- package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
- package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
- package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
- package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
- package/dist/core/__tests__/integration/lifecycle-hooks.test.js +16 -3
- package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
- package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
- package/dist/core/__tests__/integration/revive.test.js +67 -21
- package/dist/core/__tests__/integration/spawn-root.test.js +51 -8
- package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
- package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
- package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
- package/dist/core/__tests__/kickoff.test.js +36 -39
- package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
- package/dist/core/__tests__/lifecycle.test.js +2 -1
- package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
- package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
- package/dist/core/__tests__/migration.test.js +145 -31
- package/dist/core/__tests__/model-pin-durability.test.js +4 -1
- package/dist/core/__tests__/model-routes-config.test.js +13 -5
- package/dist/core/__tests__/node-outcome.test.js +93 -26
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -1
- package/dist/core/__tests__/on-read-nested-store.test.js +2 -1
- package/dist/core/__tests__/passive-subscription.test.js +50 -157
- package/dist/core/__tests__/plugin-kinds.test.js +6 -4
- package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
- package/dist/core/__tests__/plugin-page-components.test.js +6 -4
- package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +4 -1
- package/dist/core/__tests__/prune.test.js +11 -5
- package/dist/core/__tests__/push-final-guard.test.js +14 -7
- package/dist/core/__tests__/relaunch-root.test.js +30 -2
- package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
- package/dist/core/__tests__/repository-root-identity.test.js +4 -0
- package/dist/core/__tests__/review-model-floor.test.js +6 -3
- package/dist/core/__tests__/revive-all.test.js +13 -3
- package/dist/core/__tests__/revive-capacity.test.js +26 -4
- package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
- package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
- package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
- package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
- package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
- package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
- package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
- package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
- package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +2 -1
- package/dist/core/__tests__/stop-guard.test.js +2 -1
- package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +18 -4
- package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
- package/dist/core/bootstrap.js +2 -0
- package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
- package/dist/core/broker-client/transport-socket.d.ts +1 -0
- package/dist/core/broker-client/transport-socket.js +13 -5
- package/dist/core/canvas/__tests__/attention.test.js +29 -35
- package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
- package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
- package/dist/core/canvas/attention.d.ts +2 -5
- package/dist/core/canvas/attention.js +15 -64
- package/dist/core/canvas/boot.d.ts +6 -7
- package/dist/core/canvas/boot.js +5 -9
- package/dist/core/canvas/canvas.d.ts +13 -109
- package/dist/core/canvas/canvas.js +140 -401
- package/dist/core/canvas/crons.d.ts +2 -0
- package/dist/core/canvas/crons.js +16 -15
- package/dist/core/canvas/daemon-owner.js +10 -0
- package/dist/core/canvas/db.d.ts +2 -2
- package/dist/core/canvas/db.js +2 -18
- package/dist/core/canvas/deliveries.d.ts +104 -0
- package/dist/core/canvas/deliveries.js +354 -0
- package/dist/core/canvas/executions.d.ts +29 -0
- package/dist/core/canvas/executions.js +68 -0
- package/dist/core/canvas/history.js +35 -35
- package/dist/core/canvas/index.d.ts +3 -1
- package/dist/core/canvas/index.js +3 -1
- package/dist/core/canvas/migrations.js +20 -12
- package/dist/core/canvas/nav-model.js +1 -3
- package/dist/core/canvas/nav-render.d.ts +4 -4
- package/dist/core/canvas/nav-render.js +11 -21
- package/dist/core/canvas/node-sql-migration.d.ts +3 -0
- package/dist/core/canvas/node-sql-migration.js +261 -0
- package/dist/core/canvas/paths.d.ts +2 -16
- package/dist/core/canvas/paths.js +2 -31
- package/dist/core/canvas/recovery.d.ts +76 -0
- package/dist/core/canvas/recovery.js +588 -0
- package/dist/core/canvas/remote-canvas-source.js +4 -0
- package/dist/core/canvas/render-source.d.ts +16 -27
- package/dist/core/canvas/render-source.js +16 -45
- package/dist/core/canvas/sessions.d.ts +33 -0
- package/dist/core/canvas/sessions.js +78 -0
- package/dist/core/canvas/types.d.ts +33 -33
- package/dist/core/canvas/types.js +2 -5
- package/dist/core/canvas/waits.d.ts +18 -0
- package/dist/core/canvas/waits.js +70 -0
- package/dist/core/canvas/worktrees.d.ts +40 -0
- package/dist/core/canvas/worktrees.js +93 -0
- package/dist/core/config.d.ts +17 -12
- package/dist/core/config.js +106 -155
- package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
- package/dist/core/feed/feed.d.ts +0 -6
- package/dist/core/feed/feed.js +68 -56
- package/dist/core/feed/inbox.d.ts +7 -81
- package/dist/core/feed/inbox.js +46 -378
- package/dist/core/feed/messages.d.ts +58 -0
- package/dist/core/feed/messages.js +217 -0
- package/dist/core/fs-utils.d.ts +4 -0
- package/dist/core/fs-utils.js +21 -1
- package/dist/core/human/answer.d.ts +1 -1
- package/dist/core/human/answer.js +1 -1
- package/dist/core/human/claim.d.ts +1 -1
- package/dist/core/human/claim.js +2 -4
- package/dist/core/human/component-docs.js +1 -1
- package/dist/core/human/feedback-companion.js +3 -2
- package/dist/core/human/feedback.d.ts +5 -8
- package/dist/core/human/feedback.js +57 -85
- package/dist/core/human/page-authoring.d.ts +30 -0
- package/dist/core/human/page-authoring.js +19 -0
- package/dist/core/human/requests.d.ts +136 -0
- package/dist/core/human/requests.js +515 -0
- package/dist/core/human/root.d.ts +0 -7
- package/dist/core/human/root.js +0 -17
- package/dist/core/human/scan.d.ts +2 -7
- package/dist/core/human/scan.js +48 -132
- package/dist/core/human/summary.d.ts +1 -1
- package/dist/core/human/summary.js +1 -1
- package/dist/core/human/tickets.d.ts +23 -83
- package/dist/core/human/tickets.js +92 -243
- package/dist/core/human/types.d.ts +5 -1
- package/dist/core/installed-plugins.js +8 -3
- package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
- package/dist/core/locked-file.d.ts +6 -3
- package/dist/core/locked-file.js +36 -9
- package/dist/core/memory/extensions.js +2 -0
- package/dist/core/preview-registry.js +0 -1
- package/dist/core/profiles/__tests__/env-store.test.js +2 -0
- package/dist/core/profiles/default-binding.d.ts +0 -4
- package/dist/core/profiles/default-binding.js +12 -58
- package/dist/core/profiles/env-store.js +8 -3
- package/dist/core/profiles/manifest.d.ts +2 -37
- package/dist/core/profiles/manifest.js +35 -161
- package/dist/core/profiles/select.js +3 -0
- package/dist/core/review/__tests__/capture-origin.test.js +13 -2
- package/dist/core/review/__tests__/comments.test.js +28 -10
- package/dist/core/review/__tests__/remap.test.js +28 -10
- package/dist/core/review/__tests__/stage-identity.test.js +17 -7
- package/dist/core/review/birth.d.ts +1 -1
- package/dist/core/review/birth.js +34 -5
- package/dist/core/review/comments.js +1 -1
- package/dist/core/review/document.js +1 -1
- package/dist/core/review/project.d.ts +5 -1
- package/dist/core/review/project.js +14 -36
- package/dist/core/review/realize.d.ts +3 -6
- package/dist/core/review/realize.js +51 -168
- package/dist/core/review/remap.js +1 -1
- package/dist/core/review/stage.d.ts +3 -2
- package/dist/core/review/stage.js +2 -3
- package/dist/core/review/store.d.ts +3 -21
- package/dist/core/review/store.js +140 -186
- package/dist/core/review/types.d.ts +0 -1
- package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
- package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
- package/dist/core/runtime/broker/auth-reload.js +9 -10
- package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
- package/dist/core/runtime/broker/daemon-ops.js +42 -9
- package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
- package/dist/core/runtime/broker/extension-abort.js +11 -0
- package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
- package/dist/core/runtime/broker/fault-retry.js +142 -95
- package/dist/core/runtime/broker/inbox.d.ts +4 -32
- package/dist/core/runtime/broker/inbox.js +4 -92
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +21 -12
- package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
- package/dist/core/runtime/broker-persona-guidance.js +2 -13
- package/dist/core/runtime/broker.js +28 -22
- package/dist/core/runtime/close.d.ts +3 -9
- package/dist/core/runtime/close.js +16 -27
- package/dist/core/runtime/fault.d.ts +3 -93
- package/dist/core/runtime/fault.js +9 -419
- package/dist/core/runtime/kickoff.d.ts +3 -6
- package/dist/core/runtime/kickoff.js +12 -18
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/lifecycle.js +40 -7
- package/dist/core/runtime/placement.js +2 -2
- package/dist/core/runtime/recycle.js +8 -2
- package/dist/core/runtime/reset.js +22 -6
- package/dist/core/runtime/revive.js +226 -218
- package/dist/core/runtime/spawn.js +14 -3
- package/dist/core/scoped-state/db.d.ts +7 -0
- package/dist/core/scoped-state/db.js +92 -0
- package/dist/core/scoped-state/migrate.d.ts +11 -0
- package/dist/core/scoped-state/migrate.js +198 -0
- package/dist/core/scoped-state/paths.d.ts +9 -0
- package/dist/core/scoped-state/paths.js +27 -0
- package/dist/core/scoped-state/profiles.d.ts +12 -0
- package/dist/core/scoped-state/profiles.js +62 -0
- package/dist/core/scoped-state/providers.d.ts +16 -0
- package/dist/core/scoped-state/providers.js +8 -0
- package/dist/core/scoped-state/schema.d.ts +3 -0
- package/dist/core/scoped-state/schema.js +26 -0
- package/dist/core/scoped-state/settings.d.ts +28 -0
- package/dist/core/scoped-state/settings.js +82 -0
- package/dist/core/subscription-state.d.ts +29 -48
- package/dist/core/subscription-state.js +176 -712
- package/dist/core/worktree-close.js +4 -4
- package/dist/core/worktree-mutation-async.js +2 -2
- package/dist/core/worktree-sweep.js +11 -16
- package/dist/core/worktree.js +6 -12
- package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
- package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
- package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
- package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
- package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +39 -15
- package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
- package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
- package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
- package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
- package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
- package/dist/daemon/api/__tests__/model-config.test.js +8 -8
- package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
- package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
- package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
- package/dist/daemon/api/__tests__/profile-launch-gates.test.js +4 -1
- package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
- package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
- package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
- package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
- package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
- package/dist/daemon/api/__tests__/seam/profile-delete.test.js +27 -41
- package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
- package/dist/daemon/api/bridge.js +1 -1
- package/dist/daemon/api/handlers/bash-jobs.js +6 -7
- package/dist/daemon/api/handlers/broker-ops.js +47 -84
- package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
- package/dist/daemon/api/handlers/broker-recovery.js +251 -0
- package/dist/daemon/api/handlers/canvas.js +2 -44
- package/dist/daemon/api/handlers/daemon.js +41 -0
- package/dist/daemon/api/handlers/feedback-comments.js +2 -2
- package/dist/daemon/api/handlers/health.js +10 -5
- package/dist/daemon/api/handlers/human-requests.js +168 -188
- package/dist/daemon/api/handlers/human.js +3 -151
- package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
- package/dist/daemon/api/handlers/inbox-page.js +89 -0
- package/dist/daemon/api/handlers/inbox.js +21 -120
- package/dist/daemon/api/handlers/messages.d.ts +1 -0
- package/dist/daemon/api/handlers/messages.js +73 -15
- package/dist/daemon/api/handlers/node-outcomes.js +58 -13
- package/dist/daemon/api/handlers/nodes.js +7 -13
- package/dist/daemon/api/handlers/reviews.js +4 -29
- package/dist/daemon/api/map.d.ts +25 -3
- package/dist/daemon/api/map.js +84 -14
- package/dist/daemon/api/server.js +68 -9
- package/dist/daemon/control.d.ts +18 -0
- package/dist/daemon/cron/disposition.d.ts +2 -0
- package/dist/daemon/cron/disposition.js +8 -5
- package/dist/daemon/cron/settle.js +37 -13
- package/dist/daemon/cron/sinks.d.ts +18 -1
- package/dist/daemon/cron/sinks.js +47 -93
- package/dist/daemon/crtrd-cli.js +4 -19
- package/dist/daemon/crtrd.d.ts +2 -0
- package/dist/daemon/crtrd.js +206 -81
- package/dist/daemon/fleet.js +5 -6
- package/dist/daemon/human/finish.d.ts +12 -24
- package/dist/daemon/human/finish.js +88 -285
- package/dist/daemon/human/settle.d.ts +40 -0
- package/dist/daemon/human/settle.js +89 -0
- package/dist/daemon/human/sweep.d.ts +20 -5
- package/dist/daemon/human/sweep.js +56 -60
- package/dist/daemon/manage.d.ts +21 -16
- package/dist/daemon/manage.js +85 -51
- package/dist/daemon/messaging/node-message.d.ts +1 -1
- package/dist/daemon/messaging/node-message.js +9 -12
- package/dist/daemon/park-activity.js +3 -3
- package/dist/daemon/reconcilers/bash-deadline.js +6 -7
- package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
- package/dist/daemon/reconcilers/broker-supervision.js +41 -19
- package/dist/daemon/reconcilers/controller-death.js +6 -18
- package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
- package/dist/daemon/reconcilers/delivery-lane.js +130 -0
- package/dist/daemon/reconcilers/live-obligation.js +3 -3
- package/dist/daemon/reconcilers/node-deadline.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
- package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
- package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
- package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
- package/dist/daemon/review/companion.js +5 -11
- package/dist/daemon/review/deliver.d.ts +1 -5
- package/dist/daemon/review/deliver.js +3 -52
- package/dist/daemon/review/finish.d.ts +2 -27
- package/dist/daemon/review/finish.js +79 -263
- package/dist/daemon/review/sweep.d.ts +3 -10
- package/dist/daemon/review/sweep.js +13 -131
- package/dist/daemon/startup-policy.d.ts +0 -5
- package/dist/daemon/startup-policy.js +0 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
- package/dist/migrations/__tests__/activation.test.js +9 -6
- package/dist/migrations/activation.d.ts +3 -2
- package/dist/migrations/activation.js +2 -4
- package/dist/migrations/corpus.d.ts +2 -5
- package/dist/migrations/corpus.js +9 -20
- package/dist/migrations/runner.d.ts +3 -1
- package/dist/migrations/runner.js +6 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
- package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
- package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
- package/dist/pi-extensions/canvas-passive-context.js +85 -37
- package/dist/pi-extensions/canvas-stophook.js +47 -31
- package/dist/shared/env.d.ts +1 -0
- package/dist/shared/env.js +3 -0
- package/package.json +1 -1
- package/runtime.lock.json +5 -5
- package/dist/clients/attach/__tests__/page-block.test.js +0 -54
- package/dist/clients/inbox/review-visibility.d.ts +0 -5
- package/dist/clients/inbox/review-visibility.js +0 -19
- package/dist/commands/canvas-rebuild-index.d.ts +0 -2
- package/dist/commands/canvas-rebuild-index.js +0 -46
- package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
- package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
- package/dist/core/__tests__/human-deliver.test.js +0 -256
- package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
- package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
- package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
- package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
- package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
- package/dist/core/__tests__/phase4-review-store.test.js +0 -144
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
- package/dist/core/canvas/human-deliveries.d.ts +0 -39
- package/dist/core/canvas/human-deliveries.js +0 -75
- package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
- package/dist/core/canvas/node-outcome-deliveries.js +0 -159
- package/dist/core/feed/passive.d.ts +0 -3
- package/dist/core/feed/passive.js +0 -53
- package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
- package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
- package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
- package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
- package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
- package/dist/core/human/__tests__/page-tickets.test.js +0 -161
- package/dist/core/review/ticket-filter.d.ts +0 -17
- package/dist/core/review/ticket-filter.js +0 -21
- package/dist/core/runtime/broker/passive.d.ts +0 -17
- package/dist/core/runtime/broker/passive.js +0 -44
- package/dist/core/runtime/busy.d.ts +0 -46
- package/dist/core/runtime/busy.js +0 -150
- package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
- package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
- package/dist/daemon/human/deliver-action.d.ts +0 -3
- package/dist/daemon/human/deliver-action.js +0 -9
- package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
- package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
- package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
- package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
- package/dist/daemon/startup-block-marker.d.ts +0 -25
- package/dist/daemon/startup-block-marker.js +0 -99
- /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
- /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
- /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { openDb, withCanvasWrite } from './db.js';
|
|
2
|
+
function json(value) {
|
|
3
|
+
const encoded = JSON.stringify(value);
|
|
4
|
+
if (encoded === undefined)
|
|
5
|
+
throw new TypeError('worktree value must be JSON-serializable');
|
|
6
|
+
return encoded;
|
|
7
|
+
}
|
|
8
|
+
function parse(value) {
|
|
9
|
+
if (typeof value !== 'string')
|
|
10
|
+
return undefined;
|
|
11
|
+
return JSON.parse(value);
|
|
12
|
+
}
|
|
13
|
+
export function readWorktree(nodeId) {
|
|
14
|
+
const row = openDb().prepare('SELECT * FROM worktrees WHERE node_id = ?').get(nodeId);
|
|
15
|
+
if (row === undefined)
|
|
16
|
+
return null;
|
|
17
|
+
const sweepObserved = parse(row['sweep_observed_json']);
|
|
18
|
+
const attempts = row['attempts'];
|
|
19
|
+
const sweep = sweepObserved === undefined ? undefined : {
|
|
20
|
+
reason: row['sweep_reason'],
|
|
21
|
+
...(row['sweep_detail'] === null ? {} : { detail: row['sweep_detail'] }),
|
|
22
|
+
observed: sweepObserved,
|
|
23
|
+
attempts,
|
|
24
|
+
last_attempt: row['sweep_last_at'],
|
|
25
|
+
next_attempt: row['next_attempt_at'],
|
|
26
|
+
...(row['quarantined'] === 1 ? { quarantined: true } : {}),
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
state: row['state'],
|
|
30
|
+
...(row['cleanup_state'] === null ? {} : { cleanup: row['cleanup_state'] }),
|
|
31
|
+
path: row['path'],
|
|
32
|
+
branch: row['branch'],
|
|
33
|
+
repo_root: row['repo_root'],
|
|
34
|
+
base_ref: row['base_ref'],
|
|
35
|
+
base_sha: row['base_sha'],
|
|
36
|
+
created: row['created_at'],
|
|
37
|
+
...(row['closed_at'] === null ? {} : { closed: row['closed_at'] }),
|
|
38
|
+
...(parse(row['abandoned_json']) === undefined ? {} : { abandoned: parse(row['abandoned_json']) }),
|
|
39
|
+
...(parse(row['land_intent_json']) === undefined ? {} : { land_intent: parse(row['land_intent_json']) }),
|
|
40
|
+
...(sweep === undefined ? {} : { sweep }),
|
|
41
|
+
...(parse(row['disposal_json']) === undefined ? {} : { disposal: parse(row['disposal_json']) }),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function openWorktree(nodeId, worktree, db = openDb()) {
|
|
45
|
+
db.prepare(`INSERT INTO worktrees (node_id, state, cleanup_state, path, branch, repo_root, base_ref, base_sha, created_at, closed_at, next_attempt_at, attempts, quarantined, sweep_reason, sweep_detail, sweep_last_at, sweep_observed_json, abandoned_json, land_intent_json, disposal_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL)`).run(nodeId, worktree.state, worktree.cleanup ?? null, worktree.path, worktree.branch, worktree.repo_root, worktree.base_ref, worktree.base_sha, worktree.created, worktree.closed ?? null);
|
|
46
|
+
}
|
|
47
|
+
export function closeWorktree(nodeId, input) {
|
|
48
|
+
openDb().prepare("UPDATE worktrees SET state = 'closed', cleanup_state = ?, closed_at = ?, land_intent_json = NULL, sweep_reason = NULL, sweep_detail = NULL, sweep_last_at = NULL, sweep_observed_json = NULL, next_attempt_at = NULL, attempts = 0, quarantined = 0 WHERE node_id = ?").run(input.cleanup, input.closedAt, nodeId);
|
|
49
|
+
}
|
|
50
|
+
/** Complete a worktree close and repoint the node in the same SQL transaction.
|
|
51
|
+
* A close invalidates the active transcript because Pi remains in the old
|
|
52
|
+
* checkout; the next launch starts from the durable base checkout. */
|
|
53
|
+
export function closeWorktreeAndRepoint(nodeId, input) {
|
|
54
|
+
withCanvasWrite((db) => {
|
|
55
|
+
db.prepare('UPDATE nodes SET cwd = ? WHERE node_id = ?').run(input.cwd, nodeId);
|
|
56
|
+
if (input.unbindSession)
|
|
57
|
+
db.prepare('UPDATE sessions SET unbound_at = ? WHERE node_id = ? AND unbound_at IS NULL').run(input.closedAt, nodeId);
|
|
58
|
+
db.prepare("UPDATE worktrees SET state = 'closed', cleanup_state = ?, closed_at = ?, land_intent_json = NULL, sweep_reason = NULL, sweep_detail = NULL, sweep_last_at = NULL, sweep_observed_json = NULL, next_attempt_at = NULL, attempts = 0, quarantined = 0 WHERE node_id = ?").run(input.cleanup, input.closedAt, nodeId);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function abandonWorktree(nodeId, abandoned) {
|
|
62
|
+
openDb().prepare("UPDATE worktrees SET state = 'abandoned', abandoned_json = ? WHERE node_id = ? AND abandoned_json IS NULL").run(json(abandoned), nodeId);
|
|
63
|
+
}
|
|
64
|
+
export function abandonWorktreeAndRepoint(nodeId, input) {
|
|
65
|
+
withCanvasWrite((db) => {
|
|
66
|
+
db.prepare('UPDATE nodes SET cwd = ? WHERE node_id = ?').run(input.cwd, nodeId);
|
|
67
|
+
db.prepare("UPDATE worktrees SET state = 'abandoned', abandoned_json = ? WHERE node_id = ? AND abandoned_json IS NULL").run(json(input.abandoned), nodeId);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export function recordLandIntent(nodeId, intent) {
|
|
71
|
+
openDb().prepare('UPDATE worktrees SET land_intent_json = ? WHERE node_id = ? AND land_intent_json IS NULL').run(json(intent), nodeId);
|
|
72
|
+
}
|
|
73
|
+
export function recordDisposalGrant(nodeId, disposal) {
|
|
74
|
+
openDb().prepare('UPDATE worktrees SET disposal_json = ? WHERE node_id = ? AND disposal_json IS NULL').run(json(disposal), nodeId);
|
|
75
|
+
}
|
|
76
|
+
export function recordSweepAttempt(nodeId, input) {
|
|
77
|
+
openDb().prepare('UPDATE worktrees SET sweep_reason = ?, sweep_detail = ?, sweep_observed_json = ?, attempts = ?, sweep_last_at = ?, next_attempt_at = ?, quarantined = ? WHERE node_id = ?').run(input.reason, input.detail ?? null, json(input.observed), input.attempts, input.lastAttemptAt, input.nextAttemptAt, input.quarantined ? 1 : 0, nodeId);
|
|
78
|
+
}
|
|
79
|
+
export function completeCleanup(nodeId) {
|
|
80
|
+
openDb().prepare("UPDATE worktrees SET state = 'closed', cleanup_state = 'complete', sweep_reason = NULL, sweep_detail = NULL, sweep_last_at = NULL, sweep_observed_json = NULL, next_attempt_at = NULL, attempts = 0, quarantined = 0 WHERE node_id = ?").run(nodeId);
|
|
81
|
+
}
|
|
82
|
+
export function hasUnreconciledWorktree(nodeId) {
|
|
83
|
+
return openDb().prepare("SELECT 1 FROM worktrees WHERE node_id = ? AND state != 'abandoned' AND (state = 'open' OR cleanup_state IS NULL OR cleanup_state != 'complete')").get(nodeId) !== undefined;
|
|
84
|
+
}
|
|
85
|
+
export function sweepCandidates(now) {
|
|
86
|
+
return openDb().prepare(`SELECT w.node_id FROM worktrees w JOIN nodes n ON n.node_id = w.node_id WHERE n.status IN ('done', 'dead', 'canceled') AND n.frozen_at IS NULL AND w.state IN ('open', 'closed') AND (w.state = 'open' OR w.cleanup_state IS NULL OR w.cleanup_state != 'complete') AND (w.state = 'closed' OR (n.status = 'done' AND n.final_report IS NOT NULL)) AND (w.next_attempt_at IS NULL OR w.next_attempt_at <= ?) ORDER BY w.next_attempt_at, w.node_id`).all(now).map(({ node_id }) => node_id);
|
|
87
|
+
}
|
|
88
|
+
export function quarantinedWorktrees() {
|
|
89
|
+
return openDb().prepare('SELECT node_id FROM worktrees WHERE quarantined = 1 ORDER BY sweep_last_at').all().flatMap(({ node_id }) => {
|
|
90
|
+
const worktree = readWorktree(node_id);
|
|
91
|
+
return worktree === null ? [] : [{ nodeId: node_id, worktree }];
|
|
92
|
+
});
|
|
93
|
+
}
|
package/dist/core/config.d.ts
CHANGED
|
@@ -3,23 +3,28 @@ import { type BindingId } from './keybindings/catalog.js';
|
|
|
3
3
|
export declare function configPath(scope: Scope): string | null;
|
|
4
4
|
export declare function statePath(scope: Scope): string | null;
|
|
5
5
|
export declare function readConfig(scope: Scope): ResolvedScopeConfig;
|
|
6
|
-
/** Read the config that lives at an EXACT scope-root dir, rather than the
|
|
7
|
-
* resolved nearest root for a scope. Lets a caller iterate each visible
|
|
8
|
-
* project root's own config when several are in scope (nested `.crouter` dirs
|
|
9
|
-
* or a profile-widened stack), instead of collapsing them onto the nearest. */
|
|
10
6
|
export declare function readConfigAtRoot(root: string): ResolvedScopeConfig;
|
|
11
7
|
export declare function readState(scope: Scope): ScopeState;
|
|
8
|
+
export type RawSettingsTarget = {
|
|
9
|
+
scope: 'user';
|
|
10
|
+
} | {
|
|
11
|
+
scope: 'profile';
|
|
12
|
+
profileId: string;
|
|
13
|
+
};
|
|
14
|
+
export declare class InvalidRawSettingsError extends Error {
|
|
15
|
+
constructor(cause: unknown);
|
|
16
|
+
}
|
|
17
|
+
export declare function readRawSettingsForTarget(target: RawSettingsTarget): {
|
|
18
|
+
settings: Record<string, unknown> | null;
|
|
19
|
+
revision: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function replaceRawSettingsForTarget(target: RawSettingsTarget, next: Record<string, unknown>, ifRevision: string): {
|
|
22
|
+
revision: string;
|
|
23
|
+
changed: boolean;
|
|
24
|
+
};
|
|
12
25
|
type RawConfig = Record<string, unknown>;
|
|
13
26
|
export declare function updateRawConfigAtomically(scope: Scope, mutate: (latest: RawConfig) => RawConfig): RawConfig;
|
|
14
27
|
export declare function writeConfig(scope: Scope, config: ScopeConfig): void;
|
|
15
|
-
/** Persist a RAW PARTIAL config to a scope's config.json exactly as given.
|
|
16
|
-
* Unlike `writeConfig`, this makes no promise that every field is present --
|
|
17
|
-
* callers pass the sparse partial that should live on disk, so an untouched
|
|
18
|
-
* key stays absent (and `readConfig`/`mergeConfig` re-derive it live from
|
|
19
|
-
* whichever build is running). This is the single central primitive every
|
|
20
|
-
* mutation path funnels through so no writer ever freezes a fully
|
|
21
|
-
* materialized `modelLadders` (concrete model ids that go stale across
|
|
22
|
-
* builds -- the live-Hearth freeze bug) onto disk. */
|
|
23
28
|
export declare function writeRawConfig(scope: Scope, config: Partial<ScopeConfig>): void;
|
|
24
29
|
export declare function writeState(scope: Scope, state: ScopeState): void;
|
|
25
30
|
export declare function ensureScopeInitialized(scope: Scope, root: string): void;
|
package/dist/core/config.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { envProfileId } from '../shared/env.js';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
2
|
+
import { basename, dirname, join } from 'node:path';
|
|
3
3
|
import { isDeepStrictEqual } from 'node:util';
|
|
4
4
|
import { CONFIG_FILE, STATE_FILE, CONDENSED_HISTORY_MODES, MOUSE_MODE_DEFAULTS, WHIP_MESSAGE_MODES, DEFAULT_WHIP_MESSAGES, defaultScopeConfig, defaultScopeState, defaultModelLaddersConfig, defaultKindsConfig, defaultRemoteCanvasConfig } from '../types.js';
|
|
5
5
|
import { BINDING_CATALOG, BINDING_IDS, isAttachPaneBinding } from './keybindings/catalog.js';
|
|
6
|
-
import { usage } from './errors.js';
|
|
6
|
+
import { notFound, usage } from './errors.js';
|
|
7
7
|
import { emitEvent } from './events/emit.js';
|
|
8
|
-
import { atomicWriteJson, readJsonIfExists,
|
|
9
|
-
import { scopeRoot, requireScopeRoot, findProjectScopeRoots } from './scope.js';
|
|
8
|
+
import { atomicWriteJson, readJsonIfExists, ensureDir } from './fs-utils.js';
|
|
9
|
+
import { scopeRoot, requireScopeRoot, findProjectScopeRoots, userScopeRoot } from './scope.js';
|
|
10
10
|
import { listInstalledPluginsInRoot } from './installed-plugins.js';
|
|
11
|
-
import { profileRoot } from './profiles/manifest.js';
|
|
11
|
+
import { profileRoot, listProfiles } from './profiles/manifest.js';
|
|
12
|
+
import { canonicalRoot, projectStore, userStore } from './scoped-state/paths.js';
|
|
13
|
+
import { convertLegacyScopedStore, initializeFreshScopedStore } from './scoped-state/migrate.js';
|
|
14
|
+
import { mutateRawSettings, readBookkeeping, readRawSettings, readRawSettingsWithRevision, replaceRawSettings, writeBookkeeping, writeRawSettings } from './scoped-state/settings.js';
|
|
12
15
|
import { withExclusiveDirectoryLock } from './exclusive-lock.js';
|
|
13
16
|
import { SCOPE_CONFIG_KEYS } from './user-settings.js';
|
|
14
17
|
import { normalizeWorkingGerunds } from '../shared/working-activity.js';
|
|
@@ -16,6 +19,10 @@ import { mergePageComponentLayers, normalizePageComponents } from './human/page-
|
|
|
16
19
|
function configPathFor(root) {
|
|
17
20
|
return join(root, CONFIG_FILE);
|
|
18
21
|
}
|
|
22
|
+
function withConfigPathLock(path, run) {
|
|
23
|
+
ensureDir(dirname(path));
|
|
24
|
+
return withExclusiveDirectoryLock(`${path}.lock`, () => run(path), { timeoutError: () => new Error(`timed out waiting for config lock: ${path}`) });
|
|
25
|
+
}
|
|
19
26
|
function statePathFor(root) {
|
|
20
27
|
return join(root, STATE_FILE);
|
|
21
28
|
}
|
|
@@ -27,100 +34,83 @@ export function statePath(scope) {
|
|
|
27
34
|
const root = scopeRoot(scope);
|
|
28
35
|
return root ? statePathFor(root) : null;
|
|
29
36
|
}
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
37
|
+
function settingsOwnerForRoot(root) {
|
|
38
|
+
const canonical = canonicalRoot(root);
|
|
39
|
+
const userRoot = canonicalRoot(userScopeRoot());
|
|
40
|
+
if (canonical === userRoot)
|
|
41
|
+
return { store: userStore(userRoot), key: 'user', kind: 'user', root: userRoot };
|
|
42
|
+
// Project roots can be classified without opening the user store. This keeps a
|
|
43
|
+
// fresh project usable when an unrelated legacy user root still needs conversion.
|
|
44
|
+
if (basename(canonical) === '.crouter') {
|
|
45
|
+
convertLegacyScopedStore({ kind: 'project', root: canonical });
|
|
46
|
+
return { store: projectStore(canonical), key: 'project', kind: 'project', root: canonical };
|
|
47
|
+
}
|
|
48
|
+
for (const profile of listProfiles()) {
|
|
49
|
+
const profilePath = canonicalRoot(profileRoot(profile.profileId));
|
|
50
|
+
if (canonical === profilePath)
|
|
51
|
+
return { store: userStore(userRoot), key: `profile:${profile.profileId}`, kind: 'profile', root: profilePath, profileId: profile.profileId };
|
|
52
|
+
}
|
|
53
|
+
throw new Error(`settings root is not a recognized owner: ${root}`);
|
|
54
|
+
}
|
|
55
|
+
function readRawAtRoot(root) {
|
|
56
|
+
const owner = settingsOwnerForRoot(root);
|
|
57
|
+
if (owner.kind === 'project')
|
|
58
|
+
return readJsonIfExists(configPathFor(owner.root));
|
|
59
|
+
return readRawSettings(owner);
|
|
60
|
+
}
|
|
61
|
+
function writeRawAtRoot(root, raw) {
|
|
62
|
+
const owner = settingsOwnerForRoot(root);
|
|
63
|
+
if (owner.kind === 'project') {
|
|
64
|
+
withConfigPathLock(configPathFor(owner.root), (path) => atomicWriteJson(path, raw));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
writeRawSettings(owner, raw);
|
|
68
|
+
}
|
|
69
|
+
function mutateRawAtRoot(root, mutate) {
|
|
70
|
+
const owner = settingsOwnerForRoot(root);
|
|
71
|
+
if (owner.kind === 'project')
|
|
72
|
+
return withConfigPathLock(configPathFor(owner.root), (path) => { const result = mutate(readJsonIfExists(path) ?? {}); atomicWriteJson(path, result.next); return result.value; });
|
|
73
|
+
return mutateRawSettings(owner, (raw) => mutate(raw));
|
|
74
|
+
}
|
|
75
|
+
export function readConfig(scope) { const root = scopeRoot(scope); return root ? readConfigAtRoot(root) : defaultScopeConfig(); }
|
|
76
|
+
export function readConfigAtRoot(root) { const existing = readRawAtRoot(root); return existing ? mergeConfig(existing) : defaultScopeConfig(); }
|
|
77
|
+
export function readState(scope) { const root = scopeRoot(scope); if (!root)
|
|
78
|
+
return defaultScopeState(); return readBookkeeping(settingsOwnerForRoot(root)); }
|
|
79
|
+
export class InvalidRawSettingsError extends Error {
|
|
80
|
+
constructor(cause) {
|
|
81
|
+
super(`settings cannot be merged: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
82
|
+
this.name = 'InvalidRawSettingsError';
|
|
83
|
+
}
|
|
65
84
|
}
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
function rawSettingsOwnerForTarget(target) {
|
|
86
|
+
if (target.scope === 'user')
|
|
87
|
+
return settingsOwnerForRoot(userScopeRoot());
|
|
88
|
+
if (!listProfiles().some((profile) => profile.profileId === target.profileId)) {
|
|
89
|
+
throw notFound(`profile not found: ${target.profileId}`, { received: target.profileId, next: 'Run `crtr profile list` to see available profiles.' });
|
|
90
|
+
}
|
|
91
|
+
return settingsOwnerForRoot(profileRoot(target.profileId));
|
|
69
92
|
}
|
|
70
|
-
export function
|
|
71
|
-
return
|
|
72
|
-
const latest = readJsonIfExists(path) ?? {};
|
|
73
|
-
const next = mutate(latest);
|
|
74
|
-
atomicWriteJson(path, next);
|
|
75
|
-
return next;
|
|
76
|
-
});
|
|
93
|
+
export function readRawSettingsForTarget(target) {
|
|
94
|
+
return readRawSettingsWithRevision(rawSettingsOwnerForTarget(target));
|
|
77
95
|
}
|
|
78
|
-
export function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
* mutation path funnels through so no writer ever freezes a fully
|
|
87
|
-
* materialized `modelLadders` (concrete model ids that go stale across
|
|
88
|
-
* builds -- the live-Hearth freeze bug) onto disk. */
|
|
89
|
-
export function writeRawConfig(scope, config) {
|
|
90
|
-
withConfigLock(scope, (path) => atomicWriteJson(path, config));
|
|
91
|
-
}
|
|
92
|
-
export function writeState(scope, state) {
|
|
93
|
-
const root = requireScopeRoot(scope);
|
|
94
|
-
ensureDir(root);
|
|
95
|
-
writeJson(statePathFor(root), state);
|
|
96
|
-
}
|
|
97
|
-
export function ensureScopeInitialized(scope, root) {
|
|
98
|
-
ensureDir(root);
|
|
99
|
-
withConfigPathLock(configPathFor(root), (cfgPath) => {
|
|
100
|
-
if (readJsonIfExists(cfgPath))
|
|
101
|
-
return;
|
|
102
|
-
// Persist no materialized model ladder or keybinding defaults. `modelLadders` holds concrete model ids (e.g.
|
|
103
|
-
// "openai-codex/gpt-5.6-sol:high") that are only valid as long as they match whatever
|
|
104
|
-
// pi model registry this crouter build ships. Baking a materialized snapshot here would
|
|
105
|
-
// freeze this scope's ladder to whatever ids happened to be current the moment it was
|
|
106
|
-
// first initialized -- a later crouter/pi upgrade (or a rollback) that changes the
|
|
107
|
-
// registry can then never reach it: the stale persisted override still wins over the
|
|
108
|
-
// new build's compiled defaults in `mergeConfig`/`mergeModelLadders`, producing a
|
|
109
|
-
// fallback rung `resolveViableFallback` rejects as unregistered while the terminal
|
|
110
|
-
// error misleadingly claims the fallback provider needs authenticating (see live
|
|
111
|
-
// incident: a home's config.json was seeded with `gpt-5.6-*` ladder entries from a
|
|
112
|
-
// build newer than the one actually installed, so its own model registry never had
|
|
113
|
-
// them). Omitting the key here is safe: `readConfig`/`mergeConfig` already default-fill
|
|
114
|
-
// a missing `modelLadders` from whatever build is CURRENTLY running, and an explicit
|
|
115
|
-
// user override (`sys config set modelLadders...`) still persists and still wins.
|
|
116
|
-
// Keybindings are sparse user overrides, so an untouched scope omits that member too.
|
|
117
|
-
const { modelLadders: _omittedModelLadders, keybindings: _omittedKeybindings, tmux_passthrough: _omittedTmuxPassthrough, brokerThresholds, lifecycle, ...rest } = defaultScopeConfig();
|
|
118
|
-
// Broker thresholds and lifecycle timing are host-wide daemon policy, so
|
|
119
|
-
// only user scope persists them. Project config must never acquire a
|
|
120
|
-
// machine setting.
|
|
121
|
-
atomicWriteJson(cfgPath, scope === 'user' ? { ...rest, brokerThresholds, lifecycle } : rest);
|
|
96
|
+
export function replaceRawSettingsForTarget(target, next, ifRevision) {
|
|
97
|
+
return replaceRawSettings(rawSettingsOwnerForTarget(target), next, ifRevision, (settings) => {
|
|
98
|
+
try {
|
|
99
|
+
mergeConfig(settings);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
throw new InvalidRawSettingsError(error);
|
|
103
|
+
}
|
|
122
104
|
});
|
|
123
105
|
}
|
|
106
|
+
export function updateRawConfigAtomically(scope, mutate) { const root = requireScopeRoot(scope); return mutateRawAtRoot(root, (latest) => { const next = mutate(latest); return { value: next, next }; }); }
|
|
107
|
+
export function writeConfig(scope, config) { writeRawAtRoot(requireScopeRoot(scope), config); }
|
|
108
|
+
export function writeRawConfig(scope, config) { writeRawAtRoot(requireScopeRoot(scope), config); }
|
|
109
|
+
export function writeState(scope, state) { const root = requireScopeRoot(scope); writeBookkeeping(settingsOwnerForRoot(root), state); }
|
|
110
|
+
export function ensureScopeInitialized(scope, root) { ensureDir(root); const owner = scope === 'user' ? settingsOwnerForRoot(userScopeRoot()) : settingsOwnerForRoot(root); initializeFreshScopedStore({ kind: owner.kind === 'user' ? 'user' : 'project', root: owner.store.root }); if (owner.kind === 'project' && readJsonIfExists(configPathFor(owner.root)) === null) {
|
|
111
|
+
const { modelLadders: _a, keybindings: _b, tmux_passthrough: _c, brokerThresholds, lifecycle, ...rest } = defaultScopeConfig();
|
|
112
|
+
writeRawAtRoot(owner.root, rest);
|
|
113
|
+
} }
|
|
124
114
|
function normalizeKeybindings(raw) {
|
|
125
115
|
if (raw === null || typeof raw !== 'object' || Array.isArray(raw))
|
|
126
116
|
return {};
|
|
@@ -798,7 +788,7 @@ function mergeConfig(partial) {
|
|
|
798
788
|
* for why `readConfig(scope)`, which already defaults-fills, is unusable
|
|
799
789
|
* for cross-scope layering). */
|
|
800
790
|
export function readRawConfigAtRoot(root) {
|
|
801
|
-
return
|
|
791
|
+
return readRawAtRoot(root);
|
|
802
792
|
}
|
|
803
793
|
export function readRawScopeConfig(scope) {
|
|
804
794
|
const root = scopeRoot(scope);
|
|
@@ -814,7 +804,7 @@ function readRawProfileConfig(profileId) {
|
|
|
814
804
|
if (profileId === null || profileId === '')
|
|
815
805
|
return null;
|
|
816
806
|
try {
|
|
817
|
-
return
|
|
807
|
+
return readRawAtRoot(profileRoot(profileId));
|
|
818
808
|
}
|
|
819
809
|
catch {
|
|
820
810
|
return null;
|
|
@@ -832,7 +822,7 @@ function readRawProjectScopeConfigs(targetCwd, targetProfileId) {
|
|
|
832
822
|
const farthestFirst = [...nearestFirst].reverse();
|
|
833
823
|
// A root with no config.json still contributes: its installed plugins may
|
|
834
824
|
// declare kinds, so the ROOT rides along and `raw` stays null.
|
|
835
|
-
return farthestFirst.map((root) => ({ root, raw:
|
|
825
|
+
return farthestFirst.map((root) => ({ root, raw: readRawAtRoot(root) }));
|
|
836
826
|
}
|
|
837
827
|
/** Merge launch knobs (`kinds`, `modelLadders`) across scopes in
|
|
838
828
|
* project stack > profile > user > builtin precedence — the same precedence
|
|
@@ -994,27 +984,21 @@ function sparseModelLaddersToPersist(rawLadders, baseline, mutated) {
|
|
|
994
984
|
* `sys config set` never freezes a full concrete `modelLadders` to disk,
|
|
995
985
|
* and a direct ladder-rung set still persists only the touched rung -- all
|
|
996
986
|
* through one lock-protected atomic update path. */
|
|
997
|
-
function
|
|
998
|
-
return
|
|
999
|
-
const raw = readJsonIfExists(lockedPath) ?? {};
|
|
987
|
+
function mutateConfigAtRoot(root, mutate) {
|
|
988
|
+
return mutateRawAtRoot(root, (raw) => {
|
|
1000
989
|
const baseline = mergeConfig(raw);
|
|
1001
990
|
const cfg = mergeConfig(raw);
|
|
1002
991
|
mutate(cfg);
|
|
1003
992
|
const partial = { ...raw };
|
|
1004
|
-
for (const key of SCOPE_CONFIG_KEYS)
|
|
1005
|
-
if (key
|
|
1006
|
-
continue;
|
|
1007
|
-
if (!isDeepStrictEqual(cfg[key], baseline[key])) {
|
|
993
|
+
for (const key of SCOPE_CONFIG_KEYS)
|
|
994
|
+
if (key !== 'modelLadders' && !isDeepStrictEqual(cfg[key], baseline[key]))
|
|
1008
995
|
partial[key] = cfg[key];
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
996
|
const ladders = sparseModelLaddersToPersist(raw.modelLadders, baseline.modelLadders, cfg.modelLadders);
|
|
1012
997
|
if (ladders !== undefined)
|
|
1013
998
|
partial.modelLadders = ladders;
|
|
1014
999
|
else
|
|
1015
1000
|
delete partial.modelLadders;
|
|
1016
|
-
|
|
1017
|
-
return cfg;
|
|
1001
|
+
return { value: cfg, next: partial };
|
|
1018
1002
|
});
|
|
1019
1003
|
}
|
|
1020
1004
|
/** Persist an EXPLICIT sparse `modelLadders` intent at one config root: the
|
|
@@ -1029,21 +1013,10 @@ function mutateConfigAtPath(path, mutate) {
|
|
|
1029
1013
|
* evaporates as soon as the default moves. Explicit intent is recorded as
|
|
1030
1014
|
* written. Mirrors `persistDefaultKindModel`. */
|
|
1031
1015
|
export function persistModelLadders(root, patch) {
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
const current = (raw.modelLadders ?? {});
|
|
1037
|
-
const next = { ...current };
|
|
1038
|
-
if (patch.defaultProvider !== undefined)
|
|
1039
|
-
next.defaultProvider = patch.defaultProvider;
|
|
1040
|
-
for (const provider of ['anthropic', 'openai']) {
|
|
1041
|
-
const rungs = patch[provider];
|
|
1042
|
-
if (rungs !== undefined)
|
|
1043
|
-
next[provider] = { ...(current[provider] ?? {}), ...rungs };
|
|
1044
|
-
}
|
|
1045
|
-
atomicWriteJson(lockedPath, { ...raw, modelLadders: next });
|
|
1046
|
-
});
|
|
1016
|
+
mutateRawAtRoot(root, (raw) => { const current = (raw.modelLadders ?? {}); const next = { ...current }; if (patch.defaultProvider !== undefined)
|
|
1017
|
+
next.defaultProvider = patch.defaultProvider; for (const provider of ['anthropic', 'openai'])
|
|
1018
|
+
if (patch[provider] !== undefined)
|
|
1019
|
+
next[provider] = { ...(current[provider] ?? {}), ...patch[provider] }; return { value: undefined, next: { ...raw, modelLadders: next } }; });
|
|
1047
1020
|
}
|
|
1048
1021
|
/** Persist one complete route at one exact config root. The route is the only
|
|
1049
1022
|
* raw route entry touched; unrelated routes and local ladder defaults remain
|
|
@@ -1054,33 +1027,18 @@ export function persistModelRoute(root, routeId, route) {
|
|
|
1054
1027
|
assertCompleteModelRoute(routeId, route);
|
|
1055
1028
|
else if (routeId.trim() === '')
|
|
1056
1029
|
throw new Error('model route id must be a non-empty string');
|
|
1057
|
-
const
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
const
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
else {
|
|
1065
|
-
const current = routes[routeId];
|
|
1066
|
-
setRouteRecord(routes, routeId, {
|
|
1067
|
-
...(current !== null && typeof current === 'object' && !Array.isArray(current) ? current : {}),
|
|
1068
|
-
...route,
|
|
1069
|
-
models: { ...route.models },
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
const next = { ...raw };
|
|
1073
|
-
if (Object.keys(routes).length === 0)
|
|
1074
|
-
delete next.modelRoutes;
|
|
1075
|
-
else
|
|
1076
|
-
next.modelRoutes = routes;
|
|
1077
|
-
atomicWriteJson(lockedPath, next);
|
|
1078
|
-
});
|
|
1030
|
+
mutateRawAtRoot(root, (raw) => { const routes = copyRouteRecord((raw.modelRoutes ?? {})); if (route === undefined)
|
|
1031
|
+
delete routes[routeId];
|
|
1032
|
+
else
|
|
1033
|
+
setRouteRecord(routes, routeId, { ...route, models: { ...route.models } }); const next = { ...raw }; if (Object.keys(routes).length === 0)
|
|
1034
|
+
delete next.modelRoutes;
|
|
1035
|
+
else
|
|
1036
|
+
next.modelRoutes = routes; return { value: undefined, next }; });
|
|
1079
1037
|
}
|
|
1080
1038
|
/** Mutate one exact config root. Setup uses this for profile and explicit
|
|
1081
1039
|
* project targets that cannot be represented by the user/project Scope enum. */
|
|
1082
1040
|
export function updateConfigAtRoot(root, mutate) {
|
|
1083
|
-
return
|
|
1041
|
+
return mutateConfigAtRoot(root, mutate);
|
|
1084
1042
|
}
|
|
1085
1043
|
export function updateConfig(scope, mutate) {
|
|
1086
1044
|
return updateConfigAtRoot(requireScopeRoot(scope), mutate);
|
|
@@ -1090,15 +1048,8 @@ export function updateConfig(scope, mutate) {
|
|
|
1090
1048
|
* scopes instead of freezing a materialized registry snapshot. */
|
|
1091
1049
|
export function persistDefaultKindModel(opts) {
|
|
1092
1050
|
const field = opts.orchestrator === true ? 'orchestratorModel' : 'model';
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
withConfigPathLock(path, (lockedPath) => {
|
|
1096
|
-
const raw = readJsonIfExists(lockedPath) ?? {};
|
|
1097
|
-
const kinds = { ...(raw.kinds ?? {}) };
|
|
1098
|
-
kinds[opts.kind] = { ...(kinds[opts.kind] ?? {}), [field]: opts.model };
|
|
1099
|
-
atomicWriteJson(lockedPath, { ...raw, kinds });
|
|
1100
|
-
});
|
|
1101
|
-
return { path, field };
|
|
1051
|
+
mutateRawAtRoot(opts.root, (raw) => { const kinds = { ...(raw.kinds ?? {}), [opts.kind]: { ...(raw.kinds ?? {})[opts.kind], [field]: opts.model } }; return { value: undefined, next: { ...raw, kinds: kinds } }; });
|
|
1052
|
+
return { path: configPathFor(opts.root), field };
|
|
1102
1053
|
}
|
|
1103
1054
|
export function updateState(scope, mutate) {
|
|
1104
1055
|
const s = readState(scope);
|
|
@@ -1,49 +1,45 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// forever, flashing "node refreshing — reconnecting…" in every attached
|
|
5
|
-
// viewer. The boot migration must rewrite identity-less rows and map the
|
|
6
|
-
// ISO-timestamp cursor boundary onto an exact entry identity the strict
|
|
7
|
-
// reader accepts.
|
|
1
|
+
// Retained legacy mailbox import: the SQL cutover preserves historical inbox
|
|
2
|
+
// bytes by importing them into messages and deliveries without leaving a
|
|
3
|
+
// runtime filesystem reader.
|
|
8
4
|
import { test } from 'node:test';
|
|
9
5
|
import assert from 'node:assert/strict';
|
|
10
|
-
import {
|
|
6
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
7
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
8
|
import { join } from 'node:path';
|
|
12
9
|
import { tmpdir } from 'node:os';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
16
|
-
test('legacy inbox rows and ISO cursor migrate to strict identities once', () => {
|
|
10
|
+
import { migrateMessagingState } from '../../canvas/deliveries.js';
|
|
11
|
+
import { inboxPath, nodeDir } from '../../canvas/paths.js';
|
|
12
|
+
test('legacy inbox rows and cursor import once into acknowledged and pending SQL deliveries', () => {
|
|
17
13
|
const home = mkdtempSync(join(tmpdir(), 'crtr-inboxmigrate-'));
|
|
18
14
|
const orig = process.env['CRTR_HOME'];
|
|
19
15
|
process.env['CRTR_HOME'] = home;
|
|
20
16
|
const nodeId = 'mrtest01-aabbccdd';
|
|
17
|
+
const db = new DatabaseSync(join(home, 'canvas.db'));
|
|
21
18
|
try {
|
|
19
|
+
db.exec('CREATE TABLE nodes (node_id TEXT PRIMARY KEY, waiting_for TEXT, waiting_for_generation INTEGER)');
|
|
20
|
+
db.prepare('INSERT INTO nodes (node_id, waiting_for, waiting_for_generation) VALUES (?, NULL, 0)').run(nodeId);
|
|
21
|
+
db.exec('CREATE TABLE human_action_deliveries (request_id TEXT, state TEXT, attempt INTEGER, argv_json TEXT, cwd TEXT, completion_json TEXT, next_attempt_at INTEGER, claimed_at INTEGER, claim_owner TEXT, accepted_at INTEGER, permanent_failed_at INTEGER, last_failure_json TEXT, created_at INTEGER, updated_at INTEGER)');
|
|
22
|
+
db.exec('CREATE TABLE node_outcome_deliveries (node_id TEXT, state TEXT, attempt INTEGER, action_name TEXT, argv_json TEXT, cwd TEXT, config_origin TEXT, payload_json TEXT, document_json TEXT, next_attempt_at INTEGER, claimed_at INTEGER, claim_owner TEXT, accepted_at INTEGER, permanent_failed_at INTEGER, last_failure_json TEXT, created_at INTEGER, updated_at INTEGER)');
|
|
22
23
|
mkdirSync(nodeDir(nodeId), { recursive: true });
|
|
23
24
|
const lines = [
|
|
24
|
-
'{"ts":"2026-07-09T22:54:06.294Z","from":"mrpeer1-00000001","tier":"normal","kind":"final","label":"one"}',
|
|
25
|
-
'{"ts":"2026-07-10T01:00:00.000Z","from":"mrpeer1-00000001","tier":"normal","kind":"update","label":"two"}',
|
|
25
|
+
'{"entry_id":"11111111111111111111111111111111","operation_id":"22222222222222222222222222222222","ts":"2026-07-09T22:54:06.294Z","from":"mrpeer1-00000001","tier":"normal","kind":"final","label":"one"}',
|
|
26
|
+
'{"entry_id":"33333333333333333333333333333333","operation_id":"44444444444444444444444444444444","ts":"2026-07-10T01:00:00.000Z","from":"mrpeer1-00000001","tier":"normal","kind":"update","label":"two"}',
|
|
26
27
|
];
|
|
27
28
|
writeFileSync(inboxPath(nodeId), `${lines.join('\n')}\n`, 'utf8');
|
|
28
|
-
writeFileSync(`${inboxPath(nodeId)}.cursor`, '
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
assert.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
assert.
|
|
38
|
-
|
|
39
|
-
assert.equal(unread.length, 1);
|
|
40
|
-
assert.equal(unread[0].ts, '2026-07-10T01:00:00.000Z');
|
|
41
|
-
const migratedRaw = readFileSync(inboxPath(nodeId), 'utf8');
|
|
42
|
-
const second = migrateLegacyInboxIdentities();
|
|
43
|
-
assert.deepEqual(second, { nodes: 0, entries: 0, cursors: 0, failures: 0 });
|
|
44
|
-
assert.equal(readFileSync(inboxPath(nodeId), 'utf8'), migratedRaw, 'second run is a no-op');
|
|
29
|
+
writeFileSync(`${inboxPath(nodeId)}.cursor`, '11111111111111111111111111111111', 'utf8');
|
|
30
|
+
migrateMessagingState(db);
|
|
31
|
+
const deliveries = db.prepare(`SELECT m.message_id, m.created_at, d.state, d.acknowledged_at
|
|
32
|
+
FROM deliveries d JOIN messages m ON m.message_id = d.message_id
|
|
33
|
+
WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? ORDER BY m.seq`).all(nodeId);
|
|
34
|
+
assert.deepEqual(deliveries.map(({ message_id, state }) => ({ message_id, state })), [
|
|
35
|
+
{ message_id: '11111111111111111111111111111111', state: 'acknowledged' },
|
|
36
|
+
{ message_id: '33333333333333333333333333333333', state: 'pending' },
|
|
37
|
+
], 'the retained file rows become ordered SQL message/delivery facts and the legacy cursor becomes acknowledgement');
|
|
38
|
+
assert.equal(deliveries[0].acknowledged_at, Date.parse('2026-07-09T22:54:06.294Z'));
|
|
39
|
+
assert.equal(deliveries[1].created_at, '2026-07-10T01:00:00.000Z');
|
|
45
40
|
}
|
|
46
41
|
finally {
|
|
42
|
+
db.close();
|
|
47
43
|
if (orig === undefined)
|
|
48
44
|
delete process.env['CRTR_HOME'];
|
|
49
45
|
else
|
package/dist/core/feed/feed.d.ts
CHANGED
|
@@ -27,12 +27,6 @@ export declare class FinalizationError extends Error {
|
|
|
27
27
|
readonly reportPath?: string | undefined;
|
|
28
28
|
constructor(code: 'already_finalized' | 'finalization_forbidden' | 'unknown_node', reportPath?: string | undefined);
|
|
29
29
|
}
|
|
30
|
-
export declare class FinalDeliveryError extends Error {
|
|
31
|
-
readonly reportPath: string;
|
|
32
|
-
readonly deliveredTo: string[];
|
|
33
|
-
readonly failedTo: string[];
|
|
34
|
-
constructor(reportPath: string, deliveredTo: string[], failedTo: string[]);
|
|
35
|
-
}
|
|
36
30
|
export declare function push(nodeId: string, opts: PushOpts): Promise<PushResult>;
|
|
37
31
|
export declare function pushUpdate(nodeId: string, body: string, opts?: {
|
|
38
32
|
from?: string;
|