@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
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import test from 'node:test';
|
|
5
5
|
import { contextDir, jobDir, nodeDir, reportsDir } from '../../canvas/paths.js';
|
|
6
|
+
import { listNodes, listWarmSpares } from '../../canvas/canvas.js';
|
|
7
|
+
import { readExternalActionDelivery } from '../../canvas/deliveries.js';
|
|
6
8
|
import { validateHookManifest } from '../../command-hooks/schema.js';
|
|
7
9
|
import { headlessBrokerHost } from '../../runtime/host.js';
|
|
8
10
|
import { reviveNode } from '../../runtime/revive.js';
|
|
11
|
+
import { handleCreate, handleFork } from '../../../daemon/api/handlers/nodes.js';
|
|
12
|
+
import { armCron, getCron } from '../../canvas/crons.js';
|
|
13
|
+
import { settleRun } from '../../../daemon/cron/settle.js';
|
|
14
|
+
import { DeliveryLaneReconciler } from '../../../daemon/reconcilers/delivery-lane.js';
|
|
15
|
+
import { relaunchRoot } from '../../runtime/reset.js';
|
|
16
|
+
import { recycleNode } from '../../runtime/recycle.js';
|
|
9
17
|
import { createHarness } from '../helpers/harness.js';
|
|
10
18
|
function installLifecyclePlugin(harness, name, op) {
|
|
11
19
|
const ops = typeof op === 'string' ? [op] : op;
|
|
@@ -53,6 +61,64 @@ process.stdin.on('end', () => {
|
|
|
53
61
|
`);
|
|
54
62
|
chmodSync(executable, 0o755);
|
|
55
63
|
}
|
|
64
|
+
function installCreateLifecyclePlugin(harness, name, declarations, behavior) {
|
|
65
|
+
const root = join(harness.agentHome, '.crouter', 'plugins', name);
|
|
66
|
+
const records = join(root, 'records.jsonl');
|
|
67
|
+
mkdirSync(join(root, '.crouter-plugin'), { recursive: true });
|
|
68
|
+
writeFileSync(join(root, '.crouter-plugin', 'plugin.json'), JSON.stringify({
|
|
69
|
+
name,
|
|
70
|
+
version: '0.1.0',
|
|
71
|
+
description: 'node create lifecycle fixture',
|
|
72
|
+
hooks: 'hooks.json',
|
|
73
|
+
hookExecutable: 'bin/hook.js',
|
|
74
|
+
}));
|
|
75
|
+
writeFileSync(join(root, 'hooks.json'), JSON.stringify({
|
|
76
|
+
schemaVersion: 2,
|
|
77
|
+
lifecycle: declarations.map((declaration) => ({ ...declaration, description: `${declaration.op} fixture`, effects: ['writes fixture state'] })),
|
|
78
|
+
}));
|
|
79
|
+
const executable = join(root, 'bin', 'hook.js');
|
|
80
|
+
mkdirSync(join(root, 'bin'), { recursive: true });
|
|
81
|
+
writeFileSync(executable, `#!/usr/bin/env node
|
|
82
|
+
const { appendFileSync, existsSync, readdirSync } = require('node:fs');
|
|
83
|
+
const { join } = require('node:path');
|
|
84
|
+
const behavior = ${JSON.stringify(behavior)};
|
|
85
|
+
let input = '';
|
|
86
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
87
|
+
process.stdin.on('end', () => {
|
|
88
|
+
const request = JSON.parse(input);
|
|
89
|
+
appendFileSync(${JSON.stringify(records)}, JSON.stringify(request) + '\\n');
|
|
90
|
+
if (request.event === 'node:create' && behavior === 'refuse') {
|
|
91
|
+
process.stdout.write(JSON.stringify({ protocolVersion: 1, ok: false, error: { code: 'refused', message: 'fixture refusal' } }));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (request.event === 'node:create' && behavior === 'crash') process.exit(1);
|
|
95
|
+
if (request.event === 'node:create' && behavior === 'hang') return setInterval(() => {}, 1000);
|
|
96
|
+
if (request.event === 'node:create' && behavior === 'bad-output') return process.stdout.write('not an envelope');
|
|
97
|
+
if (request.event === 'node:create' && behavior === 'second-refuses') {
|
|
98
|
+
const nodes = join(request.runtime.canvasHome, 'nodes');
|
|
99
|
+
if (existsSync(nodes) && readdirSync(nodes).some((entry) => entry !== 'index.json')) {
|
|
100
|
+
process.stdout.write(JSON.stringify({ protocolVersion: 1, ok: false, error: { code: 'occupied', message: 'second create refused' } }));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (request.event === 'node:start' && behavior === 'start-refuses') {
|
|
105
|
+
process.stdout.write(JSON.stringify({ protocolVersion: 1, ok: false, error: { code: 'ignored', message: 'start refusal' } }));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
process.stdout.write(JSON.stringify({ protocolVersion: 1, ok: true }));
|
|
109
|
+
});
|
|
110
|
+
`);
|
|
111
|
+
chmodSync(executable, 0o755);
|
|
112
|
+
return { root, records };
|
|
113
|
+
}
|
|
114
|
+
function createContext(body, params = {}) {
|
|
115
|
+
return { method: 'POST', path: '/v1/nodes', params, query: new URLSearchParams(), body };
|
|
116
|
+
}
|
|
117
|
+
function lifecycleRecords(path) {
|
|
118
|
+
return existsSync(path)
|
|
119
|
+
? readFileSync(path, 'utf8').trim().split('\n').filter(Boolean).map((line) => JSON.parse(line))
|
|
120
|
+
: [];
|
|
121
|
+
}
|
|
56
122
|
function installV1CommandHook(harness) {
|
|
57
123
|
const root = join(harness.agentHome, '.crouter', 'plugins', 'z-v1-command');
|
|
58
124
|
mkdirSync(join(root, '.crouter-plugin'), { recursive: true });
|
|
@@ -149,6 +215,244 @@ test('node:start hooks receive resolved paths before broker launch; invalid and
|
|
|
149
215
|
await harness.dispose();
|
|
150
216
|
}
|
|
151
217
|
});
|
|
218
|
+
test('node:create fails closed before a row exists and node:start fires on cold birth', async () => {
|
|
219
|
+
const harness = await createHarness({ headless: true, sessionPrefix: 'crtr-node-create-hooks' });
|
|
220
|
+
try {
|
|
221
|
+
const birth = installCreateLifecyclePlugin(harness, 'birth-fixture', [
|
|
222
|
+
{ event: 'node:create', phase: 'before', op: 'record-create' },
|
|
223
|
+
{ event: 'node:start', phase: 'on', op: 'record-start' },
|
|
224
|
+
], 'start-refuses');
|
|
225
|
+
const created = await handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '' }));
|
|
226
|
+
const nodeId = created.body.node_id;
|
|
227
|
+
await harness.awaitBoot(nodeId);
|
|
228
|
+
const records = lifecycleRecords(birth.records);
|
|
229
|
+
const create = records.find((record) => record.event === 'node:create');
|
|
230
|
+
assert.deepEqual(create.create, {
|
|
231
|
+
kind: 'developer', mode: 'base', cwd: harness.agentHome, profile: null, root: true, parentId: null, requestingNodeId: null, lifecycle: 'resident', replaces: null,
|
|
232
|
+
});
|
|
233
|
+
assert.deepEqual(create.runtime, { canvasHome: harness.home, profile: null });
|
|
234
|
+
assert.deepEqual(create.context, { cwd: harness.agentHome });
|
|
235
|
+
assert.equal(Object.hasOwn(create, 'node'), false);
|
|
236
|
+
const start = records.find((record) => record.event === 'node:start');
|
|
237
|
+
assert.equal(start.runtime.isBirth, true);
|
|
238
|
+
assert.ok(harness.node(nodeId), 'a refusing node:start hook leaves the cold birth launched');
|
|
239
|
+
rmSync(birth.root, { recursive: true, force: true });
|
|
240
|
+
for (const behavior of ['refuse', 'crash', 'bad-output', 'hang']) {
|
|
241
|
+
const plugin = installCreateLifecyclePlugin(harness, `create-${behavior}`, [{ event: 'node:create', phase: 'before', op: behavior }], behavior);
|
|
242
|
+
const count = listNodes().length;
|
|
243
|
+
await assert.rejects(() => handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '' })), (error) => {
|
|
244
|
+
assert.equal(error.code, 'node_create_refused');
|
|
245
|
+
assert.match(error.message, new RegExp(`${behavior === 'refuse' ? 'fixture refusal' : `plugin "create-${behavior}" lifecycle hook "${behavior}"`}`));
|
|
246
|
+
return true;
|
|
247
|
+
});
|
|
248
|
+
assert.equal(listNodes().length, count, `${behavior} leaves no node row`);
|
|
249
|
+
rmSync(plugin.root, { recursive: true, force: true });
|
|
250
|
+
}
|
|
251
|
+
const missing = installCreateLifecyclePlugin(harness, 'create-missing', [{ event: 'node:create', phase: 'before', op: 'missing-op' }], 'allow');
|
|
252
|
+
rmSync(join(missing.root, 'bin', 'hook.js'));
|
|
253
|
+
await assert.rejects(() => handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '' })), (error) => {
|
|
254
|
+
assert.equal(error.code, 'node_create_refused');
|
|
255
|
+
assert.match(error.message, /plugin "create-missing" lifecycle hook "missing-op"/);
|
|
256
|
+
return true;
|
|
257
|
+
});
|
|
258
|
+
rmSync(missing.root, { recursive: true, force: true });
|
|
259
|
+
const malformed = installCreateLifecyclePlugin(harness, 'create-malformed', [
|
|
260
|
+
{ event: 'node:create', phase: 'before', op: 'valid-create' },
|
|
261
|
+
{ event: 'node:create', phase: 'on', op: 'invalid-create' },
|
|
262
|
+
], 'allow');
|
|
263
|
+
const count = listNodes().length;
|
|
264
|
+
await assert.rejects(() => handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '' })), (error) => {
|
|
265
|
+
assert.equal(error.code, 'node_create_refused');
|
|
266
|
+
assert.match(error.message, /plugin "create-malformed" lifecycle hook "valid-create"/);
|
|
267
|
+
return true;
|
|
268
|
+
});
|
|
269
|
+
assert.equal(listNodes().length, count, 'a later malformed node:create declaration leaves no node row');
|
|
270
|
+
rmSync(malformed.root, { recursive: true, force: true });
|
|
271
|
+
const malformedOtherLifecycle = installCreateLifecyclePlugin(harness, 'create-malformed-other-lifecycle', [
|
|
272
|
+
{ event: 'node:start', phase: 'before', op: 'invalid-start' },
|
|
273
|
+
{ event: 'node:create', phase: 'before', op: 'valid-create' },
|
|
274
|
+
], 'allow');
|
|
275
|
+
await assert.rejects(() => handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '' })), (error) => {
|
|
276
|
+
assert.equal(error.code, 'node_create_refused');
|
|
277
|
+
assert.match(error.message, /plugin "create-malformed-other-lifecycle" lifecycle hook "valid-create"/);
|
|
278
|
+
return true;
|
|
279
|
+
});
|
|
280
|
+
rmSync(malformedOtherLifecycle.root, { recursive: true, force: true });
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
await harness.dispose();
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
test('node:fork runs the node:create gate for its new resident root', async () => {
|
|
287
|
+
const harness = await createHarness({ headless: true, sessionPrefix: 'crtr-node-fork-create' });
|
|
288
|
+
try {
|
|
289
|
+
const plugin = installCreateLifecyclePlugin(harness, 'fork-create', [{ event: 'node:create', phase: 'before', op: 'record-fork' }], 'allow');
|
|
290
|
+
const source = await handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '' }));
|
|
291
|
+
const sourceId = source.body.node_id;
|
|
292
|
+
await harness.awaitBoot(sourceId);
|
|
293
|
+
const fork = await handleFork(createContext({}, { id: sourceId }));
|
|
294
|
+
const forkId = fork.body.node_id;
|
|
295
|
+
await harness.awaitBoot(forkId);
|
|
296
|
+
const creates = lifecycleRecords(plugin.records).filter((record) => record.event === 'node:create');
|
|
297
|
+
assert.equal(creates.length, 2);
|
|
298
|
+
assert.deepEqual(creates[1].create, {
|
|
299
|
+
kind: 'developer', mode: 'base', cwd: harness.agentHome, profile: null, root: true, parentId: null, requestingNodeId: null, lifecycle: 'resident', replaces: null,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
finally {
|
|
303
|
+
await harness.dispose();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
test('node:create gates warm mint and warm claim with the birth payload', async () => {
|
|
307
|
+
const harness = await createHarness({ headless: true, sessionPrefix: 'crtr-node-create-warm' });
|
|
308
|
+
const priorWarmPool = process.env['CRTR_WARM_POOL'];
|
|
309
|
+
process.env['CRTR_WARM_POOL'] = '1';
|
|
310
|
+
try {
|
|
311
|
+
const plugin = installCreateLifecyclePlugin(harness, 'warm-create', [{ event: 'node:create', phase: 'before', op: 'record-warm' }], 'allow');
|
|
312
|
+
const first = await handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '', prefer_warm: true }));
|
|
313
|
+
await harness.awaitBoot(first.body.node_id);
|
|
314
|
+
let spareId;
|
|
315
|
+
for (let i = 0; i < 100; i++) {
|
|
316
|
+
spareId = listWarmSpares()[0]?.node_id;
|
|
317
|
+
if (spareId !== undefined)
|
|
318
|
+
break;
|
|
319
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
320
|
+
}
|
|
321
|
+
assert.ok(spareId, 'the cold create refills one warm spare');
|
|
322
|
+
await harness.awaitBoot(spareId);
|
|
323
|
+
const createsBeforeClaim = lifecycleRecords(plugin.records).filter((record) => record.event === 'node:create').length;
|
|
324
|
+
const second = await handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '', prefer_warm: true }));
|
|
325
|
+
assert.equal(second.body.node_id, spareId, 'the second request claims the pre-born spare');
|
|
326
|
+
const creates = lifecycleRecords(plugin.records).filter((record) => record.event === 'node:create');
|
|
327
|
+
assert.equal(creates.length, createsBeforeClaim + 1, 'claiming the spare runs one additional create gate');
|
|
328
|
+
for (const record of creates) {
|
|
329
|
+
assert.deepEqual(record.create, {
|
|
330
|
+
kind: 'developer', mode: 'base', cwd: harness.agentHome, profile: null, root: true, parentId: null, requestingNodeId: null, lifecycle: 'resident', replaces: null,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
let replacementSpareId;
|
|
334
|
+
for (let i = 0; i < 100; i++) {
|
|
335
|
+
replacementSpareId = listWarmSpares()[0]?.node_id;
|
|
336
|
+
if (replacementSpareId !== undefined)
|
|
337
|
+
break;
|
|
338
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
339
|
+
}
|
|
340
|
+
assert.ok(replacementSpareId, 'a claimed spare is refilled before the test tears down its harness');
|
|
341
|
+
await harness.awaitBoot(replacementSpareId);
|
|
342
|
+
}
|
|
343
|
+
finally {
|
|
344
|
+
if (priorWarmPool === undefined)
|
|
345
|
+
delete process.env['CRTR_WARM_POOL'];
|
|
346
|
+
else
|
|
347
|
+
process.env['CRTR_WARM_POOL'] = priorWarmPool;
|
|
348
|
+
await harness.dispose();
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
test('a refused recycle leaves the old root active', async () => {
|
|
352
|
+
const harness = await createHarness({ headless: true, sessionPrefix: 'crtr-node-create-recycle' });
|
|
353
|
+
const priorWarmPool = process.env['CRTR_WARM_POOL'];
|
|
354
|
+
process.env['CRTR_WARM_POOL'] = '0';
|
|
355
|
+
try {
|
|
356
|
+
const source = await handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '' }));
|
|
357
|
+
const sourceId = source.body.node_id;
|
|
358
|
+
await harness.awaitBoot(sourceId);
|
|
359
|
+
installCreateLifecyclePlugin(harness, 'recycle-refusal', [{ event: 'node:create', phase: 'before', op: 'refuse-recycle' }], 'refuse');
|
|
360
|
+
await assert.rejects(() => recycleNode(sourceId, '%recycle-refusal'), /fixture refusal/);
|
|
361
|
+
assert.equal(harness.node(sourceId)?.status, 'active', 'the old root is unchanged when the replacement hook refuses');
|
|
362
|
+
}
|
|
363
|
+
finally {
|
|
364
|
+
if (priorWarmPool === undefined)
|
|
365
|
+
delete process.env['CRTR_WARM_POOL'];
|
|
366
|
+
else
|
|
367
|
+
process.env['CRTR_WARM_POOL'] = priorWarmPool;
|
|
368
|
+
await harness.dispose();
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
test('node:create reports the root replaced by /new', async () => {
|
|
372
|
+
const harness = await createHarness({ headless: true, sessionPrefix: 'crtr-node-create-relaunch' });
|
|
373
|
+
const priorWarmPool = process.env['CRTR_WARM_POOL'];
|
|
374
|
+
process.env['CRTR_WARM_POOL'] = '0';
|
|
375
|
+
try {
|
|
376
|
+
const plugin = installCreateLifecyclePlugin(harness, 'relaunch-create', [{ event: 'node:create', phase: 'before', op: 'record-relaunch' }], 'allow');
|
|
377
|
+
const source = await handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '' }));
|
|
378
|
+
const sourceId = source.body.node_id;
|
|
379
|
+
await harness.awaitBoot(sourceId);
|
|
380
|
+
const result = await relaunchRoot(sourceId, {
|
|
381
|
+
launchBroker: () => ({ pid: 999_999, exited: new Promise(() => { }) }),
|
|
382
|
+
waitForViewSocket: () => true,
|
|
383
|
+
respawnViewer: () => true,
|
|
384
|
+
teardownBroker: async () => ({ confirmed: true }),
|
|
385
|
+
});
|
|
386
|
+
assert.ok(result);
|
|
387
|
+
const create = lifecycleRecords(plugin.records).filter((record) => record.event === 'node:create').at(-1);
|
|
388
|
+
assert.deepEqual(create.create, {
|
|
389
|
+
kind: 'developer', mode: 'base', cwd: harness.agentHome, profile: null, root: true, parentId: null, requestingNodeId: null, lifecycle: 'resident', replaces: sourceId,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
finally {
|
|
393
|
+
if (priorWarmPool === undefined)
|
|
394
|
+
delete process.env['CRTR_WARM_POOL'];
|
|
395
|
+
else
|
|
396
|
+
process.env['CRTR_WARM_POOL'] = priorWarmPool;
|
|
397
|
+
await harness.dispose();
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
test('a refused cron spawn records its failure and does not throw', async () => {
|
|
401
|
+
const harness = await createHarness({ headless: true, sessionPrefix: 'crtr-node-create-cron' });
|
|
402
|
+
try {
|
|
403
|
+
installCreateLifecyclePlugin(harness, 'cron-create', [{ event: 'node:create', phase: 'before', op: 'refuse-cron' }], 'refuse');
|
|
404
|
+
armCron({
|
|
405
|
+
cron_id: 'refused-cron-spawn', name: 'refused cron spawn', created_by: null, command: 'echo fixture', fire_at: new Date().toISOString(), recur: null, tz: null, expires_at: null, anchor_node: null, cancel_on_wake: false,
|
|
406
|
+
cwd: harness.agentHome, env_json: null, profile: null, scope: 'global', run_timeout_s: 30, overlap: 'skip', on_output: 'always', sink: JSON.stringify({ spawn: 'developer' }), tier: 'normal',
|
|
407
|
+
});
|
|
408
|
+
const cron = getCron('refused-cron-spawn');
|
|
409
|
+
const record = await settleRun(cron, {
|
|
410
|
+
outOfBand: false,
|
|
411
|
+
outcome: {
|
|
412
|
+
kind: 'disposed',
|
|
413
|
+
ctx: {
|
|
414
|
+
runId: 'refused-cron-run', startedAtIso: new Date().toISOString(), exitCode: 0, timedOut: false, runTimeoutS: 30, processErrorMessage: null,
|
|
415
|
+
stdoutHead: 'fixture output', stdoutSha256: 'fixture-output-hash', stderrHead: '',
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
record: { runId: 'refused-cron-run', startedAtIso: new Date().toISOString(), finishedAtMs: Date.now(), durationMs: 1, exitCode: 0, stdoutHead: 'fixture output', stderrHead: null },
|
|
419
|
+
lease: 'none',
|
|
420
|
+
});
|
|
421
|
+
assert.match(record.delivered ?? '', /^delivered: /, 'the cron run records its durable delivery');
|
|
422
|
+
const pendingDelivery = readExternalActionDelivery('refused-cron-run');
|
|
423
|
+
assert.equal(pendingDelivery.state, 'pending');
|
|
424
|
+
assert.equal(pendingDelivery.destinationId, 'cron_spawn');
|
|
425
|
+
assert.equal(getCron(cron.cron_id), null, 'the one-shot occurrence is spent once its delivery is durable');
|
|
426
|
+
const pending = [];
|
|
427
|
+
new DeliveryLaneReconciler().run(Date.now(), {
|
|
428
|
+
lifecycle: { acceptsDetachedWork: () => true, registerDetached: (work) => { pending.push(work); } },
|
|
429
|
+
});
|
|
430
|
+
await Promise.all(pending);
|
|
431
|
+
const settledDelivery = readExternalActionDelivery('refused-cron-run');
|
|
432
|
+
assert.equal(settledDelivery.state, 'permanent_failed');
|
|
433
|
+
assert.match(settledDelivery.lastFailure?.message ?? '', /fixture refusal/);
|
|
434
|
+
assert.equal(listNodes().length, 0, 'the refused delivery neither creates a node nor escalates');
|
|
435
|
+
}
|
|
436
|
+
finally {
|
|
437
|
+
await harness.dispose();
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
test('node:create serializes concurrent creates through the persisted row', async () => {
|
|
441
|
+
const harness = await createHarness({ headless: true, sessionPrefix: 'crtr-node-create-lock' });
|
|
442
|
+
try {
|
|
443
|
+
installCreateLifecyclePlugin(harness, 'create-lock', [{ event: 'node:create', phase: 'before', op: 'only-one' }], 'second-refuses');
|
|
444
|
+
const request = () => handleCreate(createContext({ kind: 'developer', root: true, profile: null, cwd: harness.agentHome, prompt: '' }));
|
|
445
|
+
const results = await Promise.allSettled([request(), request()]);
|
|
446
|
+
assert.equal(results.filter((result) => result.status === 'fulfilled').length, 1);
|
|
447
|
+
const refusal = results.find((result) => result.status === 'rejected');
|
|
448
|
+
assert.equal(refusal.reason.code, 'node_create_refused');
|
|
449
|
+
assert.match(refusal.reason.message, /second create refused/);
|
|
450
|
+
assert.equal(listNodes().length, 1);
|
|
451
|
+
}
|
|
452
|
+
finally {
|
|
453
|
+
await harness.dispose();
|
|
454
|
+
}
|
|
455
|
+
});
|
|
152
456
|
test('hooks.json v1 remains command-only while v2 accepts a lifecycle-only declaration', () => {
|
|
153
457
|
const v1 = validateHookManifest({
|
|
154
458
|
schemaVersion: 1,
|
|
@@ -176,10 +480,17 @@ test('hooks.json v1 remains command-only while v2 accepts a lifecycle-only decla
|
|
|
176
480
|
lifecycle: [{ event: 'node:close', phase: 'on', op: 'cleanup', description: 'cleanup', effects: ['removes a directory'] }],
|
|
177
481
|
});
|
|
178
482
|
assert.deepEqual(close.issues, []);
|
|
483
|
+
const create = validateHookManifest({
|
|
484
|
+
schemaVersion: 2,
|
|
485
|
+
lifecycle: [{ event: 'node:create', phase: 'before', op: 'guard', description: 'guard', effects: ['refuses a create'] }],
|
|
486
|
+
});
|
|
487
|
+
assert.deepEqual(create.issues, []);
|
|
179
488
|
for (const manifest of [
|
|
180
489
|
{ schemaVersion: 2, hooks: [], lifecycle: [] },
|
|
181
490
|
{ schemaVersion: 2, lifecycle: [{ event: 'node:stop', phase: 'on', op: 'prepare', description: 'prepare', effects: ['creates a directory'] }] },
|
|
182
491
|
{ schemaVersion: 2, lifecycle: [{ event: 'node:start', phase: 'before', op: 'prepare', description: 'prepare', effects: ['creates a directory'] }] },
|
|
492
|
+
{ schemaVersion: 2, lifecycle: [{ event: 'node:create', phase: 'on', op: 'prepare', description: 'prepare', effects: ['creates a directory'] }] },
|
|
493
|
+
{ schemaVersion: 2, lifecycle: [{ event: 'node:close', phase: 'before', op: 'prepare', description: 'prepare', effects: ['creates a directory'] }] },
|
|
183
494
|
{ schemaVersion: 2, lifecycle: [{ event: 'node:start', phase: 'on', op: 'prepare', description: 'prepare', effects: ['creates a directory'], unexpected: true }] },
|
|
184
495
|
]) {
|
|
185
496
|
assert.equal(validateHookManifest(manifest).manifest, undefined, 'invalid v2 lifecycle declarations are rejected');
|
|
@@ -31,6 +31,8 @@ import { join } from 'node:path';
|
|
|
31
31
|
import * as tar from 'tar';
|
|
32
32
|
import { resetScopeCache, userScopeRoot } from '../../scope.js';
|
|
33
33
|
import { readState, readConfig } from '../../config.js';
|
|
34
|
+
import { closeScopedStores } from '../../scoped-state/db.js';
|
|
35
|
+
import { initializeFreshScopedStore } from '../../scoped-state/migrate.js';
|
|
34
36
|
import { revalidateBundlePlugins, forceRevalidateBundlePlugin } from '../../command-plugins/revalidate.js';
|
|
35
37
|
import { pluginInstall } from '../../../commands/pkg/plugin-manage.js';
|
|
36
38
|
import { runCli } from '../../command.js';
|
|
@@ -68,6 +70,7 @@ beforeEach(async () => {
|
|
|
68
70
|
isolatedCwd = realpathSync(mkdtempSync(join(tmpdir(), 'crtr-revalidate-cwd-')));
|
|
69
71
|
process.env.HOME = isolatedHome;
|
|
70
72
|
process.chdir(isolatedCwd);
|
|
73
|
+
initializeFreshScopedStore({ kind: 'user', root: join(isolatedHome, '.crouter') });
|
|
71
74
|
resetScopeCache();
|
|
72
75
|
served = { archive: makeArchive('v1'), etag: '"sha256-v1"' };
|
|
73
76
|
requests = [];
|
|
@@ -105,6 +108,7 @@ beforeEach(async () => {
|
|
|
105
108
|
afterEach(async () => {
|
|
106
109
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
107
110
|
process.chdir(prevCwd);
|
|
111
|
+
closeScopedStores();
|
|
108
112
|
rmSync(isolatedHome, { recursive: true, force: true });
|
|
109
113
|
rmSync(isolatedCwd, { recursive: true, force: true });
|
|
110
114
|
});
|
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
|
4
4
|
import { contextDir } from '../../canvas/paths.js';
|
|
5
5
|
import { getRow } from '../../canvas/canvas.js';
|
|
6
6
|
import { bashJobPaths, bashJobsDir } from '../../bash-jobs.js';
|
|
7
|
-
import {
|
|
7
|
+
import { acknowledgedNodeMailCount } from '../../canvas/deliveries.js';
|
|
8
8
|
import { transition } from '../../runtime/lifecycle.js';
|
|
9
9
|
import { ensureRootProfile, pauseProfile } from '../../profiles/manifest.js';
|
|
10
10
|
import { createHeadlessHarness } from '../helpers/harness.js';
|
|
@@ -22,7 +22,7 @@ function runningJob(nodeId) {
|
|
|
22
22
|
}
|
|
23
23
|
return undefined;
|
|
24
24
|
}
|
|
25
|
-
test('profile pause backgrounds real bash, retains its
|
|
25
|
+
test('profile pause backgrounds real bash, retains its queued SQL delivery, and recovers after a quick resume without failure', { timeout: 120_000 }, async () => {
|
|
26
26
|
const h = await createHeadlessHarness({ sessionPrefix: 'crtr-profile-pause' });
|
|
27
27
|
try {
|
|
28
28
|
const profile = ensureRootProfile();
|
|
@@ -39,7 +39,7 @@ test('profile pause backgrounds real bash, retains its inbox cursor, and recover
|
|
|
39
39
|
assert.equal(retained.code, 0, retained.stderr);
|
|
40
40
|
const retainedEntry = h.inbox(child).find((entry) => entry.data?.['body'] === 'mail retained while profile is paused');
|
|
41
41
|
assert.ok(retainedEntry, 'the message was durably appended');
|
|
42
|
-
assert.equal(
|
|
42
|
+
assert.equal(acknowledgedNodeMailCount(child), 0, 'pause leaves the queued SQL delivery unacknowledged');
|
|
43
43
|
const resumed = h.cli(null, ['profile', 'resume', profile.profileId]);
|
|
44
44
|
assert.equal(resumed.code, 0, resumed.stderr);
|
|
45
45
|
await h.awaitFleetExit(child);
|
|
@@ -47,7 +47,7 @@ test('profile pause backgrounds real bash, retains its inbox cursor, and recover
|
|
|
47
47
|
assert.equal(existsSync(job.jobRun), false, 'the foreground valve ownership is gone');
|
|
48
48
|
await h.waitFor(() => existsSync(job.jobExit), { label: 'background bash completion' });
|
|
49
49
|
assert.equal(readFileSync(job.jobExit, 'utf8').trim(), '0', 'the handoff command completed after its broker exited');
|
|
50
|
-
assert.equal(
|
|
50
|
+
assert.equal(acknowledgedNodeMailCount(child), 0, 'neither retained message nor background completion acknowledges the stopped broker delivery');
|
|
51
51
|
await h.tick();
|
|
52
52
|
assert.equal(h.bootCount(child), 1, 'the first resumed policy pass only consumes the pause interruption');
|
|
53
53
|
assert.equal(getRow(child)?.respawn_failures, 0, 'the intentional SIGTERM never counts as a crash');
|
|
@@ -21,13 +21,10 @@
|
|
|
21
21
|
import { test } from 'node:test';
|
|
22
22
|
import assert from 'node:assert/strict';
|
|
23
23
|
import { spawn } from 'node:child_process';
|
|
24
|
-
import { utimesSync } from 'node:fs';
|
|
25
|
-
import { join } from 'node:path';
|
|
26
24
|
import { createHeadlessHarness } from '../helpers/harness.js';
|
|
27
|
-
import { getRow
|
|
28
|
-
import {
|
|
25
|
+
import { getRow } from '../../canvas/canvas.js';
|
|
26
|
+
import { abortExecution, beginExecution, recordExecutionPid } from '../../canvas/executions.js';
|
|
29
27
|
import { isPidAlive } from '../../canvas/pid.js';
|
|
30
|
-
import { markBusy } from '../../runtime/busy.js';
|
|
31
28
|
import { BrokerSupervisionReconciler, yieldStallVerdict, YIELD_STALL_GRACE_MS } from '../../../daemon/reconcilers/broker-supervision.js';
|
|
32
29
|
// D4a — the pure refresh-stall truth table.
|
|
33
30
|
test('D4a yieldStallVerdict: kill ONLY a live, not-busy, refresh engine past the grace', () => {
|
|
@@ -61,7 +58,8 @@ test('D4b daemon force-recycles a stuck refresh: a live, not-busy refresh engine
|
|
|
61
58
|
// A live broker records its launch-time identity with its pid. The force
|
|
62
59
|
// signal re-verifies that identity, so this fixture must model the
|
|
63
60
|
// production record rather than a legacy identity-less row.
|
|
64
|
-
|
|
61
|
+
const executionId = beginExecution(id, { daemonEpoch: h.fleet.epoch() });
|
|
62
|
+
recordExecutionPid(executionId, id, enginePid);
|
|
65
63
|
// Post-cutover the live-fleet pass iterates fleet ENTRIES, not rows: a
|
|
66
64
|
// live broker IS a fleet entry (design D-1). Model the launch-time
|
|
67
65
|
// registration the production host performs, with a never-resolving
|
|
@@ -103,8 +101,8 @@ test('D4c yield escalation is scoped to the broker that received SIGTERM', { tim
|
|
|
103
101
|
pi_pid: original.pid,
|
|
104
102
|
pi_session_id: 'sess-d4c',
|
|
105
103
|
});
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
const originalExecution = beginExecution(id, { daemonEpoch: h.fleet.epoch() });
|
|
105
|
+
recordExecutionPid(originalExecution, id, original.pid);
|
|
108
106
|
h.fleet.register(id, { pid: original.pid, exited: new Promise(() => { }) });
|
|
109
107
|
const supervisor = new BrokerSupervisionReconciler();
|
|
110
108
|
const context = () => ({
|
|
@@ -119,15 +117,14 @@ test('D4c yield escalation is scoped to the broker that received SIGTERM', { tim
|
|
|
119
117
|
timeoutMs: 10_000,
|
|
120
118
|
});
|
|
121
119
|
h.fleet.forget(id);
|
|
122
|
-
|
|
123
|
-
utimesSync(join(jobDir(id), 'busy'), T_KILL / 1_000, T_KILL / 1_000);
|
|
120
|
+
abortExecution(originalExecution, { reason: 'test replacement' });
|
|
124
121
|
replacement = spawn(process.execPath, ['-e', 'process.on("SIGTERM", () => {}); console.log("ready"); setInterval(() => {}, 1000)'], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
125
122
|
await new Promise((resolve, reject) => {
|
|
126
123
|
replacement.stdout.once('data', () => resolve());
|
|
127
124
|
replacement.once('error', reject);
|
|
128
125
|
});
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
const replacementExecution = beginExecution(id, { daemonEpoch: h.fleet.epoch() });
|
|
127
|
+
recordExecutionPid(replacementExecution, id, replacement.pid);
|
|
131
128
|
h.fleet.register(id, { pid: replacement.pid, exited: new Promise(() => { }) });
|
|
132
129
|
const replacementStarted = T_KILL + KILL_ESCALATE_MS + 1;
|
|
133
130
|
await supervisor.run(replacementStarted, context());
|