@north-light/crouter 0.3.312 → 0.3.313
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +27 -30
- package/dist/api/client.js +80 -67
- package/dist/api/dto/broker-ops.d.ts +10 -20
- package/dist/api/dto/canvas.d.ts +1 -10
- package/dist/api/dto/health.d.ts +113 -0
- package/dist/api/dto/human-requests.d.ts +14 -3
- package/dist/api/dto/human.d.ts +2 -48
- package/dist/api/dto/human.js +3 -2
- package/dist/api/dto/inbox.d.ts +14 -0
- package/dist/api/dto/mail.d.ts +43 -0
- package/dist/api/dto/nodes.d.ts +11 -8
- package/dist/api/dto/recovery.d.ts +96 -0
- package/dist/api/dto/reviews.d.ts +2 -2
- package/dist/api/routes.d.ts +9 -6
- package/dist/api/routes.js +11 -11
- package/dist/build-root.d.ts +4 -0
- package/dist/build-root.js +11 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
- package/dist/cli.js +10 -1
- package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
- package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
- package/dist/clients/attach/chrome/canvas-panels.js +1 -1
- package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
- package/dist/clients/attach/chrome/inbox-strip.js +17 -9
- package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
- package/dist/clients/attach/chrome/ticket-panel.js +77 -46
- package/dist/clients/attach/overlays/graph.js +6 -9
- package/dist/clients/attach/render/chat-view.d.ts +5 -4
- package/dist/clients/attach/render/chat-view.js +22 -13
- package/dist/clients/attach/render/page-block.d.ts +11 -7
- package/dist/clients/attach/render/page-block.js +64 -55
- package/dist/clients/attach/viewer.js +746 -735
- package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
- package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
- package/dist/clients/inbox/controller.d.ts +42 -14
- package/dist/clients/inbox/controller.js +240 -124
- package/dist/clients/inbox/dto-adapt.d.ts +28 -0
- package/dist/clients/inbox/dto-adapt.js +93 -0
- package/dist/clients/inbox/page-adapter.d.ts +5 -4
- package/dist/clients/inbox/page-adapter.js +12 -12
- package/dist/clients/inbox/resolve.d.ts +10 -5
- package/dist/clients/inbox/resolve.js +13 -12
- package/dist/clients/inbox/review/review-client.d.ts +0 -1
- package/dist/clients/inbox/review/review-client.js +0 -4
- package/dist/clients/inbox/review-adapter.d.ts +2 -1
- package/dist/clients/inbox/review-adapter.js +3 -5
- package/dist/clients/inbox/surface.d.ts +2 -2
- package/dist/clients/inbox/surface.js +19 -8
- package/dist/clients/inbox/tui.d.ts +3 -3
- package/dist/commands/__tests__/human.test.js +31 -15
- package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
- package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
- package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
- package/dist/commands/api-client.d.ts +5 -4
- package/dist/commands/api-client.js +13 -7
- package/dist/commands/canvas-history/shared.js +1 -1
- package/dist/commands/canvas-history.js +1 -1
- package/dist/commands/canvas-roster.js +1 -1
- package/dist/commands/canvas.js +1 -2
- package/dist/commands/human/feedback.js +20 -17
- package/dist/commands/human/prompts.js +45 -36
- package/dist/commands/human/queue.js +73 -123
- package/dist/commands/human/review.js +6 -16
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
- package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
- package/dist/commands/node-inspect-artifacts.js +1 -1
- package/dist/commands/node-lifecycle-revive.d.ts +2 -2
- package/dist/commands/node-lifecycle-revive.js +9 -19
- package/dist/commands/pkg/plugin-manage.js +0 -1
- package/dist/commands/profile/show.js +4 -12
- package/dist/commands/surface-tmux-spread.js +1 -5
- package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
- package/dist/commands/sys/branch.d.ts +2 -0
- package/dist/commands/sys/branch.js +19 -0
- package/dist/commands/sys/config.js +106 -33
- package/dist/commands/sys/daemon.d.ts +2 -0
- package/dist/commands/sys/daemon.js +130 -7
- package/dist/commands/sys/doctor.js +5 -15
- package/dist/commands/sys/migrate.js +21 -92
- package/dist/commands/sys/setup-settings-runner.js +4 -2
- package/dist/commands/sys.js +2 -15
- package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
- package/dist/core/__tests__/boot.test.js +32 -25
- package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
- package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
- package/dist/core/__tests__/canvas.test.js +18 -47
- package/dist/core/__tests__/child-death-wake.test.js +29 -7
- package/dist/core/__tests__/cli-help-contract.test.js +2 -0
- package/dist/core/__tests__/close.test.js +29 -10
- package/dist/core/__tests__/collectible.test.js +2 -1
- package/dist/core/__tests__/config-change-delta.test.js +2 -0
- package/dist/core/__tests__/context-intro.test.js +13 -1
- package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
- package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
- package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
- package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
- package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
- package/dist/core/__tests__/daemon-boot.test.js +51 -14
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
- package/dist/core/__tests__/focuses.test.js +10 -5
- package/dist/core/__tests__/helpers/harness.js +50 -18
- package/dist/core/__tests__/history-inbox.test.js +12 -16
- package/dist/core/__tests__/human-action-delivery.test.js +48 -28
- package/dist/core/__tests__/human-actions.test.js +7 -3
- package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
- package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
- package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
- package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
- package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
- package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
- package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
- package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
- package/dist/core/__tests__/integration/lifecycle-hooks.test.js +16 -3
- package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
- package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
- package/dist/core/__tests__/integration/revive.test.js +67 -21
- package/dist/core/__tests__/integration/spawn-root.test.js +51 -8
- package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
- package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
- package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
- package/dist/core/__tests__/kickoff.test.js +36 -39
- package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
- package/dist/core/__tests__/lifecycle.test.js +2 -1
- package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
- package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
- package/dist/core/__tests__/migration.test.js +145 -31
- package/dist/core/__tests__/model-pin-durability.test.js +4 -1
- package/dist/core/__tests__/model-routes-config.test.js +13 -5
- package/dist/core/__tests__/node-outcome.test.js +93 -26
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -1
- package/dist/core/__tests__/on-read-nested-store.test.js +2 -1
- package/dist/core/__tests__/passive-subscription.test.js +50 -157
- package/dist/core/__tests__/plugin-kinds.test.js +6 -4
- package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
- package/dist/core/__tests__/plugin-page-components.test.js +6 -4
- package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +4 -1
- package/dist/core/__tests__/prune.test.js +11 -5
- package/dist/core/__tests__/push-final-guard.test.js +14 -7
- package/dist/core/__tests__/relaunch-root.test.js +30 -2
- package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
- package/dist/core/__tests__/repository-root-identity.test.js +4 -0
- package/dist/core/__tests__/review-model-floor.test.js +6 -3
- package/dist/core/__tests__/revive-all.test.js +13 -3
- package/dist/core/__tests__/revive-capacity.test.js +26 -4
- package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
- package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
- package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
- package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
- package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
- package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
- package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
- package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
- package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +2 -1
- package/dist/core/__tests__/stop-guard.test.js +2 -1
- package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +18 -4
- package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
- package/dist/core/bootstrap.js +2 -0
- package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
- package/dist/core/broker-client/transport-socket.d.ts +1 -0
- package/dist/core/broker-client/transport-socket.js +13 -5
- package/dist/core/canvas/__tests__/attention.test.js +29 -35
- package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
- package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
- package/dist/core/canvas/attention.d.ts +2 -5
- package/dist/core/canvas/attention.js +15 -64
- package/dist/core/canvas/boot.d.ts +6 -7
- package/dist/core/canvas/boot.js +5 -9
- package/dist/core/canvas/canvas.d.ts +13 -109
- package/dist/core/canvas/canvas.js +140 -401
- package/dist/core/canvas/crons.d.ts +2 -0
- package/dist/core/canvas/crons.js +16 -15
- package/dist/core/canvas/daemon-owner.js +10 -0
- package/dist/core/canvas/db.d.ts +2 -2
- package/dist/core/canvas/db.js +2 -18
- package/dist/core/canvas/deliveries.d.ts +104 -0
- package/dist/core/canvas/deliveries.js +354 -0
- package/dist/core/canvas/executions.d.ts +29 -0
- package/dist/core/canvas/executions.js +68 -0
- package/dist/core/canvas/history.js +35 -35
- package/dist/core/canvas/index.d.ts +3 -1
- package/dist/core/canvas/index.js +3 -1
- package/dist/core/canvas/migrations.js +20 -12
- package/dist/core/canvas/nav-model.js +1 -3
- package/dist/core/canvas/nav-render.d.ts +4 -4
- package/dist/core/canvas/nav-render.js +11 -21
- package/dist/core/canvas/node-sql-migration.d.ts +3 -0
- package/dist/core/canvas/node-sql-migration.js +261 -0
- package/dist/core/canvas/paths.d.ts +2 -16
- package/dist/core/canvas/paths.js +2 -31
- package/dist/core/canvas/recovery.d.ts +76 -0
- package/dist/core/canvas/recovery.js +588 -0
- package/dist/core/canvas/remote-canvas-source.js +4 -0
- package/dist/core/canvas/render-source.d.ts +16 -27
- package/dist/core/canvas/render-source.js +16 -45
- package/dist/core/canvas/sessions.d.ts +33 -0
- package/dist/core/canvas/sessions.js +78 -0
- package/dist/core/canvas/types.d.ts +33 -33
- package/dist/core/canvas/types.js +2 -5
- package/dist/core/canvas/waits.d.ts +18 -0
- package/dist/core/canvas/waits.js +70 -0
- package/dist/core/canvas/worktrees.d.ts +40 -0
- package/dist/core/canvas/worktrees.js +93 -0
- package/dist/core/config.d.ts +17 -12
- package/dist/core/config.js +106 -155
- package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
- package/dist/core/feed/feed.d.ts +0 -6
- package/dist/core/feed/feed.js +68 -56
- package/dist/core/feed/inbox.d.ts +7 -81
- package/dist/core/feed/inbox.js +46 -378
- package/dist/core/feed/messages.d.ts +58 -0
- package/dist/core/feed/messages.js +217 -0
- package/dist/core/fs-utils.d.ts +4 -0
- package/dist/core/fs-utils.js +21 -1
- package/dist/core/human/answer.d.ts +1 -1
- package/dist/core/human/answer.js +1 -1
- package/dist/core/human/claim.d.ts +1 -1
- package/dist/core/human/claim.js +2 -4
- package/dist/core/human/component-docs.js +1 -1
- package/dist/core/human/feedback-companion.js +3 -2
- package/dist/core/human/feedback.d.ts +5 -8
- package/dist/core/human/feedback.js +57 -85
- package/dist/core/human/page-authoring.d.ts +30 -0
- package/dist/core/human/page-authoring.js +19 -0
- package/dist/core/human/requests.d.ts +136 -0
- package/dist/core/human/requests.js +515 -0
- package/dist/core/human/root.d.ts +0 -7
- package/dist/core/human/root.js +0 -17
- package/dist/core/human/scan.d.ts +2 -7
- package/dist/core/human/scan.js +48 -132
- package/dist/core/human/summary.d.ts +1 -1
- package/dist/core/human/summary.js +1 -1
- package/dist/core/human/tickets.d.ts +23 -83
- package/dist/core/human/tickets.js +92 -243
- package/dist/core/human/types.d.ts +5 -1
- package/dist/core/installed-plugins.js +8 -3
- package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
- package/dist/core/locked-file.d.ts +6 -3
- package/dist/core/locked-file.js +36 -9
- package/dist/core/memory/extensions.js +2 -0
- package/dist/core/preview-registry.js +0 -1
- package/dist/core/profiles/__tests__/env-store.test.js +2 -0
- package/dist/core/profiles/default-binding.d.ts +0 -4
- package/dist/core/profiles/default-binding.js +12 -58
- package/dist/core/profiles/env-store.js +8 -3
- package/dist/core/profiles/manifest.d.ts +2 -37
- package/dist/core/profiles/manifest.js +35 -161
- package/dist/core/profiles/select.js +3 -0
- package/dist/core/review/__tests__/capture-origin.test.js +13 -2
- package/dist/core/review/__tests__/comments.test.js +28 -10
- package/dist/core/review/__tests__/remap.test.js +28 -10
- package/dist/core/review/__tests__/stage-identity.test.js +17 -7
- package/dist/core/review/birth.d.ts +1 -1
- package/dist/core/review/birth.js +34 -5
- package/dist/core/review/comments.js +1 -1
- package/dist/core/review/document.js +1 -1
- package/dist/core/review/project.d.ts +5 -1
- package/dist/core/review/project.js +14 -36
- package/dist/core/review/realize.d.ts +3 -6
- package/dist/core/review/realize.js +51 -168
- package/dist/core/review/remap.js +1 -1
- package/dist/core/review/stage.d.ts +3 -2
- package/dist/core/review/stage.js +2 -3
- package/dist/core/review/store.d.ts +3 -21
- package/dist/core/review/store.js +140 -186
- package/dist/core/review/types.d.ts +0 -1
- package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
- package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
- package/dist/core/runtime/broker/auth-reload.js +9 -10
- package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
- package/dist/core/runtime/broker/daemon-ops.js +42 -9
- package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
- package/dist/core/runtime/broker/extension-abort.js +11 -0
- package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
- package/dist/core/runtime/broker/fault-retry.js +142 -95
- package/dist/core/runtime/broker/inbox.d.ts +4 -32
- package/dist/core/runtime/broker/inbox.js +4 -92
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +21 -12
- package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
- package/dist/core/runtime/broker-persona-guidance.js +2 -13
- package/dist/core/runtime/broker.js +28 -22
- package/dist/core/runtime/close.d.ts +3 -9
- package/dist/core/runtime/close.js +16 -27
- package/dist/core/runtime/fault.d.ts +3 -93
- package/dist/core/runtime/fault.js +9 -419
- package/dist/core/runtime/kickoff.d.ts +3 -6
- package/dist/core/runtime/kickoff.js +12 -18
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/lifecycle.js +40 -7
- package/dist/core/runtime/placement.js +2 -2
- package/dist/core/runtime/recycle.js +8 -2
- package/dist/core/runtime/reset.js +22 -6
- package/dist/core/runtime/revive.js +226 -218
- package/dist/core/runtime/spawn.js +14 -3
- package/dist/core/scoped-state/db.d.ts +7 -0
- package/dist/core/scoped-state/db.js +92 -0
- package/dist/core/scoped-state/migrate.d.ts +11 -0
- package/dist/core/scoped-state/migrate.js +198 -0
- package/dist/core/scoped-state/paths.d.ts +9 -0
- package/dist/core/scoped-state/paths.js +27 -0
- package/dist/core/scoped-state/profiles.d.ts +12 -0
- package/dist/core/scoped-state/profiles.js +62 -0
- package/dist/core/scoped-state/providers.d.ts +16 -0
- package/dist/core/scoped-state/providers.js +8 -0
- package/dist/core/scoped-state/schema.d.ts +3 -0
- package/dist/core/scoped-state/schema.js +26 -0
- package/dist/core/scoped-state/settings.d.ts +28 -0
- package/dist/core/scoped-state/settings.js +82 -0
- package/dist/core/subscription-state.d.ts +29 -48
- package/dist/core/subscription-state.js +176 -712
- package/dist/core/worktree-close.js +4 -4
- package/dist/core/worktree-mutation-async.js +2 -2
- package/dist/core/worktree-sweep.js +11 -16
- package/dist/core/worktree.js +6 -12
- package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
- package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
- package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
- package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
- package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +39 -15
- package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
- package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
- package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
- package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
- package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
- package/dist/daemon/api/__tests__/model-config.test.js +8 -8
- package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
- package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
- package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
- package/dist/daemon/api/__tests__/profile-launch-gates.test.js +4 -1
- package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
- package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
- package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
- package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
- package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
- package/dist/daemon/api/__tests__/seam/profile-delete.test.js +27 -41
- package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
- package/dist/daemon/api/bridge.js +1 -1
- package/dist/daemon/api/handlers/bash-jobs.js +6 -7
- package/dist/daemon/api/handlers/broker-ops.js +47 -84
- package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
- package/dist/daemon/api/handlers/broker-recovery.js +251 -0
- package/dist/daemon/api/handlers/canvas.js +2 -44
- package/dist/daemon/api/handlers/daemon.js +41 -0
- package/dist/daemon/api/handlers/feedback-comments.js +2 -2
- package/dist/daemon/api/handlers/health.js +10 -5
- package/dist/daemon/api/handlers/human-requests.js +168 -188
- package/dist/daemon/api/handlers/human.js +3 -151
- package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
- package/dist/daemon/api/handlers/inbox-page.js +89 -0
- package/dist/daemon/api/handlers/inbox.js +21 -120
- package/dist/daemon/api/handlers/messages.d.ts +1 -0
- package/dist/daemon/api/handlers/messages.js +73 -15
- package/dist/daemon/api/handlers/node-outcomes.js +58 -13
- package/dist/daemon/api/handlers/nodes.js +7 -13
- package/dist/daemon/api/handlers/reviews.js +4 -29
- package/dist/daemon/api/map.d.ts +25 -3
- package/dist/daemon/api/map.js +84 -14
- package/dist/daemon/api/server.js +68 -9
- package/dist/daemon/control.d.ts +18 -0
- package/dist/daemon/cron/disposition.d.ts +2 -0
- package/dist/daemon/cron/disposition.js +8 -5
- package/dist/daemon/cron/settle.js +37 -13
- package/dist/daemon/cron/sinks.d.ts +18 -1
- package/dist/daemon/cron/sinks.js +47 -93
- package/dist/daemon/crtrd-cli.js +4 -19
- package/dist/daemon/crtrd.d.ts +2 -0
- package/dist/daemon/crtrd.js +206 -81
- package/dist/daemon/fleet.js +5 -6
- package/dist/daemon/human/finish.d.ts +12 -24
- package/dist/daemon/human/finish.js +88 -285
- package/dist/daemon/human/settle.d.ts +40 -0
- package/dist/daemon/human/settle.js +89 -0
- package/dist/daemon/human/sweep.d.ts +20 -5
- package/dist/daemon/human/sweep.js +56 -60
- package/dist/daemon/manage.d.ts +21 -16
- package/dist/daemon/manage.js +85 -51
- package/dist/daemon/messaging/node-message.d.ts +1 -1
- package/dist/daemon/messaging/node-message.js +9 -12
- package/dist/daemon/park-activity.js +3 -3
- package/dist/daemon/reconcilers/bash-deadline.js +6 -7
- package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
- package/dist/daemon/reconcilers/broker-supervision.js +41 -19
- package/dist/daemon/reconcilers/controller-death.js +6 -18
- package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
- package/dist/daemon/reconcilers/delivery-lane.js +130 -0
- package/dist/daemon/reconcilers/live-obligation.js +3 -3
- package/dist/daemon/reconcilers/node-deadline.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
- package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
- package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
- package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
- package/dist/daemon/review/companion.js +5 -11
- package/dist/daemon/review/deliver.d.ts +1 -5
- package/dist/daemon/review/deliver.js +3 -52
- package/dist/daemon/review/finish.d.ts +2 -27
- package/dist/daemon/review/finish.js +79 -263
- package/dist/daemon/review/sweep.d.ts +3 -10
- package/dist/daemon/review/sweep.js +13 -131
- package/dist/daemon/startup-policy.d.ts +0 -5
- package/dist/daemon/startup-policy.js +0 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
- package/dist/migrations/__tests__/activation.test.js +9 -6
- package/dist/migrations/activation.d.ts +3 -2
- package/dist/migrations/activation.js +2 -4
- package/dist/migrations/corpus.d.ts +2 -5
- package/dist/migrations/corpus.js +9 -20
- package/dist/migrations/runner.d.ts +3 -1
- package/dist/migrations/runner.js +6 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
- package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
- package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
- package/dist/pi-extensions/canvas-passive-context.js +85 -37
- package/dist/pi-extensions/canvas-stophook.js +47 -31
- package/dist/shared/env.d.ts +1 -0
- package/dist/shared/env.js +3 -0
- package/package.json +1 -1
- package/runtime.lock.json +5 -5
- package/dist/clients/attach/__tests__/page-block.test.js +0 -54
- package/dist/clients/inbox/review-visibility.d.ts +0 -5
- package/dist/clients/inbox/review-visibility.js +0 -19
- package/dist/commands/canvas-rebuild-index.d.ts +0 -2
- package/dist/commands/canvas-rebuild-index.js +0 -46
- package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
- package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
- package/dist/core/__tests__/human-deliver.test.js +0 -256
- package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
- package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
- package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
- package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
- package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
- package/dist/core/__tests__/phase4-review-store.test.js +0 -144
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
- package/dist/core/canvas/human-deliveries.d.ts +0 -39
- package/dist/core/canvas/human-deliveries.js +0 -75
- package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
- package/dist/core/canvas/node-outcome-deliveries.js +0 -159
- package/dist/core/feed/passive.d.ts +0 -3
- package/dist/core/feed/passive.js +0 -53
- package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
- package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
- package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
- package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
- package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
- package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
- package/dist/core/human/__tests__/page-tickets.test.js +0 -161
- package/dist/core/review/ticket-filter.d.ts +0 -17
- package/dist/core/review/ticket-filter.js +0 -21
- package/dist/core/runtime/broker/passive.d.ts +0 -17
- package/dist/core/runtime/broker/passive.js +0 -44
- package/dist/core/runtime/busy.d.ts +0 -46
- package/dist/core/runtime/busy.js +0 -150
- package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
- package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
- package/dist/daemon/human/deliver-action.d.ts +0 -3
- package/dist/daemon/human/deliver-action.js +0 -9
- package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
- package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
- package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
- package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
- package/dist/daemon/startup-block-marker.d.ts +0 -25
- package/dist/daemon/startup-block-marker.js +0 -99
- /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
- /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
- /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// message) — falling back to a short note when it never reported.
|
|
18
18
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
19
19
|
import { join } from 'node:path';
|
|
20
|
-
import { getNode, setPresence, setFocusOccupant, fullName,
|
|
20
|
+
import { abortExecution, beginExecution, getNode, setPresence, setFocusOccupant, fullName, recordExecutionPid, recordLaunch } from '../canvas/index.js';
|
|
21
21
|
import { reportsDir } from '../canvas/paths.js';
|
|
22
22
|
import { pushFinal } from '../feed/feed.js';
|
|
23
23
|
import { spawnNode, rootOfSpine } from './nodes.js';
|
|
@@ -25,6 +25,7 @@ import { buildLaunchSpecAsync, buildPiArgv } from './launch.js';
|
|
|
25
25
|
import { focusOf, respawnPaneSync, setPaneOption } from './placement.js';
|
|
26
26
|
import { waitForBrokerViewSocket, viewerSplitEnv } from './placement-tmux.js';
|
|
27
27
|
import { headlessBrokerHost } from './host.js';
|
|
28
|
+
import { boundFleet } from './fleet.js';
|
|
28
29
|
import { transition } from './lifecycle.js';
|
|
29
30
|
import { ensureDaemon } from '../../daemon/manage.js';
|
|
30
31
|
import { invokeBirthLifecycleHooks } from './spawn.js';
|
|
@@ -137,17 +138,22 @@ export async function recycleNode(nodeId, callerPane) {
|
|
|
137
138
|
* broker. This direct birth path writes the same launch/pid coordinates as
|
|
138
139
|
* reviveNode, and a failed readiness probe terminalizes and tears it down. */
|
|
139
140
|
async function launchRecycledBroker(fresh, inv) {
|
|
141
|
+
const executionId = beginExecution(fresh.node_id, { daemonEpoch: boundFleet().epoch() });
|
|
142
|
+
inv.env = { ...inv.env, CRTR_EXECUTION_ID: executionId };
|
|
140
143
|
let placed;
|
|
141
144
|
try {
|
|
142
145
|
placed = headlessBrokerHost.launch(fresh.node_id, inv, { cwd: fresh.cwd, name: fullName(fresh), resuming: false });
|
|
143
146
|
if (placed.pid === null)
|
|
144
147
|
throw new Error('broker host returned no pid');
|
|
145
148
|
recordLaunch(fresh.node_id, new Date().toISOString());
|
|
146
|
-
|
|
149
|
+
if (recordExecutionPid(executionId, fresh.node_id, placed.pid) === 'shutdown') {
|
|
150
|
+
throw new Error('broker execution was superseded');
|
|
151
|
+
}
|
|
147
152
|
if (await waitForBrokerViewSocket(fresh.node_id, placed.exited))
|
|
148
153
|
return true;
|
|
149
154
|
}
|
|
150
155
|
catch {
|
|
156
|
+
abortExecution(executionId, { reason: 'launch_failed' });
|
|
151
157
|
// The replacement never became usable; the finished root's broker remains
|
|
152
158
|
// intact and this half-born row cannot remain active without a fleet handle.
|
|
153
159
|
}
|
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
// reserved for finish).
|
|
21
21
|
//
|
|
22
22
|
// Best-effort throughout: a tmux/fs failure on one node never aborts the reap.
|
|
23
|
-
import { getNode,
|
|
23
|
+
import { getNode, beginExecution, recordExecutionPid, abortExecution, bindSession, recordLaunch, fullName, liveExecutionForNode, closeFocusRow, view, } from '../canvas/index.js';
|
|
24
24
|
import { transition } from './lifecycle.js';
|
|
25
25
|
import { headlessBrokerHost, requestBrokerTeardown } from './host.js';
|
|
26
|
+
import { boundFleet } from './fleet.js';
|
|
26
27
|
import { tearDownNode, focusOf, registerViewerFocus, respawnPaneSync, windowOfPane, renameWindow, } from './placement.js';
|
|
27
28
|
import { viewerSplitEnv, waitForBrokerViewSocket } from './placement-tmux.js';
|
|
28
29
|
import { buildLaunchSpecAsync, buildPiArgv } from './launch.js';
|
|
@@ -116,6 +117,8 @@ export async function relaunchRoot(oldId, deps = {}) {
|
|
|
116
117
|
...inv.env,
|
|
117
118
|
CRTR_SUBTREE: rootOfSpine(newMeta.node_id),
|
|
118
119
|
};
|
|
120
|
+
const executionId = beginExecution(newMeta.node_id, { daemonEpoch: boundFleet().epoch() });
|
|
121
|
+
inv.env = { ...inv.env, CRTR_EXECUTION_ID: executionId };
|
|
119
122
|
let placed;
|
|
120
123
|
try {
|
|
121
124
|
placed = launchBroker(newMeta.node_id, inv, {
|
|
@@ -125,10 +128,12 @@ export async function relaunchRoot(oldId, deps = {}) {
|
|
|
125
128
|
});
|
|
126
129
|
}
|
|
127
130
|
catch (error) {
|
|
131
|
+
abortExecution(executionId, { reason: 'launch_failed' });
|
|
128
132
|
transition(newMeta.node_id, 'crash');
|
|
129
133
|
throw error;
|
|
130
134
|
}
|
|
131
135
|
if (placed.pid == null) {
|
|
136
|
+
abortExecution(executionId, { reason: 'launch_failed' });
|
|
132
137
|
// The new broker never started — crash the half-born node and BAIL. The old
|
|
133
138
|
// root is still live and untouched (nothing below this point has run).
|
|
134
139
|
transition(newMeta.node_id, 'crash');
|
|
@@ -138,7 +143,15 @@ export async function relaunchRoot(oldId, deps = {}) {
|
|
|
138
143
|
// real exit, while the row gets the matching launch and pid coordinates before
|
|
139
144
|
// readiness can observe an early exit.
|
|
140
145
|
recordLaunch(newMeta.node_id, new Date().toISOString());
|
|
141
|
-
|
|
146
|
+
if (recordExecutionPid(executionId, newMeta.node_id, placed.pid) === 'shutdown') {
|
|
147
|
+
abortExecution(executionId, { reason: 'superseded' });
|
|
148
|
+
try {
|
|
149
|
+
teardownBroker(newMeta.node_id);
|
|
150
|
+
}
|
|
151
|
+
catch { /* best-effort */ }
|
|
152
|
+
transition(newMeta.node_id, 'crash');
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
142
155
|
let ready = false;
|
|
143
156
|
try {
|
|
144
157
|
ready = await waitForViewSocket(newMeta.node_id, placed.exited);
|
|
@@ -191,10 +204,13 @@ export function resetRoot(nodeId, newSessionId, newSessionFile) {
|
|
|
191
204
|
if (meta === null || meta.parent == null)
|
|
192
205
|
return empty; // unknown or a root
|
|
193
206
|
if (newSessionId !== undefined) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
207
|
+
const execution = liveExecutionForNode(nodeId);
|
|
208
|
+
if (execution !== null)
|
|
209
|
+
bindSession(nodeId, {
|
|
210
|
+
executionId: execution.executionId,
|
|
211
|
+
sessionId: newSessionId,
|
|
212
|
+
transcriptPath: newSessionFile ?? null,
|
|
213
|
+
});
|
|
198
214
|
}
|
|
199
215
|
return empty;
|
|
200
216
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
// reviveNode remains the ONLY sanctioned launcher of the node engine.
|
|
25
25
|
import { existsSync, readFileSync } from 'node:fs';
|
|
26
26
|
import { isAbsolute } from 'node:path';
|
|
27
|
-
import { findNodeBySessionFile, getNode, updateNode,
|
|
27
|
+
import { findNodeBySessionFile, getNode, updateNode, beginExecution, recordExecutionPid, abortExecution, endLiveExecution, unbindSession, recordLaunch, setFrozen, clearFrozen, fullName, subscribersOf, } from '../canvas/index.js';
|
|
28
28
|
import { emitEvent } from '../events/emit.js';
|
|
29
29
|
import { cancelCronsOnWake } from '../canvas/crons.js';
|
|
30
30
|
import { isParked, transition } from './lifecycle.js';
|
|
@@ -36,8 +36,7 @@ import { readRoadmap } from './roadmap.js';
|
|
|
36
36
|
import { encodeKickoffOrigin, KICKOFF_ORIGIN_ENV } from './stamp/protocol.js';
|
|
37
37
|
import { headlessBrokerHost } from './host.js';
|
|
38
38
|
import { boundFleet, brokerCapReached, brokerThresholdsForDaemon } from './fleet.js';
|
|
39
|
-
import {
|
|
40
|
-
import { clearTransportPoisonVerdict } from './busy.js';
|
|
39
|
+
import { beginBootFaultAttempt, clearFault } from '../canvas/recovery.js';
|
|
41
40
|
import { clearContextExposureState, initializeForkContextExposure, } from '../substrate/injected-store.js';
|
|
42
41
|
import { rootOfSpine } from './nodes.js';
|
|
43
42
|
import { isReviewCompanionBound } from '../review/companion.js';
|
|
@@ -228,7 +227,7 @@ async function decideRevive(nodeId, opts, wakeParked) {
|
|
|
228
227
|
// every status surface. Clearing it is what makes `isFrozenRow` true and
|
|
229
228
|
// what tells the tick this row has never launched OR has already been
|
|
230
229
|
// written off, so the freeze lane owes it a launch rather than a verdict.
|
|
231
|
-
|
|
230
|
+
endLiveExecution(nodeId, { reason: 'frozen' });
|
|
232
231
|
emitEvent({
|
|
233
232
|
level: 'info',
|
|
234
233
|
event: 'broker.frozen',
|
|
@@ -289,7 +288,7 @@ async function launchRevive(nodeId, meta, opts) {
|
|
|
289
288
|
// immutable fork instead of resuming an inherited transcript.
|
|
290
289
|
const reviewCompanionUnbound = meta.review_binding != null && !isReviewCompanionBound(nodeId);
|
|
291
290
|
if (reviewCompanionUnbound && (meta.pi_session_id != null || meta.pi_session_file != null)) {
|
|
292
|
-
|
|
291
|
+
unbindSession(nodeId);
|
|
293
292
|
meta.pi_session_id = null;
|
|
294
293
|
meta.pi_session_file = null;
|
|
295
294
|
}
|
|
@@ -312,225 +311,234 @@ async function launchRevive(nodeId, meta, opts) {
|
|
|
312
311
|
next: `Restore ${forkSourceFile}, or update the node's fork source, then retry \`crtr node lifecycle revive ${nodeId}\`.`,
|
|
313
312
|
});
|
|
314
313
|
}
|
|
315
|
-
|
|
316
|
-
// footer stay in sync. Mutate the in-memory meta too so buildPiArgv below
|
|
317
|
-
// launches with the incremented count.
|
|
318
|
-
meta.cycles = (meta.cycles ?? 0) + 1;
|
|
319
|
-
updateNode(nodeId, { cycles: meta.cycles });
|
|
320
|
-
// Ordinary nodes choose resume/cycle/fresh from their recorded session.
|
|
321
|
-
// Persisted pending-refresh state (durable across a pre-session_start
|
|
322
|
-
// crash-retry) outranks a caller's strict-resume request. A parked node now
|
|
323
|
-
// resumes its transcript too: its isolated parking turn never entered the
|
|
324
|
-
// main session, so wake mail continues one intact conversation without a
|
|
325
|
-
// fresh-cycle handoff. resumeArgs then selects only an existing absolute
|
|
326
|
-
// session path: strict resume continues its leaf, while refresh/cycle mode
|
|
327
|
-
// roots a new one.
|
|
328
|
-
const refreshPending = meta.intent === 'refresh' || meta.cycle_pending === true;
|
|
329
|
-
const effectiveResume = refreshPending ? false : opts.resume;
|
|
330
|
-
const resume = forkBirthPending ? {} : resumableSessionArgs(nodeId, meta, effectiveResume);
|
|
331
|
-
// hasSessionPath: there is a real `.jsonl` to hand pi via `--session`, whether
|
|
332
|
-
// as a true resume OR a cycling refresh-yield (resumeArgs sets `newCycle` in
|
|
333
|
-
// the latter case). cycling and resuming are mutually exclusive; `resuming`
|
|
334
|
-
// keeps the STRICT true-resume-only meaning `ReviveResult.resumed` and the
|
|
335
|
-
// launch-placement hint below have always carried.
|
|
336
|
-
const hasSessionPath = resume.resumeSessionPath !== undefined;
|
|
337
|
-
const cycling = hasSessionPath && resume.newCycle === true;
|
|
338
|
-
const resuming = hasSessionPath && !cycling;
|
|
339
|
-
// Never conversed and nothing on disk to rebuild from — relaunch as a birth
|
|
340
|
-
// (see isUnstartedBirth). A clock-driven wake must keep its <crtr-wake>
|
|
341
|
-
// kickoff — the schedule IS the instruction to take a turn, so it never
|
|
342
|
-
// degrades to a birth replay. This one decision selects the kickoff below
|
|
343
|
-
// and is what the node:start hook payload reports as `isBirth`: a hook is
|
|
344
|
-
// told "birth" exactly when the node boots as one.
|
|
345
|
-
const birthReplay = !hasSessionPath
|
|
346
|
-
&& (opts.wakeReason === undefined || opts.wakeReason === 'runtime-restart-abort')
|
|
347
|
-
&& isUnstartedBirth(nodeId);
|
|
348
|
-
const isBirth = forkBirthPending || birthReplay;
|
|
349
|
-
// A cycling launch is provisional until session_start confirms it. Once
|
|
350
|
-
// true, this marker is monotonic across failed retries; only that confirmed
|
|
351
|
-
// boot may clear it.
|
|
352
|
-
const cyclePending = meta.cycle_pending === true || cycling;
|
|
353
|
-
updateNode(nodeId, { cycle_pending: cyclePending });
|
|
354
|
-
meta.cycle_pending = cyclePending;
|
|
355
|
-
// A truly fresh launch — no `.jsonl` to resume OR cycle from —
|
|
356
|
-
// starts a NEW pi session, so the old session identity is dead. Clear it from meta NOW,
|
|
357
|
-
// before launch, so the recorded state matches what we're about to do. This
|
|
358
|
-
// is load-bearing for exit-policy classification (classifyDeadNode row 8):
|
|
359
|
-
// if this fresh broker dies BEFORE session_start (recording no new session),
|
|
360
|
-
// the exit-policy job reads pi_session_id=NULL → boot-failure, the correct
|
|
361
|
-
// terminal outcome (transition('crash') + surfaceBootFailure) — a leftover
|
|
362
|
-
// stale id would instead be misread as a resumable session and retried. A
|
|
363
|
-
// cycling refresh keeps BOTH ids — the `.jsonl` and its recorded session id
|
|
364
|
-
// are exactly what's being reused — so a cycling broker that dies
|
|
365
|
-
// pre-session_start classifies as a retryable respawn, same class as a
|
|
366
|
-
// resume revive.
|
|
367
|
-
if (!forkBirthPending && !hasSessionPath && (meta.pi_session_id != null || meta.pi_session_file != null)) {
|
|
368
|
-
updateNode(nodeId, { pi_session_id: null, pi_session_file: null });
|
|
369
|
-
meta.pi_session_id = null;
|
|
370
|
-
meta.pi_session_file = null;
|
|
371
|
-
}
|
|
372
|
-
// A true resume needs no kickoff — the saved conversation just continues. A
|
|
373
|
-
// fresh revive OR a cycling refresh-yield both get one so the (re-rooted)
|
|
374
|
-
// window re-reads its roadmap and continues: drainBearings captures the
|
|
375
|
-
// yield note and consumes the feed/persona acknowledgements; the builder is
|
|
376
|
-
// then pure. `resume` carries `resumeSessionPath`/`newCycle` through
|
|
377
|
-
// unchanged (empty for true-fresh, both set for cycling) so buildPiArgv emits
|
|
378
|
-
// the right argv either way.
|
|
379
|
-
let inv;
|
|
380
|
-
// The provenance of whatever kickoff this branch composes, handed to the
|
|
381
|
-
// broker process through inv.env below. A promptless launch announces
|
|
382
|
-
// nothing — there is no message to stamp.
|
|
383
|
-
let kickoffOrigin = null;
|
|
384
|
-
const birthOrigin = () => ({
|
|
385
|
-
type: 'runtime',
|
|
386
|
-
kind: 'kickoff',
|
|
387
|
-
...(meta.spawned_by != null && meta.spawned_by !== '' ? { spawnedBy: meta.spawned_by } : {}),
|
|
388
|
-
});
|
|
389
|
-
if (forkBirthPending) {
|
|
390
|
-
const goal = readGoal(nodeId);
|
|
391
|
-
inv = buildPiArgv(meta, {
|
|
392
|
-
forkFrom: forkSourceFile,
|
|
393
|
-
...(goal != null && goal !== '' ? { prompt: goal } : {}),
|
|
394
|
-
});
|
|
395
|
-
if (goal != null && goal !== '')
|
|
396
|
-
kickoffOrigin = birthOrigin();
|
|
397
|
-
initializeForkContextExposure(nodeId, findNodeBySessionFile(forkSourceFile)?.node_id ?? null);
|
|
398
|
-
}
|
|
399
|
-
else if (resuming) {
|
|
400
|
-
inv = buildPiArgv(meta, {
|
|
401
|
-
...resume,
|
|
402
|
-
...(opts.wakeReason === 'runtime-restart-abort' ? { prompt: RUNTIME_RESTART_CONTINUATION } : {}),
|
|
403
|
-
});
|
|
404
|
-
if (opts.wakeReason === 'runtime-restart-abort') {
|
|
405
|
-
kickoffOrigin = { type: 'runtime', kind: 'restart-continuation' };
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
else if (birthReplay) {
|
|
409
|
-
// A node with a spawn mandate replays it; a bare interactive node boots
|
|
410
|
-
// promptless and takes no turn at all.
|
|
411
|
-
const goal = readGoal(nodeId)?.trim();
|
|
412
|
-
inv = buildPiArgv(meta, goal !== undefined && goal !== '' ? { prompt: goal } : {});
|
|
413
|
-
if (goal !== undefined && goal !== '')
|
|
414
|
-
kickoffOrigin = birthOrigin();
|
|
415
|
-
clearContextExposureState(nodeId);
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
// Fresh or cycling: drain the one-shot bearings, then build the kickoff.
|
|
419
|
-
// When a scheduled bare self-alarm drove this revive, opts.wakeReason
|
|
420
|
-
// carries the wake provenance so the kickoff leads with a <crtr-wake> block
|
|
421
|
-
// ("a timer woke you"); every other reviveNode caller passes nothing → no
|
|
422
|
-
// block.
|
|
423
|
-
const bearings = drainBearings(meta);
|
|
424
|
-
inv = buildPiArgv(meta, {
|
|
425
|
-
...resume,
|
|
426
|
-
prompt: buildReviveKickoff(meta, bearings, opts.wakeReason === 'runtime-restart-abort' ? undefined : opts.wakeReason),
|
|
427
|
-
});
|
|
428
|
-
kickoffOrigin = opts.wakeReason !== undefined && opts.wakeReason !== 'runtime-restart-abort'
|
|
429
|
-
? { type: 'runtime', kind: 'wake', wake: opts.wakeReason }
|
|
430
|
-
: { type: 'runtime', kind: 'revive', cycle: meta.cycles ?? 0 };
|
|
431
|
-
clearContextExposureState(nodeId);
|
|
432
|
-
}
|
|
433
|
-
// Every lifecycle launch emits the authoritative spine root rather than
|
|
434
|
-
// inheriting whichever node happened to invoke reviveNode.
|
|
435
|
-
inv.env = { ...inv.env, CRTR_SUBTREE: rootOfSpine(nodeId) };
|
|
436
|
-
if (kickoffOrigin !== null)
|
|
437
|
-
inv.env[KICKOFF_ORIGIN_ENV] = encodeKickoffOrigin(kickoffOrigin);
|
|
438
|
-
// The broker host launches the detached engine. reviveNode keeps
|
|
439
|
-
// transition+clearPid around it (the crash-safety ordering, unchanged). It
|
|
440
|
-
// opens NO viewer — engine-only; existing viewers reconnect, and a viewer-less
|
|
441
|
-
// node is brought on screen by the next `focus`.
|
|
442
|
-
transition(nodeId, meta.intent === 'refresh' ? 'refresh-launch' : 'revive');
|
|
443
|
-
// Clear any active fault the instant this node is (re)launched — don't wait
|
|
444
|
-
// for the fresh agent_start. A revive (auto or on-demand) is the recovery
|
|
445
|
-
// this marker advertised. Snapshot the boot-fault attempt in the SAME
|
|
446
|
-
// breath (see beginBootFaultAttempt): its own verification, not this call's
|
|
447
|
-
// unchecked result, is what scopes a later diagnostic read to this attempt.
|
|
448
|
-
clearFault(nodeId, { preserveProviderRetryEpisode: true });
|
|
449
|
-
const bootFault = beginBootFaultAttempt(nodeId);
|
|
450
|
-
// A cron armed --cancel-on-wake belongs to the dormancy being left, so a WAKE
|
|
451
|
-
// of its anchor deletes it — an inbox delivery, an attach, an explicit
|
|
452
|
-
// revive. A recovery relaunch is not a wake: the broker instance died or was
|
|
453
|
-
// torn down and the daemon is putting the same waiting node back on its feet,
|
|
454
|
-
// with nothing external having arrived. The wait the deadline bounds is still
|
|
455
|
-
// open, so the deadline survives — the same rule lifecycle.ts applies to the
|
|
456
|
-
// `crash` transition (a deadline MUST survive instance death).
|
|
457
|
-
if (opts.recovery !== true)
|
|
458
|
-
cancelCronsOnWake(nodeId);
|
|
459
|
-
ensureNodeDirs(nodeId);
|
|
460
|
-
clearTransportPoisonVerdict(nodeId);
|
|
461
|
-
const lifecycleHooks = discoverLifecycleHookRegistry(meta.cwd, meta.profile_id).plans.get('node:start') ?? [];
|
|
462
|
-
await invokeLifecycleHooks(lifecycleHooks, {
|
|
463
|
-
node: {
|
|
464
|
-
id: nodeId,
|
|
465
|
-
name: meta.name,
|
|
466
|
-
kind: meta.kind,
|
|
467
|
-
mode: meta.mode,
|
|
468
|
-
lifecycle: meta.lifecycle,
|
|
469
|
-
cwd: meta.cwd,
|
|
470
|
-
nodeDir: nodeDir(nodeId),
|
|
471
|
-
contextDir: contextDir(nodeId),
|
|
472
|
-
jobDir: jobDir(nodeId),
|
|
473
|
-
reportsDir: reportsDir(nodeId),
|
|
474
|
-
},
|
|
475
|
-
runtime: {
|
|
476
|
-
isBirth,
|
|
477
|
-
canvasHome: crtrHome(),
|
|
478
|
-
profile: meta.profile_id ?? null,
|
|
479
|
-
},
|
|
480
|
-
});
|
|
481
|
-
let launched;
|
|
314
|
+
const executionId = beginExecution(nodeId, { daemonEpoch: boundFleet().epoch() });
|
|
482
315
|
try {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
316
|
+
// Every (re)launch is a new cycle — bump the counter so CRTR_CYCLES and the
|
|
317
|
+
// footer stay in sync. Mutate the in-memory meta too so buildPiArgv below
|
|
318
|
+
// launches with the incremented count.
|
|
319
|
+
meta.cycles = (meta.cycles ?? 0) + 1;
|
|
320
|
+
updateNode(nodeId, { cycles: meta.cycles });
|
|
321
|
+
// Ordinary nodes choose resume/cycle/fresh from their recorded session.
|
|
322
|
+
// Persisted pending-refresh state (durable across a pre-session_start
|
|
323
|
+
// crash-retry) outranks a caller's strict-resume request. A parked node now
|
|
324
|
+
// resumes its transcript too: its isolated parking turn never entered the
|
|
325
|
+
// main session, so wake mail continues one intact conversation without a
|
|
326
|
+
// fresh-cycle handoff. resumeArgs then selects only an existing absolute
|
|
327
|
+
// session path: strict resume continues its leaf, while refresh/cycle mode
|
|
328
|
+
// roots a new one.
|
|
329
|
+
const refreshPending = meta.intent === 'refresh' || meta.cycle_pending === true;
|
|
330
|
+
const effectiveResume = refreshPending ? false : opts.resume;
|
|
331
|
+
const resume = forkBirthPending ? {} : resumableSessionArgs(nodeId, meta, effectiveResume);
|
|
332
|
+
// hasSessionPath: there is a real `.jsonl` to hand pi via `--session`, whether
|
|
333
|
+
// as a true resume OR a cycling refresh-yield (resumeArgs sets `newCycle` in
|
|
334
|
+
// the latter case). cycling and resuming are mutually exclusive; `resuming`
|
|
335
|
+
// keeps the STRICT true-resume-only meaning `ReviveResult.resumed` and the
|
|
336
|
+
// launch-placement hint below have always carried.
|
|
337
|
+
const hasSessionPath = resume.resumeSessionPath !== undefined;
|
|
338
|
+
const cycling = hasSessionPath && resume.newCycle === true;
|
|
339
|
+
const resuming = hasSessionPath && !cycling;
|
|
340
|
+
// Never conversed and nothing on disk to rebuild from — relaunch as a birth
|
|
341
|
+
// (see isUnstartedBirth). A clock-driven wake must keep its <crtr-wake>
|
|
342
|
+
// kickoff — the schedule IS the instruction to take a turn, so it never
|
|
343
|
+
// degrades to a birth replay. This one decision selects the kickoff below
|
|
344
|
+
// and is what the node:start hook payload reports as `isBirth`: a hook is
|
|
345
|
+
// told "birth" exactly when the node boots as one.
|
|
346
|
+
const birthReplay = !hasSessionPath
|
|
347
|
+
&& (opts.wakeReason === undefined || opts.wakeReason === 'runtime-restart-abort')
|
|
348
|
+
&& isUnstartedBirth(nodeId);
|
|
349
|
+
const isBirth = forkBirthPending || birthReplay;
|
|
350
|
+
// A cycling launch is provisional until session_start confirms it. Once
|
|
351
|
+
// true, this marker is monotonic across failed retries; only that confirmed
|
|
352
|
+
// boot may clear it.
|
|
353
|
+
const cyclePending = meta.cycle_pending === true || cycling;
|
|
354
|
+
updateNode(nodeId, { cycle_pending: cyclePending });
|
|
355
|
+
meta.cycle_pending = cyclePending;
|
|
356
|
+
// A truly fresh launch — no `.jsonl` to resume OR cycle from —
|
|
357
|
+
// starts a NEW pi session, so the old session identity is dead. Clear it from meta NOW,
|
|
358
|
+
// before launch, so the recorded state matches what we're about to do. This
|
|
359
|
+
// is load-bearing for exit-policy classification (classifyDeadNode row 8):
|
|
360
|
+
// if this fresh broker dies BEFORE session_start (recording no new session),
|
|
361
|
+
// the exit-policy job reads pi_session_id=NULL → boot-failure, the correct
|
|
362
|
+
// terminal outcome (transition('crash') + surfaceBootFailure) — a leftover
|
|
363
|
+
// stale id would instead be misread as a resumable session and retried. A
|
|
364
|
+
// cycling refresh keeps BOTH ids — the `.jsonl` and its recorded session id
|
|
365
|
+
// are exactly what's being reused — so a cycling broker that dies
|
|
366
|
+
// pre-session_start classifies as a retryable respawn, same class as a
|
|
367
|
+
// resume revive.
|
|
368
|
+
if (!forkBirthPending && !hasSessionPath && (meta.pi_session_id != null || meta.pi_session_file != null)) {
|
|
369
|
+
unbindSession(nodeId);
|
|
370
|
+
meta.pi_session_id = null;
|
|
371
|
+
meta.pi_session_file = null;
|
|
372
|
+
}
|
|
373
|
+
// A true resume needs no kickoff — the saved conversation just continues. A
|
|
374
|
+
// fresh revive OR a cycling refresh-yield both get one so the (re-rooted)
|
|
375
|
+
// window re-reads its roadmap and continues: drainBearings captures the
|
|
376
|
+
// yield note and consumes the feed/persona acknowledgements; the builder is
|
|
377
|
+
// then pure. `resume` carries `resumeSessionPath`/`newCycle` through
|
|
378
|
+
// unchanged (empty for true-fresh, both set for cycling) so buildPiArgv emits
|
|
379
|
+
// the right argv either way.
|
|
380
|
+
let inv;
|
|
381
|
+
// The provenance of whatever kickoff this branch composes, handed to the
|
|
382
|
+
// broker process through inv.env below. A promptless launch announces
|
|
383
|
+
// nothing — there is no message to stamp.
|
|
384
|
+
let kickoffOrigin = null;
|
|
385
|
+
const birthOrigin = () => ({
|
|
386
|
+
type: 'runtime',
|
|
387
|
+
kind: 'kickoff',
|
|
388
|
+
...(meta.spawned_by != null && meta.spawned_by !== '' ? { spawnedBy: meta.spawned_by } : {}),
|
|
487
389
|
});
|
|
488
|
-
if (
|
|
489
|
-
|
|
390
|
+
if (forkBirthPending) {
|
|
391
|
+
const goal = readGoal(nodeId);
|
|
392
|
+
inv = buildPiArgv(meta, {
|
|
393
|
+
forkFrom: forkSourceFile,
|
|
394
|
+
...(goal != null && goal !== '' ? { prompt: goal } : {}),
|
|
395
|
+
});
|
|
396
|
+
if (goal != null && goal !== '')
|
|
397
|
+
kickoffOrigin = birthOrigin();
|
|
398
|
+
initializeForkContextExposure(nodeId, findNodeBySessionFile(forkSourceFile)?.node_id ?? null);
|
|
490
399
|
}
|
|
491
|
-
|
|
492
|
-
|
|
400
|
+
else if (resuming) {
|
|
401
|
+
inv = buildPiArgv(meta, {
|
|
402
|
+
...resume,
|
|
403
|
+
...(opts.wakeReason === 'runtime-restart-abort' ? { prompt: RUNTIME_RESTART_CONTINUATION } : {}),
|
|
404
|
+
});
|
|
405
|
+
if (opts.wakeReason === 'runtime-restart-abort') {
|
|
406
|
+
kickoffOrigin = { type: 'runtime', kind: 'restart-continuation' };
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
else if (birthReplay) {
|
|
410
|
+
// A node with a spawn mandate replays it; a bare interactive node boots
|
|
411
|
+
// promptless and takes no turn at all.
|
|
412
|
+
const goal = readGoal(nodeId)?.trim();
|
|
413
|
+
inv = buildPiArgv(meta, goal !== undefined && goal !== '' ? { prompt: goal } : {});
|
|
414
|
+
if (goal !== undefined && goal !== '')
|
|
415
|
+
kickoffOrigin = birthOrigin();
|
|
416
|
+
clearContextExposureState(nodeId);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
// Fresh or cycling: drain the one-shot bearings, then build the kickoff.
|
|
420
|
+
// When a scheduled bare self-alarm drove this revive, opts.wakeReason
|
|
421
|
+
// carries the wake provenance so the kickoff leads with a <crtr-wake> block
|
|
422
|
+
// ("a timer woke you"); every other reviveNode caller passes nothing → no
|
|
423
|
+
// block.
|
|
424
|
+
const bearings = drainBearings(meta, executionId);
|
|
425
|
+
inv = buildPiArgv(meta, {
|
|
426
|
+
...resume,
|
|
427
|
+
prompt: buildReviveKickoff(meta, bearings, opts.wakeReason === 'runtime-restart-abort' ? undefined : opts.wakeReason),
|
|
428
|
+
});
|
|
429
|
+
kickoffOrigin = opts.wakeReason !== undefined && opts.wakeReason !== 'runtime-restart-abort'
|
|
430
|
+
? { type: 'runtime', kind: 'wake', wake: opts.wakeReason }
|
|
431
|
+
: { type: 'runtime', kind: 'revive', cycle: meta.cycles ?? 0 };
|
|
432
|
+
clearContextExposureState(nodeId);
|
|
433
|
+
}
|
|
434
|
+
// Every lifecycle launch emits the authoritative spine root rather than
|
|
435
|
+
// inheriting whichever node happened to invoke reviveNode.
|
|
436
|
+
inv.env = { ...inv.env, CRTR_SUBTREE: rootOfSpine(nodeId) };
|
|
437
|
+
if (kickoffOrigin !== null)
|
|
438
|
+
inv.env[KICKOFF_ORIGIN_ENV] = encodeKickoffOrigin(kickoffOrigin);
|
|
439
|
+
// The broker host launches the detached engine. reviveNode keeps
|
|
440
|
+
// transition+clearPid around it (the crash-safety ordering, unchanged). It
|
|
441
|
+
// opens NO viewer — engine-only; existing viewers reconnect, and a viewer-less
|
|
442
|
+
// node is brought on screen by the next `focus`.
|
|
443
|
+
transition(nodeId, meta.intent === 'refresh' ? 'refresh-launch' : 'revive');
|
|
444
|
+
// Clear any active fault the instant this node is (re)launched — don't wait
|
|
445
|
+
// for the fresh agent_start. A revive (auto or on-demand) is the recovery
|
|
446
|
+
// this marker advertised. Snapshot the boot-fault attempt in the SAME
|
|
447
|
+
// breath (see beginBootFaultAttempt): its own verification, not this call's
|
|
448
|
+
// unchecked result, is what scopes a later diagnostic read to this attempt.
|
|
449
|
+
clearFault(nodeId, { preserveEpisode: true });
|
|
450
|
+
const bootFault = beginBootFaultAttempt(nodeId);
|
|
451
|
+
// A cron armed --cancel-on-wake belongs to the dormancy being left, so a WAKE
|
|
452
|
+
// of its anchor deletes it — an inbox delivery, an attach, an explicit
|
|
453
|
+
// revive. A recovery relaunch is not a wake: the broker instance died or was
|
|
454
|
+
// torn down and the daemon is putting the same waiting node back on its feet,
|
|
455
|
+
// with nothing external having arrived. The wait the deadline bounds is still
|
|
456
|
+
// open, so the deadline survives — the same rule lifecycle.ts applies to the
|
|
457
|
+
// `crash` transition (a deadline MUST survive instance death).
|
|
458
|
+
if (opts.recovery !== true)
|
|
459
|
+
cancelCronsOnWake(nodeId);
|
|
460
|
+
ensureNodeDirs(nodeId);
|
|
461
|
+
const lifecycleHooks = discoverLifecycleHookRegistry(meta.cwd, meta.profile_id).plans.get('node:start') ?? [];
|
|
462
|
+
await invokeLifecycleHooks(lifecycleHooks, {
|
|
463
|
+
node: {
|
|
464
|
+
id: nodeId,
|
|
465
|
+
name: meta.name,
|
|
466
|
+
kind: meta.kind,
|
|
467
|
+
mode: meta.mode,
|
|
468
|
+
lifecycle: meta.lifecycle,
|
|
469
|
+
cwd: meta.cwd,
|
|
470
|
+
nodeDir: nodeDir(nodeId),
|
|
471
|
+
contextDir: contextDir(nodeId),
|
|
472
|
+
jobDir: jobDir(nodeId),
|
|
473
|
+
reportsDir: reportsDir(nodeId),
|
|
474
|
+
},
|
|
475
|
+
runtime: {
|
|
476
|
+
isBirth,
|
|
477
|
+
canvasHome: crtrHome(),
|
|
478
|
+
profile: meta.profile_id ?? null,
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
inv.env = { ...inv.env, CRTR_EXECUTION_ID: executionId };
|
|
482
|
+
let launched;
|
|
493
483
|
try {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
schema: 'crtr.node-outcome-detail/v1',
|
|
499
|
-
message: error instanceof Error ? error.message : String(error),
|
|
500
|
-
},
|
|
501
|
-
},
|
|
484
|
+
launched = headlessBrokerHost.launch(nodeId, inv, {
|
|
485
|
+
cwd: meta.cwd,
|
|
486
|
+
name: fullName(meta),
|
|
487
|
+
resuming,
|
|
502
488
|
});
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
489
|
+
if (launched.pid === null) {
|
|
490
|
+
throw new Error(`broker host returned no pid for ${nodeId}`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
catch (error) {
|
|
494
|
+
try {
|
|
495
|
+
transition(nodeId, 'crash', {
|
|
496
|
+
reason: 'launch_failed',
|
|
497
|
+
outcome: {
|
|
498
|
+
detail: {
|
|
499
|
+
schema: 'crtr.node-outcome-detail/v1',
|
|
500
|
+
message: error instanceof Error ? error.message : String(error),
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
// Doctrine wake: a launch-refusal here marks the node dead with NOTHING
|
|
505
|
+
// else fanning that out — the daemon's own auto-revive is the most common
|
|
506
|
+
// caller (superviseTick, best-effort try/catch, stderr-only on failure), so
|
|
507
|
+
// without this a manager waiting dormant on this child hangs forever,
|
|
508
|
+
// never told it will not come back on its own. Mirrors closeNode's step-4
|
|
509
|
+
// fan-out (same subscribers table, same active/passive split).
|
|
510
|
+
fanDoctrineWake(nodeId, subscribersOf(nodeId), `Child crashed — ${fullName(meta)} (${nodeId}) failed to relaunch and is now dead: ${error.message}\n\n` +
|
|
511
|
+
`It stays dead until you revive it — \`crtr node lifecycle revive ${nodeId}\`.`, { reason: 'child-crashed', child: nodeId });
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
/* best-effort cleanup */
|
|
515
|
+
}
|
|
516
|
+
abortExecution(executionId, { reason: 'launch_failed' });
|
|
517
|
+
throw new Error(`[broker] refusing to revive ${nodeId}: ${error.message}`);
|
|
511
518
|
}
|
|
512
|
-
|
|
513
|
-
|
|
519
|
+
recordLaunch(nodeId, new Date().toISOString());
|
|
520
|
+
// The execution row owns the broker pid. A stale broker that reaches the
|
|
521
|
+
// daemon after its execution was superseded is stopped rather than claiming
|
|
522
|
+
// the current node process slot.
|
|
523
|
+
if (recordExecutionPid(executionId, nodeId, launched.pid) === 'shutdown') {
|
|
524
|
+
abortExecution(executionId, { reason: 'superseded' });
|
|
525
|
+
headlessBrokerHost.teardown(nodeId);
|
|
526
|
+
throw new Error(`[broker] refusing to revive ${nodeId}: execution was superseded during launch`);
|
|
514
527
|
}
|
|
515
|
-
|
|
516
|
-
|
|
528
|
+
// A launch is the definition of not-frozen, and every launcher passes
|
|
529
|
+
// through here — so this is the one place the wait mark is retired.
|
|
530
|
+
clearFrozen(nodeId);
|
|
531
|
+
return {
|
|
532
|
+
window: null,
|
|
533
|
+
session: meta.tmux_session ?? null,
|
|
534
|
+
resumed: resuming,
|
|
535
|
+
launch: launched,
|
|
536
|
+
faultExplanation: bootFault.explanation,
|
|
537
|
+
outcome: 'launched',
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
abortExecution(executionId, { reason: 'launch_failed' });
|
|
542
|
+
throw error;
|
|
517
543
|
}
|
|
518
|
-
recordLaunch(nodeId, new Date().toISOString());
|
|
519
|
-
// Record the real broker pid. The pid is METADATA now (design D-13) — the
|
|
520
|
-
// liveness authority is the fleet entry registered synchronously inside
|
|
521
|
-
// `launch` above — but session_start re-records it after extension bind,
|
|
522
|
-
// and status surfaces (canvas views, the daemon's identity rechecks at
|
|
523
|
-
// signal boundaries) still read it.
|
|
524
|
-
recordPid(nodeId, launched.pid);
|
|
525
|
-
// A launch is the definition of not-frozen, and every launcher passes
|
|
526
|
-
// through here — so this is the one place the wait mark is retired.
|
|
527
|
-
clearFrozen(nodeId);
|
|
528
|
-
return {
|
|
529
|
-
window: null,
|
|
530
|
-
session: meta.tmux_session ?? null,
|
|
531
|
-
resumed: resuming,
|
|
532
|
-
launch: launched,
|
|
533
|
-
faultExplanation: bootFault.explanation,
|
|
534
|
-
outcome: 'launched',
|
|
535
|
-
};
|
|
536
544
|
}
|
|
@@ -22,13 +22,13 @@ import { appendSituationalContext, formatSituationalProse } from './situational-
|
|
|
22
22
|
import { hasRoadmap, seedRoadmap } from './roadmap.js';
|
|
23
23
|
import { buildWakeBearings } from './bearings.js';
|
|
24
24
|
import { encodeKickoffOrigin, KICKOFF_ORIGIN_ENV } from './stamp/protocol.js';
|
|
25
|
-
import { canonicalSessionFile, contextDir, crtrHome, findNodeBySessionFile, getNode, fullName, jobDir, nodeDir,
|
|
25
|
+
import { beginExecution, canonicalSessionFile, contextDir, crtrHome, findNodeBySessionFile, getNode, fullName, jobDir, nodeDir, recordExecutionPid, abortExecution, reportsDir, setFrozen } from '../canvas/index.js';
|
|
26
26
|
import { boundFleet, brokerThresholdsForDaemon } from './fleet.js';
|
|
27
27
|
import { emitEvent } from '../events/emit.js';
|
|
28
28
|
import { openViewerWindow, focusOf, windowOfPane, } from './placement.js';
|
|
29
29
|
import { waitForBrokerViewSocket } from './placement-tmux.js';
|
|
30
30
|
import { transition } from './lifecycle.js';
|
|
31
|
-
import { beginBootFaultAttempt } from '
|
|
31
|
+
import { beginBootFaultAttempt } from '../canvas/recovery.js';
|
|
32
32
|
import { piSessionsRoot } from './pi-vendored.js';
|
|
33
33
|
import { headlessBrokerHost } from './host.js';
|
|
34
34
|
import { ensureDaemon } from '../../daemon/manage.js';
|
|
@@ -390,6 +390,8 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch, afterNodeCrea
|
|
|
390
390
|
return { node: getNode(meta.node_id), frozen: true, window: null, session: null };
|
|
391
391
|
}
|
|
392
392
|
const bootFault = beginBootFaultAttempt(meta.node_id);
|
|
393
|
+
const executionId = beginExecution(meta.node_id, { daemonEpoch: fleet.epoch() });
|
|
394
|
+
inv.env = { ...inv.env, CRTR_EXECUTION_ID: executionId };
|
|
393
395
|
// Broker is the only host: launch the detached broker ENGINE. A managed child
|
|
394
396
|
// gets NO viewer — spawning it returns once the engine is launched and a human
|
|
395
397
|
// opens a viewer on demand via `crtr surface node focus` / surface attach. A --root gets
|
|
@@ -402,6 +404,10 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch, afterNodeCrea
|
|
|
402
404
|
resuming: false,
|
|
403
405
|
});
|
|
404
406
|
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
abortExecution(executionId, { reason: 'launch_failed' });
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
405
411
|
finally {
|
|
406
412
|
fleet.releaseReservation(meta.node_id);
|
|
407
413
|
}
|
|
@@ -411,10 +417,15 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch, afterNodeCrea
|
|
|
411
417
|
// spawnNode). Mirrors the old window===null crash.
|
|
412
418
|
if (placed.pid === null) {
|
|
413
419
|
const error = new Error(`broker host returned no pid for ${meta.node_id}`);
|
|
420
|
+
abortExecution(executionId, { reason: 'launch_failed' });
|
|
414
421
|
transition(meta.node_id, 'crash', { reason: 'launch_failed', outcome: launchFailureOutcome(error) });
|
|
415
422
|
throw brokerLaunchFailed(`failed to launch the broker engine for ${meta.node_id} (${meta.name}) — the node was not started.`, `Inspect canonical event diagnostics with \`crtr sys logs --node ${meta.node_id}\`, then verify the pi engine can start in this environment.`);
|
|
416
423
|
}
|
|
417
|
-
|
|
424
|
+
if (recordExecutionPid(executionId, meta.node_id, placed.pid) === 'shutdown') {
|
|
425
|
+
abortExecution(executionId, { reason: 'superseded' });
|
|
426
|
+
headlessBrokerHost.teardown(meta.node_id);
|
|
427
|
+
throw brokerLaunchFailed(`broker execution was superseded for ${meta.node_id}`, 'Retry spawning the node.');
|
|
428
|
+
}
|
|
418
429
|
// A root created through `node new --root` must accept viewers before the
|
|
419
430
|
// command returns because its caller may attach immediately. Only that root
|
|
420
431
|
// waits — a managed child (and every cron-launched
|