@north-light/crouter 0.3.311 → 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 +17 -5
- 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 +312 -1
- 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 +62 -19
- 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 +4 -3
- package/dist/core/__tests__/on-read-dedup-resume.test.js +8 -7
- package/dist/core/__tests__/on-read-nested-store.test.js +8 -7
- 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 +35 -32
- 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 +5 -4
- 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 +19 -5
- 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/command-hooks/artifact.d.ts +6 -0
- package/dist/core/command-hooks/artifact.js +22 -6
- package/dist/core/command-hooks/discovery.d.ts +2 -0
- package/dist/core/command-hooks/discovery.js +2 -0
- package/dist/core/command-hooks/lifecycle-catalog.d.ts +20 -2
- package/dist/core/command-hooks/lifecycle-catalog.js +7 -2
- package/dist/core/command-hooks/lifecycle-create.d.ts +4 -0
- package/dist/core/command-hooks/lifecycle-create.js +51 -0
- package/dist/core/command-hooks/schema.d.ts +2 -2
- package/dist/core/command-hooks/schema.js +5 -4
- package/dist/core/command-hooks/transport/exec-invoke.js +3 -1
- package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +18 -8
- package/dist/core/command-hooks/transport/exec-lifecycle.js +89 -30
- package/dist/core/config.d.ts +17 -12
- package/dist/core/config.js +106 -155
- package/dist/core/errors.d.ts +1 -0
- package/dist/core/errors.js +4 -0
- 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 +4 -3
- 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/companion.d.ts +1 -1
- package/dist/core/review/companion.js +2 -2
- 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/nodes.d.ts +5 -1
- package/dist/core/runtime/nodes.js +26 -11
- package/dist/core/runtime/placement.js +2 -2
- package/dist/core/runtime/recycle.js +24 -14
- package/dist/core/runtime/reset.js +26 -7
- package/dist/core/runtime/revive.js +226 -217
- package/dist/core/runtime/spawn.d.ts +4 -1
- package/dist/core/runtime/spawn.js +43 -5
- package/dist/core/runtime/warm-pool.d.ts +2 -2
- package/dist/core/runtime/warm-pool.js +23 -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 +41 -17
- 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 +6 -3
- 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 +29 -43
- 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 -148
- 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.d.ts +1 -0
- package/dist/daemon/api/handlers/nodes.js +41 -42
- 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/hook-authoring.d.ts +28 -2
- package/dist/hook-authoring.js +53 -1
- package/dist/index.d.ts +3 -2
- 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,217 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { randomBytes, randomUUID } from 'node:crypto';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { cancelCronsOnWake } from '../canvas/crons.js';
|
|
5
|
+
import { withCanvasWrite } from '../canvas/db.js';
|
|
6
|
+
import { getRow } from '../canvas/canvas.js';
|
|
7
|
+
import { messagesDir } from '../canvas/paths.js';
|
|
8
|
+
import { syncDirectory, syncFile } from '../fs-utils.js';
|
|
9
|
+
import { settleDeadControllerWait, settleMatchingControllerWait } from '../canvas/waits.js';
|
|
10
|
+
import { generateOperationId, operationIdContext, validateOperationId } from '../events/operation-id.js';
|
|
11
|
+
import { clipBody } from './inbox.js';
|
|
12
|
+
function newId() {
|
|
13
|
+
let id;
|
|
14
|
+
do
|
|
15
|
+
id = randomBytes(16).toString('hex');
|
|
16
|
+
while (id === '00000000000000000000000000000000');
|
|
17
|
+
return id;
|
|
18
|
+
}
|
|
19
|
+
function stringify(value, name) {
|
|
20
|
+
const json = JSON.stringify(value);
|
|
21
|
+
if (json === undefined)
|
|
22
|
+
throw new TypeError(`${name} must be JSON-serializable`);
|
|
23
|
+
return json;
|
|
24
|
+
}
|
|
25
|
+
function effectiveTier(nodeId, urgency) {
|
|
26
|
+
if (urgency !== 'normal' && urgency !== 'deferred')
|
|
27
|
+
return urgency;
|
|
28
|
+
return getRow(nodeId)?.lifecycle === 'terminal' ? 'urgent' : urgency;
|
|
29
|
+
}
|
|
30
|
+
function writeMessageBody(nodeId, createdAt, body) {
|
|
31
|
+
const dir = messagesDir(nodeId);
|
|
32
|
+
if (!existsSync(dir))
|
|
33
|
+
mkdirSync(dir, { recursive: true });
|
|
34
|
+
const stamp = createdAt.replace(/[^0-9]/g, '');
|
|
35
|
+
const finalPath = join(dir, `${stamp}-${randomUUID().slice(0, 6)}.md`);
|
|
36
|
+
const temporaryPath = `${finalPath}.tmp`;
|
|
37
|
+
writeFileSync(temporaryPath, body, 'utf8');
|
|
38
|
+
syncFile(temporaryPath);
|
|
39
|
+
renameSync(temporaryPath, finalPath);
|
|
40
|
+
syncDirectory(dir);
|
|
41
|
+
return finalPath;
|
|
42
|
+
}
|
|
43
|
+
function inlineBody(bodyJson) {
|
|
44
|
+
if (typeof bodyJson !== 'object' || bodyJson === null)
|
|
45
|
+
return '';
|
|
46
|
+
const body = bodyJson['body'];
|
|
47
|
+
return typeof body === 'string' ? body.trim() : '';
|
|
48
|
+
}
|
|
49
|
+
function insertMessage(db, input) {
|
|
50
|
+
const messageId = input.messageId ?? newId();
|
|
51
|
+
const seq = Number(db.prepare('SELECT COALESCE(MAX(seq), 0) AS seq FROM messages').get().seq) + 1;
|
|
52
|
+
db.prepare(`INSERT INTO messages
|
|
53
|
+
(message_id, seq, operation_id, created_at, sender_node_id, sender_name, kind, label, body_ref, body_json, disposition)
|
|
54
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(messageId, seq, input.operationId, input.createdAt, input.senderNodeId, input.senderName, input.kind, input.label, input.bodyRef, input.bodyJson === undefined ? null : stringify(input.bodyJson, 'message body'), input.disposition);
|
|
55
|
+
return messageId;
|
|
56
|
+
}
|
|
57
|
+
function insertNodeDelivery(db, messageId, destinationId, urgency, wake, now, idempotencyKey) {
|
|
58
|
+
const deliveryId = newId();
|
|
59
|
+
db.prepare(`INSERT INTO deliveries
|
|
60
|
+
(delivery_id, message_id, destination_kind, destination_id, urgency, wake, state, idempotency_key, created_at, updated_at)
|
|
61
|
+
VALUES (?, ?, 'node_inbox', ?, ?, ?, 'pending', ?, ?, ?)`).run(deliveryId, messageId, destinationId, urgency, wake, idempotencyKey ?? null, now, now);
|
|
62
|
+
return deliveryId;
|
|
63
|
+
}
|
|
64
|
+
function finalizeNodeMail(input, destinationNodeId) {
|
|
65
|
+
const createdAt = new Date().toISOString();
|
|
66
|
+
const operationId = input.operationId === undefined
|
|
67
|
+
? operationIdContext.current() ?? generateOperationId()
|
|
68
|
+
: validateOperationId(input.operationId);
|
|
69
|
+
let bodyRef = input.bodyRef ?? null;
|
|
70
|
+
let bodyJson = input.bodyJson;
|
|
71
|
+
if (bodyRef === null) {
|
|
72
|
+
const body = inlineBody(bodyJson);
|
|
73
|
+
const clipped = clipBody(body);
|
|
74
|
+
if (clipped.clipped) {
|
|
75
|
+
// The destination owns the referenced body. Publish the file before its reference enters the transaction.
|
|
76
|
+
bodyRef = writeMessageBody(destinationNodeId, createdAt, body);
|
|
77
|
+
bodyJson = { ...bodyJson, body: clipped.text };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { createdAt, operationId, bodyRef, bodyJson };
|
|
81
|
+
}
|
|
82
|
+
/** Insert one immutable message and one node-inbox obligation into the caller's write transaction. */
|
|
83
|
+
export function enqueueNodeMail(destinationNodeId, input) {
|
|
84
|
+
const prepared = finalizeNodeMail(input, destinationNodeId);
|
|
85
|
+
return withCanvasWrite((db) => {
|
|
86
|
+
if (input.idempotencyKey !== undefined) {
|
|
87
|
+
const existing = db.prepare(`SELECT d.delivery_id, d.message_id, d.urgency, m.created_at
|
|
88
|
+
FROM deliveries d JOIN messages m ON m.message_id = d.message_id WHERE d.idempotency_key = ?`).get(input.idempotencyKey);
|
|
89
|
+
if (existing !== undefined)
|
|
90
|
+
return { messageId: existing.message_id, deliveryId: existing.delivery_id, urgency: existing.urgency, createdAt: existing.created_at };
|
|
91
|
+
}
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
const urgency = effectiveTier(destinationNodeId, input.urgency);
|
|
94
|
+
const messageId = insertMessage(db, {
|
|
95
|
+
operationId: prepared.operationId,
|
|
96
|
+
createdAt: prepared.createdAt,
|
|
97
|
+
senderNodeId: input.senderNodeId ?? null,
|
|
98
|
+
senderName: input.senderName ?? null,
|
|
99
|
+
kind: input.kind,
|
|
100
|
+
label: input.label,
|
|
101
|
+
bodyRef: prepared.bodyRef,
|
|
102
|
+
bodyJson: prepared.bodyJson,
|
|
103
|
+
disposition: input.disposition ?? null,
|
|
104
|
+
});
|
|
105
|
+
const deliveryId = insertNodeDelivery(db, messageId, destinationNodeId, urgency, input.wake, now, input.idempotencyKey);
|
|
106
|
+
settleMatchingControllerWait(destinationNodeId, input.senderNodeId ?? null, now, db);
|
|
107
|
+
if (input.wake === 1 && urgency !== 'deferred')
|
|
108
|
+
cancelCronsOnWake(destinationNodeId);
|
|
109
|
+
return { messageId, deliveryId, urgency, createdAt: prepared.createdAt };
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/** Fan out one immutable report message to the supplied subscriber snapshot. */
|
|
113
|
+
export function enqueueSubscriberMail(subscribers, input) {
|
|
114
|
+
if (subscribers.length === 0)
|
|
115
|
+
return [];
|
|
116
|
+
const prepared = finalizeNodeMail(input, subscribers[0].node_id);
|
|
117
|
+
return withCanvasWrite((db) => {
|
|
118
|
+
const now = Date.now();
|
|
119
|
+
const messageId = insertMessage(db, {
|
|
120
|
+
operationId: prepared.operationId,
|
|
121
|
+
createdAt: prepared.createdAt,
|
|
122
|
+
senderNodeId: input.senderNodeId ?? null,
|
|
123
|
+
senderName: input.senderName ?? null,
|
|
124
|
+
kind: input.kind,
|
|
125
|
+
label: input.label,
|
|
126
|
+
bodyRef: prepared.bodyRef,
|
|
127
|
+
bodyJson: prepared.bodyJson,
|
|
128
|
+
disposition: input.disposition ?? null,
|
|
129
|
+
});
|
|
130
|
+
return subscribers.map((subscriber) => {
|
|
131
|
+
const urgency = effectiveTier(subscriber.node_id, input.urgency);
|
|
132
|
+
const wake = subscriber.active ? 1 : 0;
|
|
133
|
+
const deliveryId = insertNodeDelivery(db, messageId, subscriber.node_id, urgency, wake, now);
|
|
134
|
+
settleMatchingControllerWait(subscriber.node_id, input.senderNodeId ?? null, now, db);
|
|
135
|
+
if (wake === 1 && urgency !== 'deferred')
|
|
136
|
+
cancelCronsOnWake(subscriber.node_id);
|
|
137
|
+
return { destinationNodeId: subscriber.node_id, messageId, deliveryId, urgency, createdAt: prepared.createdAt };
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/** Insert one frozen external-action obligation. A duplicate idempotency key returns its original identity. */
|
|
142
|
+
export function enqueueExternalAction(input) {
|
|
143
|
+
return withCanvasWrite((db) => {
|
|
144
|
+
const existing = db.prepare('SELECT delivery_id, message_id FROM deliveries WHERE idempotency_key = ?').get(input.idempotencyKey);
|
|
145
|
+
if (existing !== undefined)
|
|
146
|
+
return { messageId: existing.message_id, deliveryId: existing.delivery_id };
|
|
147
|
+
const now = Date.now();
|
|
148
|
+
const messageId = insertMessage(db, {
|
|
149
|
+
operationId: operationIdContext.current() ?? generateOperationId(),
|
|
150
|
+
createdAt: new Date(now).toISOString(),
|
|
151
|
+
senderNodeId: null,
|
|
152
|
+
senderName: null,
|
|
153
|
+
kind: input.kind ?? 'completed',
|
|
154
|
+
label: input.label ?? input.destinationId,
|
|
155
|
+
bodyRef: null,
|
|
156
|
+
bodyJson: input.bodyJson,
|
|
157
|
+
disposition: null,
|
|
158
|
+
});
|
|
159
|
+
const deliveryId = newId();
|
|
160
|
+
const inserted = db.prepare(`INSERT INTO deliveries
|
|
161
|
+
(delivery_id, message_id, destination_kind, destination_id, urgency, wake, state, next_attempt_at,
|
|
162
|
+
adapter_json, idempotency_key, created_at, updated_at)
|
|
163
|
+
VALUES (?, ?, 'external_action', ?, 'normal', 0, 'pending', ?, ?, ?, ?, ?)
|
|
164
|
+
ON CONFLICT(idempotency_key) WHERE idempotency_key IS NOT NULL DO NOTHING`).run(deliveryId, messageId, input.destinationId, now, stringify(input.adapter, 'external action adapter'), input.idempotencyKey, now, now);
|
|
165
|
+
if (Number(inserted.changes) === 1)
|
|
166
|
+
return { messageId, deliveryId };
|
|
167
|
+
const winner = db.prepare('SELECT delivery_id, message_id FROM deliveries WHERE idempotency_key = ?').get(input.idempotencyKey);
|
|
168
|
+
if (winner === undefined)
|
|
169
|
+
throw new Error(`external action idempotency winner disappeared: ${input.idempotencyKey}`);
|
|
170
|
+
return { messageId: winner.message_id, deliveryId: winner.delivery_id };
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/** Retract an obligation without fabricating a terminal receipt. */
|
|
174
|
+
export function cancelDelivery(deliveryId, now = Date.now()) {
|
|
175
|
+
return withCanvasWrite((db) => Number(db.prepare('UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE delivery_id = ? AND canceled_at IS NULL').run(now, now, deliveryId).changes) === 1);
|
|
176
|
+
}
|
|
177
|
+
export function cancelPendingNodeMail(nodeId, select = {}) {
|
|
178
|
+
return withCanvasWrite((db) => {
|
|
179
|
+
const clauses = ["d.destination_kind = 'node_inbox'", 'd.destination_id = ?', 'd.canceled_at IS NULL', "d.state IN ('pending', 'handed_off')"];
|
|
180
|
+
const params = [nodeId];
|
|
181
|
+
if (select.senderNodeId !== undefined) {
|
|
182
|
+
clauses.push('m.sender_node_id IS ?');
|
|
183
|
+
params.push(select.senderNodeId);
|
|
184
|
+
}
|
|
185
|
+
if (select.kind !== undefined) {
|
|
186
|
+
clauses.push('m.kind = ?');
|
|
187
|
+
params.push(select.kind);
|
|
188
|
+
}
|
|
189
|
+
const rows = db.prepare(`SELECT d.delivery_id FROM deliveries d JOIN messages m ON m.message_id = d.message_id WHERE ${clauses.join(' AND ')}`).all(...params);
|
|
190
|
+
if (rows.length === 0)
|
|
191
|
+
return [];
|
|
192
|
+
const now = Date.now();
|
|
193
|
+
db.prepare(`UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE delivery_id IN (${rows.map(() => '?').join(',')})`)
|
|
194
|
+
.run(now, now, ...rows.map((row) => row.delivery_id));
|
|
195
|
+
return rows.map((row) => row.delivery_id);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/** Enqueue the one notice only if the same live controller wait is still dead. */
|
|
199
|
+
export function enqueueControllerDeathNotice(nodeId, controllerId) {
|
|
200
|
+
return withCanvasWrite((db) => {
|
|
201
|
+
const now = Date.now();
|
|
202
|
+
const row = db.prepare(`SELECT target.status AS controller_status FROM waits w LEFT JOIN nodes target ON target.node_id = w.target_id
|
|
203
|
+
WHERE w.node_id = ? AND w.kind = 'controller' AND w.target_id = ? AND w.satisfied_at IS NULL`).get(nodeId, controllerId);
|
|
204
|
+
if (row === undefined || row.controller_status === 'active' || row.controller_status === 'idle')
|
|
205
|
+
return null;
|
|
206
|
+
const result = enqueueNodeMail(nodeId, {
|
|
207
|
+
kind: 'urgent',
|
|
208
|
+
label: 'declared controller is no longer live',
|
|
209
|
+
bodyJson: { body: `Controller ${controllerId} is no longer live; the declared message wait was cleared.` },
|
|
210
|
+
urgency: 'urgent',
|
|
211
|
+
wake: 1,
|
|
212
|
+
});
|
|
213
|
+
if (!settleDeadControllerWait(nodeId, controllerId, now, db))
|
|
214
|
+
throw new Error(`controller wait changed while notifying ${nodeId}`);
|
|
215
|
+
return result;
|
|
216
|
+
});
|
|
217
|
+
}
|
package/dist/core/fs-utils.d.ts
CHANGED
|
@@ -13,6 +13,10 @@ export interface AtomicWriteOptions {
|
|
|
13
13
|
mode?: number;
|
|
14
14
|
}
|
|
15
15
|
export declare function atomicWriteJson(path: string, data: unknown, opts?: AtomicWriteOptions): void;
|
|
16
|
+
/** Force a file's written bytes to stable storage. */
|
|
17
|
+
export declare function syncFile(path: string): void;
|
|
18
|
+
/** Force a directory's entry changes to stable storage. */
|
|
19
|
+
export declare function syncDirectory(path: string): void;
|
|
16
20
|
/** Replace one file's contents in a single rename, so a concurrent reader — the
|
|
17
21
|
* tmux inbox watcher, or crtrd serving a ticket read — sees either the old
|
|
18
22
|
* bytes or the new ones and never a truncated file mid-write. */
|
package/dist/core/fs-utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync, cpSync, readlinkSync, renameSync, chmodSync, realpathSync, } from 'node:fs';
|
|
1
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync, cpSync, readlinkSync, renameSync, chmodSync, realpathSync, closeSync, fsyncSync, openSync, } from 'node:fs';
|
|
2
2
|
import { dirname, join, relative, sep } from 'node:path';
|
|
3
3
|
import { homedir, platform } from 'node:os';
|
|
4
4
|
/** `realpathSync`, tolerant of a path that doesn't exist or can't be resolved. */
|
|
@@ -38,6 +38,26 @@ export function writeJson(path, data) {
|
|
|
38
38
|
export function atomicWriteJson(path, data, opts) {
|
|
39
39
|
atomicWriteText(path, JSON.stringify(data, null, 2) + '\n', opts);
|
|
40
40
|
}
|
|
41
|
+
/** Force a file's written bytes to stable storage. */
|
|
42
|
+
export function syncFile(path) {
|
|
43
|
+
const fd = openSync(path, 'r');
|
|
44
|
+
try {
|
|
45
|
+
fsyncSync(fd);
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
closeSync(fd);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Force a directory's entry changes to stable storage. */
|
|
52
|
+
export function syncDirectory(path) {
|
|
53
|
+
const fd = openSync(path, 'r');
|
|
54
|
+
try {
|
|
55
|
+
fsyncSync(fd);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
closeSync(fd);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
41
61
|
/** Replace one file's contents in a single rename, so a concurrent reader — the
|
|
42
62
|
* tmux inbox watcher, or crtrd serving a ticket read — sees either the old
|
|
43
63
|
* bytes or the new ones and never a truncated file mid-write. */
|
|
@@ -49,5 +49,5 @@ export declare function answerCommentAnchorLabel(slot: PageSlot, anchor: LegacyC
|
|
|
49
49
|
/** Derive the one shared answer model from the immutable page manifest and a response map. */
|
|
50
50
|
/** `manifest` is unknown deliberately: this boundary validates persisted JSON before deriving. */
|
|
51
51
|
export declare function describePageAnswer(manifest: unknown, responses: PageResponses, opts: DescribePageAnswerOptions): PageAnswer;
|
|
52
|
-
/** The stable one-line digest stored in
|
|
52
|
+
/** The stable one-line digest stored in the terminal request result and shown in history. */
|
|
53
53
|
export declare function buildAnswerDigest(slots: PageAnswerSlot[]): string;
|
|
@@ -100,7 +100,7 @@ function groupDigest(group) {
|
|
|
100
100
|
return group.picked.map((item) => item.label).join(', ');
|
|
101
101
|
return group.total > 0 ? `${group.picked.length} of ${group.total} ${group.group}s` : `${group.picked.length} ${group.group}${group.picked.length === 1 ? '' : 's'}`;
|
|
102
102
|
}
|
|
103
|
-
/** The stable one-line digest stored in
|
|
103
|
+
/** The stable one-line digest stored in the terminal request result and shown in history. */
|
|
104
104
|
export function buildAnswerDigest(slots) {
|
|
105
105
|
return slots.map((slot) => {
|
|
106
106
|
if (slot.raw !== undefined)
|
|
@@ -14,7 +14,7 @@ export interface ClaimOptions {
|
|
|
14
14
|
}
|
|
15
15
|
export declare function readTicketClaim(dir: string): TicketClaim | null;
|
|
16
16
|
export declare function isStaleClaim(claim: TicketClaim, now?: number, localHost?: string): boolean;
|
|
17
|
-
/**
|
|
17
|
+
/** Exclude another live presenter; the daemon owns whether the request can settle. */
|
|
18
18
|
export declare function claimTicket(dir: string, opts?: ClaimOptions): TicketClaim | null;
|
|
19
19
|
export declare function heartbeatClaim(dir: string, token: string, now?: Date): boolean;
|
|
20
20
|
export declare function releaseClaimLocked(dir: string, token: string): boolean;
|
package/dist/core/human/claim.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { hostname } from 'node:os';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import { claimPath
|
|
4
|
+
import { claimPath } from './convention.js';
|
|
5
5
|
import { readJsonOrNull } from '../fs-utils.js';
|
|
6
6
|
import { withExclusiveDirectoryLock } from '../exclusive-lock.js';
|
|
7
7
|
const REMOTE_STALE_MS = 30_000;
|
|
@@ -27,11 +27,9 @@ export function isStaleClaim(claim, now = Date.now(), localHost = hostname()) {
|
|
|
27
27
|
const heartbeat = Date.parse(claim.heartbeatAt);
|
|
28
28
|
return !Number.isFinite(heartbeat) || now - heartbeat > REMOTE_STALE_MS;
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
30
|
+
/** Exclude another live presenter; the daemon owns whether the request can settle. */
|
|
31
31
|
export function claimTicket(dir, opts = {}) {
|
|
32
32
|
return withExclusiveDirectoryLock(`${dir}/.ticket-lock`, () => {
|
|
33
|
-
if (existsSync(responsePath(dir)) || (!existsSync(pageManifestPath(dir)) && !existsSync(reviewPath(dir))))
|
|
34
|
-
return null;
|
|
35
33
|
const path = claimPath(dir);
|
|
36
34
|
const existing = readTicketClaim(dir);
|
|
37
35
|
if (existing !== null && !isStaleClaim(existing, Date.now(), opts.host ?? hostname()))
|
|
@@ -10,7 +10,7 @@ const SLOT_RULES = `Component contract
|
|
|
10
10
|
- When a page has two or more response-bearing components, every one needs a nonempty \`label\`.
|
|
11
11
|
- Config objects and every documented nested object reject unknown fields.
|
|
12
12
|
- A quoted JSX attribute does not interpret escape sequences, so \`body="one.\\n\\ntwo."\` reaches the reader as one run-on paragraph with a literal backslash-n in the middle of it. Write any markdown body that has a line or paragraph break as a braced string — \`body={"one.\\n\\ntwo."}\` — where the escape is real.
|
|
13
|
-
- The page is rendered once at submit to build the ticket manifest, so every prop reaches it whatever its shape — mapped data, a helper component, a value from \`useState\`. Event handlers are dropped, because a manifest is JSON. The user's answer
|
|
13
|
+
- The page is rendered once at submit to build the ticket manifest, so every prop reaches it whatever its shape — mapped data, a helper component, a value from \`useState\`. Event handlers are dropped, because a manifest is JSON. The user's answer is stored in the request's terminal result under this \`id\`.`;
|
|
14
14
|
const COMMENT_SHAPE = `Comments are \`{id:string (nonempty), text:string, anchor:Anchor}\` and qualify the answer itself. \`Anchor\` is \`{kind:"option",optionId:string}\` or \`{kind:"row",rowId:string}\`; each component accepts only the anchors named next. The reader writes one against a specific option or row in whatever host they read in — in the terminal inbox, \`c\` on the selected one — so every option and row needs a label that reads on its own. Feedback on the page's own prose is a separate channel, offered only by hosts that render the page, and never part of a response.`;
|
|
15
15
|
const DISPLAY_RULES = `Display-only: it is not recorded in the page manifest and contributes nothing to the response. Every display component also takes \`className\` for Tailwind utilities, and standard shadcn props otherwise.`;
|
|
16
16
|
export const BUILTIN_COMPONENT_DOCS = [
|
|
@@ -18,11 +18,12 @@ import { reviveNode } from '../runtime/revive.js';
|
|
|
18
18
|
import { materializeSessionBranchAt } from '../runtime/session-branch.js';
|
|
19
19
|
import { initializeForkContextExposure } from '../substrate/injected-store.js';
|
|
20
20
|
import { pagePath } from './convention.js';
|
|
21
|
+
import { selectedPageDir } from './tickets.js';
|
|
21
22
|
export function feedbackCompanionBranchPath(dir) { return `${dir}/companion-branch.jsonl`; }
|
|
22
23
|
/** Hex sha256 of the ticket's page document, recorded at first comment so the
|
|
23
24
|
* settle-time completion can say whether the page changed during feedback. */
|
|
24
25
|
export function pageDocumentSha256(dir) {
|
|
25
|
-
return createHash('sha256').update(readFileSync(pagePath(dir, 'jsx'))).digest('hex');
|
|
26
|
+
return createHash('sha256').update(readFileSync(pagePath(selectedPageDir(dir), 'jsx'))).digest('hex');
|
|
26
27
|
}
|
|
27
28
|
/** Materialize the companion's fork branch, reusing only an already-valid one. */
|
|
28
29
|
function materializeFeedbackBranch(dir, capture) {
|
|
@@ -65,7 +66,7 @@ export async function realizeFeedbackCompanion(args) {
|
|
|
65
66
|
cwd: origin.cwd,
|
|
66
67
|
profileId: origin.profile_id ?? null,
|
|
67
68
|
});
|
|
68
|
-
const node = spawnNode({
|
|
69
|
+
const node = await spawnNode({
|
|
69
70
|
kind: origin.kind,
|
|
70
71
|
mode: origin.mode,
|
|
71
72
|
lifecycle: 'resident',
|
|
@@ -82,7 +83,7 @@ export async function realizeFeedbackCompanion(args) {
|
|
|
82
83
|
review_id: args.ticketId,
|
|
83
84
|
origin_node_id: args.senderNodeId,
|
|
84
85
|
branch_file: branchFile,
|
|
85
|
-
target_file: pagePath(args.dir, 'jsx'),
|
|
86
|
+
target_file: pagePath(selectedPageDir(args.dir), 'jsx'),
|
|
86
87
|
},
|
|
87
88
|
name: `Feedback · ${args.title}`,
|
|
88
89
|
launch,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export declare const PAGE_FEEDBACK_SCHEMA: "crtr.page-feedback/v1";
|
|
3
|
+
/** The path is a presentation projection, not the feedback authority. */
|
|
3
4
|
export declare function pageFeedbackPath(dir: string): string;
|
|
4
5
|
declare const pageFeedbackCommentSchema: z.ZodObject<{
|
|
5
6
|
id: z.ZodString;
|
|
@@ -32,24 +33,19 @@ declare const pageFeedbackSchema: z.ZodObject<{
|
|
|
32
33
|
}, z.core.$strict>;
|
|
33
34
|
export type PageFeedbackComment = z.infer<typeof pageFeedbackCommentSchema>;
|
|
34
35
|
export type PageFeedback = z.infer<typeof pageFeedbackSchema>;
|
|
35
|
-
/** Read
|
|
36
|
+
/** Read page feedback from the request row. */
|
|
36
37
|
export declare function readPageFeedback(dir: string): PageFeedback;
|
|
37
38
|
export interface CreatePageFeedbackCommentInput {
|
|
38
39
|
quote: string;
|
|
39
40
|
context?: string;
|
|
40
41
|
note: string;
|
|
41
|
-
/** Bound as the ticket's companion when none is recorded yet. An already
|
|
42
|
-
* recorded companion wins; the first writer's companion stays the target. */
|
|
43
42
|
companionNodeId?: string;
|
|
44
|
-
/** Recorded with the companion binding; ignored once one is recorded. */
|
|
45
43
|
firstCommentedSha256?: string;
|
|
46
44
|
}
|
|
47
45
|
export interface PageFeedbackMutation {
|
|
48
46
|
comment: PageFeedbackComment;
|
|
49
47
|
feedback: PageFeedback;
|
|
50
48
|
}
|
|
51
|
-
/** Append one open feedback comment under the ticket lock. */
|
|
52
|
-
export declare function createPageFeedbackComment(dir: string, input: CreatePageFeedbackCommentInput): PageFeedbackMutation;
|
|
53
49
|
export declare class PageTicketTerminalError extends Error {
|
|
54
50
|
constructor();
|
|
55
51
|
}
|
|
@@ -59,7 +55,8 @@ export declare class UnknownPageFeedbackCommentError extends Error {
|
|
|
59
55
|
export declare class PageFeedbackCommentAlreadyResolvedError extends Error {
|
|
60
56
|
constructor(commentId: string);
|
|
61
57
|
}
|
|
62
|
-
/**
|
|
63
|
-
|
|
58
|
+
/** Append feedback in the request transaction; terminal rows reject it. */
|
|
59
|
+
export declare function createPageFeedbackComment(dir: string, input: CreatePageFeedbackCommentInput): PageFeedbackMutation;
|
|
60
|
+
/** Resolve one feedback comment in the request transaction. */
|
|
64
61
|
export declare function resolvePageFeedbackComment(dir: string, commentId: string): PageFeedbackMutation;
|
|
65
62
|
export {};
|
|
@@ -1,116 +1,88 @@
|
|
|
1
|
-
// Page feedback comments — ticket-attached notes on highlighted page content.
|
|
2
|
-
// Feedback is about the agent's WORK, never part of the page answer: it lives
|
|
3
|
-
// in its own file beside the page, delivers immediately to the ticket's
|
|
4
|
-
// companion conversation, and reaches the response the sender reads at settle
|
|
5
|
-
// only as the unresolved-item addendum. A sent comment is immutable — it is
|
|
6
|
-
// either open or resolved, and only the companion resolves it. The file name
|
|
7
|
-
// is `feedback-comments.json` — `feedback.json` is the review subsystem's
|
|
8
|
-
// output file and must not be shadowed.
|
|
9
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
10
1
|
import { randomUUID } from 'node:crypto';
|
|
11
2
|
import { z } from 'zod';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { pageTicketState } from './tickets.js';
|
|
3
|
+
import { withCanvasWrite } from '../canvas/db.js';
|
|
4
|
+
import { getRequestByDirectory } from './requests.js';
|
|
15
5
|
export const PAGE_FEEDBACK_SCHEMA = 'crtr.page-feedback/v1';
|
|
6
|
+
/** The path is a presentation projection, not the feedback authority. */
|
|
16
7
|
export function pageFeedbackPath(dir) { return `${dir}/feedback-comments.json`; }
|
|
17
8
|
const pageFeedbackCommentSchema = z.object({
|
|
18
|
-
id: z.string().min(1),
|
|
19
|
-
|
|
20
|
-
quote: z.string().min(1),
|
|
21
|
-
/** Surrounding text that locates the quote on the page. */
|
|
22
|
-
context: z.string().optional(),
|
|
23
|
-
note: z.string().min(1),
|
|
24
|
-
/** Open until the companion has dealt with it; resolution is terminal. */
|
|
25
|
-
status: z.enum(['open', 'resolved']),
|
|
26
|
-
createdAt: z.string().datetime({ offset: true }),
|
|
27
|
-
resolvedAt: z.string().datetime({ offset: true }).optional(),
|
|
9
|
+
id: z.string().min(1), quote: z.string().min(1), context: z.string().optional(), note: z.string().min(1),
|
|
10
|
+
status: z.enum(['open', 'resolved']), createdAt: z.string().datetime({ offset: true }), resolvedAt: z.string().datetime({ offset: true }).optional(),
|
|
28
11
|
}).strict();
|
|
29
12
|
const pageFeedbackSchema = z.object({
|
|
30
|
-
schema: z.literal(PAGE_FEEDBACK_SCHEMA),
|
|
31
|
-
|
|
32
|
-
* recorded by the first comment and never replaced. */
|
|
33
|
-
companionNodeId: z.string().min(1).optional(),
|
|
34
|
-
/** Hex sha256 of the page document at first comment, for the settle-time
|
|
35
|
-
* change notice to the sender. */
|
|
36
|
-
firstCommentedSha256: z.string().min(1).optional(),
|
|
37
|
-
comments: z.array(pageFeedbackCommentSchema),
|
|
13
|
+
schema: z.literal(PAGE_FEEDBACK_SCHEMA), companionNodeId: z.string().min(1).optional(),
|
|
14
|
+
firstCommentedSha256: z.string().min(1).optional(), comments: z.array(pageFeedbackCommentSchema),
|
|
38
15
|
}).strict();
|
|
39
|
-
function emptyFeedback() {
|
|
40
|
-
|
|
16
|
+
function emptyFeedback() { return { schema: PAGE_FEEDBACK_SCHEMA, comments: [] }; }
|
|
17
|
+
function requestForDirectory(dir) {
|
|
18
|
+
const request = getRequestByDirectory(dir);
|
|
19
|
+
if (request === null || request.kind !== 'page')
|
|
20
|
+
throw new Error(`unknown page request directory: ${dir}`);
|
|
21
|
+
return request;
|
|
41
22
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (!existsSync(path))
|
|
46
|
-
return emptyFeedback();
|
|
47
|
-
let raw;
|
|
23
|
+
function feedback(request) {
|
|
24
|
+
if (request.feedback_json === null)
|
|
25
|
+
return request.companion_node_id === null ? emptyFeedback() : { ...emptyFeedback(), companionNodeId: request.companion_node_id };
|
|
48
26
|
try {
|
|
49
|
-
|
|
27
|
+
return pageFeedbackSchema.parse(JSON.parse(request.feedback_json));
|
|
50
28
|
}
|
|
51
|
-
catch {
|
|
52
|
-
throw new Error(`page feedback is
|
|
29
|
+
catch (error) {
|
|
30
|
+
throw new Error(`page feedback is malformed for ${request.request_id}`, { cause: error });
|
|
53
31
|
}
|
|
54
|
-
const parsed = pageFeedbackSchema.safeParse(raw);
|
|
55
|
-
if (!parsed.success)
|
|
56
|
-
throw new Error(`page feedback is malformed: ${path}`);
|
|
57
|
-
return parsed.data;
|
|
58
|
-
}
|
|
59
|
-
/** Append one open feedback comment under the ticket lock. */
|
|
60
|
-
export function createPageFeedbackComment(dir, input) {
|
|
61
|
-
return withTicketLock(dir, () => {
|
|
62
|
-
requirePendingTicket(dir);
|
|
63
|
-
const feedback = readPageFeedback(dir);
|
|
64
|
-
if (feedback.companionNodeId === undefined && input.companionNodeId !== undefined) {
|
|
65
|
-
feedback.companionNodeId = input.companionNodeId;
|
|
66
|
-
if (input.firstCommentedSha256 !== undefined)
|
|
67
|
-
feedback.firstCommentedSha256 = input.firstCommentedSha256;
|
|
68
|
-
}
|
|
69
|
-
const comment = {
|
|
70
|
-
id: randomUUID(),
|
|
71
|
-
quote: input.quote,
|
|
72
|
-
...(input.context === undefined ? {} : { context: input.context }),
|
|
73
|
-
note: input.note,
|
|
74
|
-
status: 'open',
|
|
75
|
-
createdAt: new Date().toISOString(),
|
|
76
|
-
};
|
|
77
|
-
feedback.comments.push(comment);
|
|
78
|
-
atomicWriteJson(pageFeedbackPath(dir), feedback);
|
|
79
|
-
return { comment, feedback };
|
|
80
|
-
});
|
|
81
32
|
}
|
|
33
|
+
/** Read page feedback from the request row. */
|
|
34
|
+
export function readPageFeedback(dir) { return feedback(requestForDirectory(dir)); }
|
|
82
35
|
export class PageTicketTerminalError extends Error {
|
|
83
36
|
constructor() { super('ticket already has a terminal outcome'); }
|
|
84
37
|
}
|
|
85
|
-
/** Settlement commits its terminal outcome under this same ticket lock, so an
|
|
86
|
-
* in-lock pending check is the race-free gate: no feedback mutation may land
|
|
87
|
-
* after the first ticket-ending action, however long the caller's own
|
|
88
|
-
* pre-lock work (a companion fork await) took. */
|
|
89
|
-
function requirePendingTicket(dir) {
|
|
90
|
-
const state = pageTicketState(dir);
|
|
91
|
-
if (state === 'resolved' || state === 'canceled')
|
|
92
|
-
throw new PageTicketTerminalError();
|
|
93
|
-
}
|
|
94
38
|
export class UnknownPageFeedbackCommentError extends Error {
|
|
95
39
|
constructor(commentId) { super(`no feedback comment ${commentId} on this ticket`); }
|
|
96
40
|
}
|
|
97
41
|
export class PageFeedbackCommentAlreadyResolvedError extends Error {
|
|
98
42
|
constructor(commentId) { super(`feedback comment ${commentId} is already resolved`); }
|
|
99
43
|
}
|
|
100
|
-
/**
|
|
101
|
-
|
|
44
|
+
/** Append feedback in the request transaction; terminal rows reject it. */
|
|
45
|
+
export function createPageFeedbackComment(dir, input) {
|
|
46
|
+
const request = requestForDirectory(dir);
|
|
47
|
+
return withCanvasWrite((db) => {
|
|
48
|
+
const current = db.prepare('SELECT * FROM human_requests WHERE request_id = ?').get(request.request_id);
|
|
49
|
+
if (current === undefined)
|
|
50
|
+
throw new Error(`human request disappeared: ${request.request_id}`);
|
|
51
|
+
if (current['state'] !== 'open')
|
|
52
|
+
throw new PageTicketTerminalError();
|
|
53
|
+
const row = { ...request, feedback_json: current['feedback_json'], companion_node_id: current['companion_node_id'] };
|
|
54
|
+
const next = feedback(row);
|
|
55
|
+
if (next.companionNodeId === undefined && input.companionNodeId !== undefined) {
|
|
56
|
+
next.companionNodeId = input.companionNodeId;
|
|
57
|
+
if (input.firstCommentedSha256 !== undefined)
|
|
58
|
+
next.firstCommentedSha256 = input.firstCommentedSha256;
|
|
59
|
+
}
|
|
60
|
+
const comment = { id: randomUUID(), quote: input.quote, ...(input.context === undefined ? {} : { context: input.context }), note: input.note, status: 'open', createdAt: new Date().toISOString() };
|
|
61
|
+
next.comments.push(comment);
|
|
62
|
+
pageFeedbackSchema.parse(next);
|
|
63
|
+
db.prepare('UPDATE human_requests SET feedback_json = ?, companion_node_id = COALESCE(companion_node_id, ?), updated_at = ? WHERE request_id = ?').run(JSON.stringify(next), next.companionNodeId ?? null, new Date().toISOString(), request.request_id);
|
|
64
|
+
return { comment, feedback: next };
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** Resolve one feedback comment in the request transaction. */
|
|
102
68
|
export function resolvePageFeedbackComment(dir, commentId) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
69
|
+
const request = requestForDirectory(dir);
|
|
70
|
+
return withCanvasWrite((db) => {
|
|
71
|
+
const current = db.prepare('SELECT * FROM human_requests WHERE request_id = ?').get(request.request_id);
|
|
72
|
+
if (current === undefined)
|
|
73
|
+
throw new Error(`human request disappeared: ${request.request_id}`);
|
|
74
|
+
if (current['state'] !== 'open')
|
|
75
|
+
throw new PageTicketTerminalError();
|
|
76
|
+
const row = { ...request, feedback_json: current['feedback_json'], companion_node_id: current['companion_node_id'] };
|
|
77
|
+
const next = feedback(row);
|
|
78
|
+
const comment = next.comments.find((entry) => entry.id === commentId);
|
|
107
79
|
if (comment === undefined)
|
|
108
80
|
throw new UnknownPageFeedbackCommentError(commentId);
|
|
109
81
|
if (comment.status === 'resolved')
|
|
110
82
|
throw new PageFeedbackCommentAlreadyResolvedError(commentId);
|
|
111
83
|
comment.status = 'resolved';
|
|
112
84
|
comment.resolvedAt = new Date().toISOString();
|
|
113
|
-
|
|
114
|
-
return { comment, feedback };
|
|
85
|
+
db.prepare('UPDATE human_requests SET feedback_json = ?, updated_at = ? WHERE request_id = ?').run(JSON.stringify(next), new Date().toISOString(), request.request_id);
|
|
86
|
+
return { comment, feedback: next };
|
|
115
87
|
});
|
|
116
88
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ProductPageComponents } from '../../types.js';
|
|
2
|
+
import type { PageDelivery, PageManifest, PageSource } from './page-schema.js';
|
|
3
|
+
/** One validated page, ready for the daemon to publish. */
|
|
4
|
+
export interface PreparedPage {
|
|
5
|
+
/** The authored source, verbatim, as the request's `document`. */
|
|
6
|
+
document: string;
|
|
7
|
+
/** The compiled CommonJS module the Gateway evaluates for JSX pages. */
|
|
8
|
+
module?: string;
|
|
9
|
+
manifest: PageManifest;
|
|
10
|
+
}
|
|
11
|
+
export interface PreparePageSourceOptions {
|
|
12
|
+
/** `jsx` carries .tsx semantics; `html` is a complete document. */
|
|
13
|
+
dialect: 'jsx' | 'html';
|
|
14
|
+
/** The complete authored page text, inline. */
|
|
15
|
+
document: string;
|
|
16
|
+
source?: PageSource;
|
|
17
|
+
productKinds?: ProductPageComponents;
|
|
18
|
+
delivery?: PageDelivery;
|
|
19
|
+
}
|
|
20
|
+
export interface PreparePageOptions {
|
|
21
|
+
/** The authored page source. */
|
|
22
|
+
sourceFile: string;
|
|
23
|
+
source?: PageSource;
|
|
24
|
+
productKinds?: ProductPageComponents;
|
|
25
|
+
delivery?: PageDelivery;
|
|
26
|
+
}
|
|
27
|
+
/** Validate one authored page document into a transportable request. */
|
|
28
|
+
export declare function preparePageSource(opts: PreparePageSourceOptions): PreparedPage;
|
|
29
|
+
/** Validate one authored page source file without reading or writing canvas state. */
|
|
30
|
+
export declare function preparePage(opts: PreparePageOptions): PreparedPage;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { compilePage, deriveHtmlPageManifest, derivePageManifestFromDocument, PageAuthoringError } from './page.js';
|
|
3
|
+
/** Validate one authored page document into a transportable request. */
|
|
4
|
+
export function preparePageSource(opts) {
|
|
5
|
+
const stamped = { ...(opts.source ?? {}), emittedAt: opts.source?.emittedAt ?? new Date().toISOString() };
|
|
6
|
+
const delivery = opts.delivery ?? { placement: 'inline', inbox: false, reply: false };
|
|
7
|
+
if (opts.dialect === 'html')
|
|
8
|
+
return { document: opts.document, manifest: deriveHtmlPageManifest(opts.document, stamped, delivery) };
|
|
9
|
+
return { document: opts.document, module: compilePage(opts.document), manifest: derivePageManifestFromDocument(opts.document, opts.productKinds ?? [], stamped, delivery) };
|
|
10
|
+
}
|
|
11
|
+
/** Validate one authored page source file without reading or writing canvas state. */
|
|
12
|
+
export function preparePage(opts) {
|
|
13
|
+
const html = /\.html?$/i.test(opts.sourceFile);
|
|
14
|
+
if (!html && !opts.sourceFile.endsWith('.tsx'))
|
|
15
|
+
throw new PageAuthoringError(`page source file must be .tsx, .html, or .htm: ${opts.sourceFile}`);
|
|
16
|
+
if (!existsSync(opts.sourceFile))
|
|
17
|
+
throw new PageAuthoringError(`page source file does not exist: ${opts.sourceFile}`);
|
|
18
|
+
return preparePageSource({ ...opts, dialect: html ? 'html' : 'jsx', document: readFileSync(opts.sourceFile, 'utf8') });
|
|
19
|
+
}
|