@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
|
@@ -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',
|
|
@@ -278,10 +277,6 @@ async function launchRevive(nodeId, meta, opts) {
|
|
|
278
277
|
next,
|
|
279
278
|
});
|
|
280
279
|
}
|
|
281
|
-
// This is the same durable distinction used to decide between a birth prompt
|
|
282
|
-
// and a revive kickoff below. Compute it before launch mutations so a stale
|
|
283
|
-
// session identity cannot make an ordinary fresh revive look like a birth.
|
|
284
|
-
const isBirth = isUnstartedBirth(nodeId);
|
|
285
280
|
// Lazy host_kind coerce (§C): every launch now uses the broker host. Persist
|
|
286
281
|
// that identity only after this call has passed the no-op liveness guard.
|
|
287
282
|
if (meta.host_kind !== 'broker') {
|
|
@@ -293,7 +288,7 @@ async function launchRevive(nodeId, meta, opts) {
|
|
|
293
288
|
// immutable fork instead of resuming an inherited transcript.
|
|
294
289
|
const reviewCompanionUnbound = meta.review_binding != null && !isReviewCompanionBound(nodeId);
|
|
295
290
|
if (reviewCompanionUnbound && (meta.pi_session_id != null || meta.pi_session_file != null)) {
|
|
296
|
-
|
|
291
|
+
unbindSession(nodeId);
|
|
297
292
|
meta.pi_session_id = null;
|
|
298
293
|
meta.pi_session_file = null;
|
|
299
294
|
}
|
|
@@ -316,220 +311,234 @@ async function launchRevive(nodeId, meta, opts) {
|
|
|
316
311
|
next: `Restore ${forkSourceFile}, or update the node's fork source, then retry \`crtr node lifecycle revive ${nodeId}\`.`,
|
|
317
312
|
});
|
|
318
313
|
}
|
|
319
|
-
|
|
320
|
-
// footer stay in sync. Mutate the in-memory meta too so buildPiArgv below
|
|
321
|
-
// launches with the incremented count.
|
|
322
|
-
meta.cycles = (meta.cycles ?? 0) + 1;
|
|
323
|
-
updateNode(nodeId, { cycles: meta.cycles });
|
|
324
|
-
// Ordinary nodes choose resume/cycle/fresh from their recorded session.
|
|
325
|
-
// Persisted pending-refresh state (durable across a pre-session_start
|
|
326
|
-
// crash-retry) outranks a caller's strict-resume request. A parked node now
|
|
327
|
-
// resumes its transcript too: its isolated parking turn never entered the
|
|
328
|
-
// main session, so wake mail continues one intact conversation without a
|
|
329
|
-
// fresh-cycle handoff. resumeArgs then selects only an existing absolute
|
|
330
|
-
// session path: strict resume continues its leaf, while refresh/cycle mode
|
|
331
|
-
// roots a new one.
|
|
332
|
-
const refreshPending = meta.intent === 'refresh' || meta.cycle_pending === true;
|
|
333
|
-
const effectiveResume = refreshPending ? false : opts.resume;
|
|
334
|
-
const resume = forkBirthPending ? {} : resumableSessionArgs(nodeId, meta, effectiveResume);
|
|
335
|
-
// hasSessionPath: there is a real `.jsonl` to hand pi via `--session`, whether
|
|
336
|
-
// as a true resume OR a cycling refresh-yield (resumeArgs sets `newCycle` in
|
|
337
|
-
// the latter case). cycling and resuming are mutually exclusive; `resuming`
|
|
338
|
-
// keeps the STRICT true-resume-only meaning `ReviveResult.resumed` and the
|
|
339
|
-
// launch-placement hint below have always carried.
|
|
340
|
-
const hasSessionPath = resume.resumeSessionPath !== undefined;
|
|
341
|
-
const cycling = hasSessionPath && resume.newCycle === true;
|
|
342
|
-
const resuming = hasSessionPath && !cycling;
|
|
343
|
-
// A cycling launch is provisional until session_start confirms it. Once
|
|
344
|
-
// true, this marker is monotonic across failed retries; only that confirmed
|
|
345
|
-
// boot may clear it.
|
|
346
|
-
const cyclePending = meta.cycle_pending === true || cycling;
|
|
347
|
-
updateNode(nodeId, { cycle_pending: cyclePending });
|
|
348
|
-
meta.cycle_pending = cyclePending;
|
|
349
|
-
// A truly fresh launch — no `.jsonl` to resume OR cycle from —
|
|
350
|
-
// starts a NEW pi session, so the old session identity is dead. Clear it from meta NOW,
|
|
351
|
-
// before launch, so the recorded state matches what we're about to do. This
|
|
352
|
-
// is load-bearing for exit-policy classification (classifyDeadNode row 8):
|
|
353
|
-
// if this fresh broker dies BEFORE session_start (recording no new session),
|
|
354
|
-
// the exit-policy job reads pi_session_id=NULL → boot-failure, the correct
|
|
355
|
-
// terminal outcome (transition('crash') + surfaceBootFailure) — a leftover
|
|
356
|
-
// stale id would instead be misread as a resumable session and retried. A
|
|
357
|
-
// cycling refresh keeps BOTH ids — the `.jsonl` and its recorded session id
|
|
358
|
-
// are exactly what's being reused — so a cycling broker that dies
|
|
359
|
-
// pre-session_start classifies as a retryable respawn, same class as a
|
|
360
|
-
// resume revive.
|
|
361
|
-
if (!forkBirthPending && !hasSessionPath && (meta.pi_session_id != null || meta.pi_session_file != null)) {
|
|
362
|
-
updateNode(nodeId, { pi_session_id: null, pi_session_file: null });
|
|
363
|
-
meta.pi_session_id = null;
|
|
364
|
-
meta.pi_session_file = null;
|
|
365
|
-
}
|
|
366
|
-
// A true resume needs no kickoff — the saved conversation just continues. A
|
|
367
|
-
// fresh revive OR a cycling refresh-yield both get one so the (re-rooted)
|
|
368
|
-
// window re-reads its roadmap and continues: drainBearings captures the
|
|
369
|
-
// yield note and consumes the feed/persona acknowledgements; the builder is
|
|
370
|
-
// then pure. `resume` carries `resumeSessionPath`/`newCycle` through
|
|
371
|
-
// unchanged (empty for true-fresh, both set for cycling) so buildPiArgv emits
|
|
372
|
-
// the right argv either way.
|
|
373
|
-
let inv;
|
|
374
|
-
// The provenance of whatever kickoff this branch composes, handed to the
|
|
375
|
-
// broker process through inv.env below. A promptless launch announces
|
|
376
|
-
// nothing — there is no message to stamp.
|
|
377
|
-
let kickoffOrigin = null;
|
|
378
|
-
const birthOrigin = () => ({
|
|
379
|
-
type: 'runtime',
|
|
380
|
-
kind: 'kickoff',
|
|
381
|
-
...(meta.spawned_by != null && meta.spawned_by !== '' ? { spawnedBy: meta.spawned_by } : {}),
|
|
382
|
-
});
|
|
383
|
-
if (forkBirthPending) {
|
|
384
|
-
const goal = readGoal(nodeId);
|
|
385
|
-
inv = buildPiArgv(meta, {
|
|
386
|
-
forkFrom: forkSourceFile,
|
|
387
|
-
...(goal != null && goal !== '' ? { prompt: goal } : {}),
|
|
388
|
-
});
|
|
389
|
-
if (goal != null && goal !== '')
|
|
390
|
-
kickoffOrigin = birthOrigin();
|
|
391
|
-
initializeForkContextExposure(nodeId, findNodeBySessionFile(forkSourceFile)?.node_id ?? null);
|
|
392
|
-
}
|
|
393
|
-
else if (resuming) {
|
|
394
|
-
inv = buildPiArgv(meta, {
|
|
395
|
-
...resume,
|
|
396
|
-
...(opts.wakeReason === 'runtime-restart-abort' ? { prompt: RUNTIME_RESTART_CONTINUATION } : {}),
|
|
397
|
-
});
|
|
398
|
-
if (opts.wakeReason === 'runtime-restart-abort') {
|
|
399
|
-
kickoffOrigin = { type: 'runtime', kind: 'restart-continuation' };
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
else if (!hasSessionPath &&
|
|
403
|
-
// A clock-driven wake must keep its <crtr-wake> kickoff — the schedule IS
|
|
404
|
-
// the instruction to take a turn, so it never degrades to a birth replay.
|
|
405
|
-
(opts.wakeReason === undefined || opts.wakeReason === 'runtime-restart-abort') &&
|
|
406
|
-
isUnstartedBirth(nodeId)) {
|
|
407
|
-
// Never conversed and nothing on disk to rebuild from — relaunch as a birth
|
|
408
|
-
// (see isUnstartedBirth). A node with a spawn mandate replays it; a bare
|
|
409
|
-
// interactive node boots promptless and takes no turn at all.
|
|
410
|
-
const goal = readGoal(nodeId)?.trim();
|
|
411
|
-
inv = buildPiArgv(meta, goal !== undefined && goal !== '' ? { prompt: goal } : {});
|
|
412
|
-
if (goal !== undefined && goal !== '')
|
|
413
|
-
kickoffOrigin = birthOrigin();
|
|
414
|
-
clearContextExposureState(nodeId);
|
|
415
|
-
}
|
|
416
|
-
else {
|
|
417
|
-
// Fresh or cycling: drain the one-shot bearings, then build the kickoff.
|
|
418
|
-
// When a scheduled bare self-alarm drove this revive, opts.wakeReason
|
|
419
|
-
// carries the wake provenance so the kickoff leads with a <crtr-wake> block
|
|
420
|
-
// ("a timer woke you"); every other reviveNode caller passes nothing → no
|
|
421
|
-
// block.
|
|
422
|
-
const bearings = drainBearings(meta);
|
|
423
|
-
inv = buildPiArgv(meta, {
|
|
424
|
-
...resume,
|
|
425
|
-
prompt: buildReviveKickoff(meta, bearings, opts.wakeReason === 'runtime-restart-abort' ? undefined : opts.wakeReason),
|
|
426
|
-
});
|
|
427
|
-
kickoffOrigin = opts.wakeReason !== undefined && opts.wakeReason !== 'runtime-restart-abort'
|
|
428
|
-
? { type: 'runtime', kind: 'wake', wake: opts.wakeReason }
|
|
429
|
-
: { type: 'runtime', kind: 'revive', cycle: meta.cycles ?? 0 };
|
|
430
|
-
clearContextExposureState(nodeId);
|
|
431
|
-
}
|
|
432
|
-
// Every lifecycle launch emits the authoritative spine root rather than
|
|
433
|
-
// inheriting whichever node happened to invoke reviveNode.
|
|
434
|
-
inv.env = { ...inv.env, CRTR_SUBTREE: rootOfSpine(nodeId) };
|
|
435
|
-
if (kickoffOrigin !== null)
|
|
436
|
-
inv.env[KICKOFF_ORIGIN_ENV] = encodeKickoffOrigin(kickoffOrigin);
|
|
437
|
-
// The broker host launches the detached engine. reviveNode keeps
|
|
438
|
-
// transition+clearPid around it (the crash-safety ordering, unchanged). It
|
|
439
|
-
// opens NO viewer — engine-only; existing viewers reconnect, and a viewer-less
|
|
440
|
-
// node is brought on screen by the next `focus`.
|
|
441
|
-
transition(nodeId, meta.intent === 'refresh' ? 'refresh-launch' : 'revive');
|
|
442
|
-
// Clear any active fault the instant this node is (re)launched — don't wait
|
|
443
|
-
// for the fresh agent_start. A revive (auto or on-demand) is the recovery
|
|
444
|
-
// this marker advertised. Snapshot the boot-fault attempt in the SAME
|
|
445
|
-
// breath (see beginBootFaultAttempt): its own verification, not this call's
|
|
446
|
-
// unchecked result, is what scopes a later diagnostic read to this attempt.
|
|
447
|
-
clearFault(nodeId, { preserveProviderRetryEpisode: true });
|
|
448
|
-
const bootFault = beginBootFaultAttempt(nodeId);
|
|
449
|
-
// A cron armed --cancel-on-wake belongs to the dormancy being left, so a WAKE
|
|
450
|
-
// of its anchor deletes it — an inbox delivery, an attach, an explicit
|
|
451
|
-
// revive. A recovery relaunch is not a wake: the broker instance died or was
|
|
452
|
-
// torn down and the daemon is putting the same waiting node back on its feet,
|
|
453
|
-
// with nothing external having arrived. The wait the deadline bounds is still
|
|
454
|
-
// open, so the deadline survives — the same rule lifecycle.ts applies to the
|
|
455
|
-
// `crash` transition (a deadline MUST survive instance death).
|
|
456
|
-
if (opts.recovery !== true)
|
|
457
|
-
cancelCronsOnWake(nodeId);
|
|
458
|
-
ensureNodeDirs(nodeId);
|
|
459
|
-
clearTransportPoisonVerdict(nodeId);
|
|
460
|
-
const lifecycleHooks = discoverLifecycleHookRegistry(meta.cwd, meta.profile_id).plans.get('node:start') ?? [];
|
|
461
|
-
await invokeLifecycleHooks(lifecycleHooks, {
|
|
462
|
-
node: {
|
|
463
|
-
id: nodeId,
|
|
464
|
-
name: meta.name,
|
|
465
|
-
kind: meta.kind,
|
|
466
|
-
mode: meta.mode,
|
|
467
|
-
lifecycle: meta.lifecycle,
|
|
468
|
-
cwd: meta.cwd,
|
|
469
|
-
nodeDir: nodeDir(nodeId),
|
|
470
|
-
contextDir: contextDir(nodeId),
|
|
471
|
-
jobDir: jobDir(nodeId),
|
|
472
|
-
reportsDir: reportsDir(nodeId),
|
|
473
|
-
},
|
|
474
|
-
runtime: {
|
|
475
|
-
isBirth,
|
|
476
|
-
canvasHome: crtrHome(),
|
|
477
|
-
profile: meta.profile_id ?? null,
|
|
478
|
-
},
|
|
479
|
-
});
|
|
480
|
-
let launched;
|
|
314
|
+
const executionId = beginExecution(nodeId, { daemonEpoch: boundFleet().epoch() });
|
|
481
315
|
try {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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 } : {}),
|
|
486
389
|
});
|
|
487
|
-
if (
|
|
488
|
-
|
|
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);
|
|
489
399
|
}
|
|
490
|
-
|
|
491
|
-
|
|
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;
|
|
492
483
|
try {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
schema: 'crtr.node-outcome-detail/v1',
|
|
498
|
-
message: error instanceof Error ? error.message : String(error),
|
|
499
|
-
},
|
|
500
|
-
},
|
|
484
|
+
launched = headlessBrokerHost.launch(nodeId, inv, {
|
|
485
|
+
cwd: meta.cwd,
|
|
486
|
+
name: fullName(meta),
|
|
487
|
+
resuming,
|
|
501
488
|
});
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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}`);
|
|
510
518
|
}
|
|
511
|
-
|
|
512
|
-
|
|
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`);
|
|
513
527
|
}
|
|
514
|
-
|
|
515
|
-
|
|
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;
|
|
516
543
|
}
|
|
517
|
-
recordLaunch(nodeId, new Date().toISOString());
|
|
518
|
-
// Record the real broker pid. The pid is METADATA now (design D-13) — the
|
|
519
|
-
// liveness authority is the fleet entry registered synchronously inside
|
|
520
|
-
// `launch` above — but session_start re-records it after extension bind,
|
|
521
|
-
// and status surfaces (canvas views, the daemon's identity rechecks at
|
|
522
|
-
// signal boundaries) still read it.
|
|
523
|
-
recordPid(nodeId, launched.pid);
|
|
524
|
-
// A launch is the definition of not-frozen, and every launcher passes
|
|
525
|
-
// through here — so this is the one place the wait mark is retired.
|
|
526
|
-
clearFrozen(nodeId);
|
|
527
|
-
return {
|
|
528
|
-
window: null,
|
|
529
|
-
session: meta.tmux_session ?? null,
|
|
530
|
-
resumed: resuming,
|
|
531
|
-
launch: launched,
|
|
532
|
-
faultExplanation: bootFault.explanation,
|
|
533
|
-
outcome: 'launched',
|
|
534
|
-
};
|
|
535
544
|
}
|
|
@@ -127,8 +127,11 @@ export declare function resolveSpawner(parent: string | undefined, ctxNodeId: st
|
|
|
127
127
|
* to the spawner (it carries spawned_by=spawner for provenance only), brought
|
|
128
128
|
* forefront so a human can pick up the conversation directly. */
|
|
129
129
|
type BeforeBrokerLaunch = (nodeId: string) => void;
|
|
130
|
-
|
|
130
|
+
type AfterNodeCreated = (nodeId: string) => void;
|
|
131
|
+
export declare function spawnChildPrepared(opts: SpawnChildOpts, beforeBrokerLaunch?: BeforeBrokerLaunch, afterNodeCreated?: AfterNodeCreated): Promise<SpawnChildResult>;
|
|
131
132
|
export declare function spawnChild(opts: SpawnChildOpts): Promise<SpawnChildResult>;
|
|
133
|
+
/** Invoke the best-effort node-start plan before any cold broker launch. */
|
|
134
|
+
export declare function invokeBirthLifecycleHooks(meta: NodeMeta): Promise<void>;
|
|
132
135
|
/** Create an independent, idle root from a node's conversation and durable
|
|
133
136
|
* context. Runtime/graph state is intentionally not inherited. */
|
|
134
137
|
export declare function forkNode(sourceId: string): Promise<SpawnChildResult>;
|
|
@@ -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, findNodeBySessionFile, getNode, fullName,
|
|
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';
|
|
@@ -37,6 +37,8 @@ import { modelRequestFromConfig, unregisteredConcreteModel } from '../model-rout
|
|
|
37
37
|
import { openModelRegistry } from './model-registry.js';
|
|
38
38
|
import { MODEL_SPEC_NEXT } from './model-selection.js';
|
|
39
39
|
import { initializeForkContextExposure } from '../substrate/injected-store.js';
|
|
40
|
+
import { discoverLifecycleHookRegistry } from '../command-hooks/discovery.js';
|
|
41
|
+
import { invokeLifecycleHooks } from '../command-hooks/transport/exec-lifecycle.js';
|
|
40
42
|
// Shared broker-launch wrapper — catch launch/preflight failures at the call
|
|
41
43
|
// site, mark the node crashed, then rethrow a clear error.
|
|
42
44
|
function launchFailureOutcome(error) {
|
|
@@ -210,7 +212,7 @@ export function resolveSpawner(parent, ctxNodeId, root) {
|
|
|
210
212
|
}
|
|
211
213
|
return spawner;
|
|
212
214
|
}
|
|
213
|
-
export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
215
|
+
export async function spawnChildPrepared(opts, beforeBrokerLaunch, afterNodeCreated) {
|
|
214
216
|
try {
|
|
215
217
|
ensureDaemon();
|
|
216
218
|
}
|
|
@@ -276,7 +278,7 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
276
278
|
if (wantsWorktree) {
|
|
277
279
|
await assertLaunchModelRegistered(requestFromLaunch(launch), { cwd: spawnCwd, profileId });
|
|
278
280
|
}
|
|
279
|
-
const meta = spawnNode({
|
|
281
|
+
const meta = await spawnNode({
|
|
280
282
|
kind: opts.kind,
|
|
281
283
|
mode,
|
|
282
284
|
lifecycle,
|
|
@@ -299,6 +301,7 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
299
301
|
...(opts.outcomeDelivery === undefined ? {} : { outcomeDelivery: opts.outcomeDelivery }),
|
|
300
302
|
launch,
|
|
301
303
|
});
|
|
304
|
+
afterNodeCreated?.(meta.node_id);
|
|
302
305
|
if (opts.forkFrom !== undefined) {
|
|
303
306
|
try {
|
|
304
307
|
initializeForkContextExposure(meta.node_id, forkSourceNodeId);
|
|
@@ -345,6 +348,7 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
345
348
|
}
|
|
346
349
|
// (The three scoped long-term memory stores are seeded for EVERY node at birth
|
|
347
350
|
// in spawnNode — no orchestrator-gated seeding needed here.)
|
|
351
|
+
await invokeBirthLifecycleHooks(meta);
|
|
348
352
|
const inv = buildPiArgv(meta, { prompt: kickoff, forkFrom });
|
|
349
353
|
// CRTR_SUBTREE (the spine root) rides the detached broker's env so it can group
|
|
350
354
|
// the subtree; the host sets CRTR_FRONT_DOOR itself.
|
|
@@ -386,6 +390,8 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
386
390
|
return { node: getNode(meta.node_id), frozen: true, window: null, session: null };
|
|
387
391
|
}
|
|
388
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 };
|
|
389
395
|
// Broker is the only host: launch the detached broker ENGINE. A managed child
|
|
390
396
|
// gets NO viewer — spawning it returns once the engine is launched and a human
|
|
391
397
|
// opens a viewer on demand via `crtr surface node focus` / surface attach. A --root gets
|
|
@@ -398,6 +404,10 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
398
404
|
resuming: false,
|
|
399
405
|
});
|
|
400
406
|
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
abortExecution(executionId, { reason: 'launch_failed' });
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
401
411
|
finally {
|
|
402
412
|
fleet.releaseReservation(meta.node_id);
|
|
403
413
|
}
|
|
@@ -407,10 +417,15 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
407
417
|
// spawnNode). Mirrors the old window===null crash.
|
|
408
418
|
if (placed.pid === null) {
|
|
409
419
|
const error = new Error(`broker host returned no pid for ${meta.node_id}`);
|
|
420
|
+
abortExecution(executionId, { reason: 'launch_failed' });
|
|
410
421
|
transition(meta.node_id, 'crash', { reason: 'launch_failed', outcome: launchFailureOutcome(error) });
|
|
411
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.`);
|
|
412
423
|
}
|
|
413
|
-
|
|
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
|
+
}
|
|
414
429
|
// A root created through `node new --root` must accept viewers before the
|
|
415
430
|
// command returns because its caller may attach immediately. Only that root
|
|
416
431
|
// waits — a managed child (and every cron-launched
|
|
@@ -491,6 +506,29 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
491
506
|
export async function spawnChild(opts) {
|
|
492
507
|
return spawnChildPrepared(opts);
|
|
493
508
|
}
|
|
509
|
+
/** Invoke the best-effort node-start plan before any cold broker launch. */
|
|
510
|
+
export async function invokeBirthLifecycleHooks(meta) {
|
|
511
|
+
const lifecycleHooks = discoverLifecycleHookRegistry(meta.cwd, meta.profile_id).plans.get('node:start') ?? [];
|
|
512
|
+
await invokeLifecycleHooks(lifecycleHooks, {
|
|
513
|
+
node: {
|
|
514
|
+
id: meta.node_id,
|
|
515
|
+
name: meta.name,
|
|
516
|
+
kind: meta.kind,
|
|
517
|
+
mode: meta.mode,
|
|
518
|
+
lifecycle: meta.lifecycle,
|
|
519
|
+
cwd: meta.cwd,
|
|
520
|
+
nodeDir: nodeDir(meta.node_id),
|
|
521
|
+
contextDir: contextDir(meta.node_id),
|
|
522
|
+
jobDir: jobDir(meta.node_id),
|
|
523
|
+
reportsDir: reportsDir(meta.node_id),
|
|
524
|
+
},
|
|
525
|
+
runtime: {
|
|
526
|
+
isBirth: true,
|
|
527
|
+
canvasHome: crtrHome(),
|
|
528
|
+
profile: meta.profile_id ?? null,
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
}
|
|
494
532
|
const FORK_CONTEXT_EXCLUDES = new Set(['initial-prompt.md', 'yield-message.md', 'result.json']);
|
|
495
533
|
/** Create an independent, idle root from a node's conversation and durable
|
|
496
534
|
* context. Runtime/graph state is intentionally not inherited. */
|
|
@@ -30,8 +30,8 @@ export interface WarmRequest {
|
|
|
30
30
|
creator: string | null;
|
|
31
31
|
}
|
|
32
32
|
/** Claim a spare for `request`, reshaped onto its persona — or null when the
|
|
33
|
-
* pool cannot serve it. The caller falls back to a cold spawn; a
|
|
34
|
-
*
|
|
33
|
+
* pool cannot serve it. The caller falls back to a cold spawn; a refused
|
|
34
|
+
* create hook leaves the spare untouched and rejects the create. */
|
|
35
35
|
export declare function claimWarmNode(request: WarmRequest, birth?: {
|
|
36
36
|
deadlineAt?: string;
|
|
37
37
|
outcomeDelivery?: OutcomeDeliveryBinding;
|