@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
|
@@ -8,15 +8,14 @@ import { contextDir, isSafeNodeId } from '../../core/canvas/paths.js';
|
|
|
8
8
|
import { fullName } from '../../core/canvas/labels.js';
|
|
9
9
|
import { recordedPidLiveness } from '../../core/canvas/pid.js';
|
|
10
10
|
import { transition } from '../../core/runtime/lifecycle.js';
|
|
11
|
-
import {
|
|
12
|
-
import { readFault, recordFault } from '../../core/runtime/fault.js';
|
|
11
|
+
import { readFault, readLiveTurn, recordFault } from '../../core/canvas/recovery.js';
|
|
13
12
|
import { nextFaultRetry } from '../../core/runtime/fault-recovery.js';
|
|
14
13
|
import { fanDoctrineWake } from '../../core/runtime/close.js';
|
|
15
14
|
import { headlessBrokerHost } from '../../core/runtime/host.js';
|
|
16
15
|
import { reloadAuthLive } from '../../core/runtime/auth-reload.js';
|
|
17
16
|
import { emitEvent } from '../../core/events/emit.js';
|
|
18
17
|
import { operationIdContext } from '../../core/events/operation-id.js';
|
|
19
|
-
import { hasUnseenWakeMail } from '../../core/
|
|
18
|
+
import { hasUnseenWakeMail, resetStaleExecutionMail } from '../../core/canvas/deliveries.js';
|
|
20
19
|
import { activeBackgroundBashJobs } from '../../core/bash-jobs.js';
|
|
21
20
|
import { focusOf } from '../../core/runtime/placement.js';
|
|
22
21
|
import { hasAttachedBrokerViewers } from '../../core/runtime/broker/client-registry.js';
|
|
@@ -25,18 +24,32 @@ import { BrokerUnreachableError } from '../../core/runtime/broker-request.js';
|
|
|
25
24
|
import { formatCard, PARK_SUMMARY_PROMPT } from '../../shared/generated-context.js';
|
|
26
25
|
import { brokerThresholdsForDaemon } from '../../core/runtime/fleet.js';
|
|
27
26
|
import { readConfig } from '../../core/config.js';
|
|
27
|
+
import { scopedStoreExists } from '../../core/scoped-state/db.js';
|
|
28
|
+
import { providerStore } from '../../core/scoped-state/paths.js';
|
|
29
|
+
import { providerRevision } from '../../core/scoped-state/providers.js';
|
|
28
30
|
import { beginPendingPark, consumePendingPark, pendingParkIds, pendingParkStartedAt } from '../park-pending.js';
|
|
29
31
|
import { shouldParkAfterSummary } from '../park-activity.js';
|
|
30
32
|
import { applyParkEvent, parkEvent } from './live-obligation.js';
|
|
31
33
|
import { envNoDaemonAutostart, envTestUnattendedParkMs, parkSummaryGraceMs, envUnattendedParkMs, } from '../../shared/env.js';
|
|
32
34
|
const execFileAsync = promisify(execFile);
|
|
35
|
+
function liveBusy(nodeId) {
|
|
36
|
+
return readLiveTurn(nodeId)?.busy === true;
|
|
37
|
+
}
|
|
38
|
+
function liveTurnProgressMs(nodeId) {
|
|
39
|
+
const progressAt = readLiveTurn(nodeId)?.progressAt;
|
|
40
|
+
if (progressAt === null || progressAt === undefined)
|
|
41
|
+
return null;
|
|
42
|
+
const progressMs = Date.parse(progressAt);
|
|
43
|
+
return Number.isNaN(progressMs) ? null : progressMs;
|
|
44
|
+
}
|
|
33
45
|
function incarnationOf(pid, identity) {
|
|
34
46
|
return identity === null ? null : `${pid}:${identity}`;
|
|
35
47
|
}
|
|
36
48
|
function busySinceForIncarnation(row) {
|
|
37
|
-
const
|
|
49
|
+
const turn = readLiveTurn(row.node_id);
|
|
50
|
+
const since = turn?.busy === true && turn.progressAt !== null ? Date.parse(turn.progressAt) : Number.NaN;
|
|
38
51
|
const launchedAt = row.launched_at === null || row.launched_at === undefined ? Number.NaN : Date.parse(row.launched_at);
|
|
39
|
-
if (since
|
|
52
|
+
if (!Number.isFinite(since) || !Number.isFinite(launchedAt) || since < launchedAt)
|
|
40
53
|
return null;
|
|
41
54
|
return since;
|
|
42
55
|
}
|
|
@@ -131,13 +144,17 @@ export function treeHasDescendants(psOutput, rootPid) {
|
|
|
131
144
|
return null;
|
|
132
145
|
return (childrenOf.get(rootPid) ?? []).length > 0;
|
|
133
146
|
}
|
|
134
|
-
function
|
|
135
|
-
const
|
|
136
|
-
|
|
147
|
+
function piAgentDir() {
|
|
148
|
+
const configured = process.env['PI_CODING_AGENT_DIR'];
|
|
149
|
+
if (configured !== undefined && configured !== '')
|
|
150
|
+
return configured === '~' || configured.startsWith('~/') ? join(homedir(), configured.slice(1)) : configured;
|
|
151
|
+
return join(homedir(), '.pi', 'agent');
|
|
152
|
+
}
|
|
153
|
+
function piCredentialPaths(agentDir) {
|
|
137
154
|
return [
|
|
138
155
|
join(agentDir, 'auth.json'),
|
|
139
|
-
join(agentDir, 'anthropic-
|
|
140
|
-
join(agentDir, 'openai-codex-
|
|
156
|
+
join(agentDir, 'anthropic-credentials.json'),
|
|
157
|
+
join(agentDir, 'openai-codex-credentials.json'),
|
|
141
158
|
];
|
|
142
159
|
}
|
|
143
160
|
export class BrokerSupervisionReconciler {
|
|
@@ -147,7 +164,7 @@ export class BrokerSupervisionReconciler {
|
|
|
147
164
|
unattendedSince = new Map();
|
|
148
165
|
sampleTree;
|
|
149
166
|
signal;
|
|
150
|
-
|
|
167
|
+
knownCredentialRevision = null;
|
|
151
168
|
lastOverCapWarnAt = Number.NEGATIVE_INFINITY;
|
|
152
169
|
constructor(options = {}) {
|
|
153
170
|
this.sampleTree = options.readTreeSample ?? ((pid) => this.readTreeSample(pid));
|
|
@@ -209,6 +226,7 @@ export class BrokerSupervisionReconciler {
|
|
|
209
226
|
if (!ctx.fleet.has(id))
|
|
210
227
|
consumePendingPark(id);
|
|
211
228
|
}
|
|
229
|
+
resetStaleExecutionMail(now);
|
|
212
230
|
}
|
|
213
231
|
/** Every condition that resets the unattended clock EXCEPT `isBusy` — the
|
|
214
232
|
* node is unavailable to park, attended, or has work waiting. Short-circuits
|
|
@@ -278,7 +296,7 @@ export class BrokerSupervisionReconciler {
|
|
|
278
296
|
headlessBrokerHost.teardown(id);
|
|
279
297
|
return;
|
|
280
298
|
}
|
|
281
|
-
if (reset ||
|
|
299
|
+
if (reset || liveBusy(id)) {
|
|
282
300
|
this.unattendedSince.delete(id);
|
|
283
301
|
return;
|
|
284
302
|
}
|
|
@@ -682,8 +700,9 @@ export class BrokerSupervisionReconciler {
|
|
|
682
700
|
});
|
|
683
701
|
}
|
|
684
702
|
async handleAuthReload(fleet) {
|
|
703
|
+
const agentDir = piAgentDir();
|
|
685
704
|
let mtimeMs = 0;
|
|
686
|
-
for (const path of piCredentialPaths()) {
|
|
705
|
+
for (const path of piCredentialPaths(agentDir)) {
|
|
687
706
|
try {
|
|
688
707
|
mtimeMs = Math.max(mtimeMs, statSync(path).mtimeMs);
|
|
689
708
|
}
|
|
@@ -691,15 +710,18 @@ export class BrokerSupervisionReconciler {
|
|
|
691
710
|
/* not written yet — nothing to observe for this file */
|
|
692
711
|
}
|
|
693
712
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
if (
|
|
697
|
-
|
|
713
|
+
let revision = 0;
|
|
714
|
+
const store = providerStore(agentDir);
|
|
715
|
+
if (scopedStoreExists(store))
|
|
716
|
+
revision = providerRevision(agentDir);
|
|
717
|
+
const current = `${mtimeMs}:${revision}`;
|
|
718
|
+
if (this.knownCredentialRevision === null) {
|
|
719
|
+
this.knownCredentialRevision = current;
|
|
698
720
|
return;
|
|
699
721
|
}
|
|
700
|
-
if (
|
|
722
|
+
if (current === this.knownCredentialRevision)
|
|
701
723
|
return;
|
|
702
|
-
this.
|
|
724
|
+
this.knownCredentialRevision = current;
|
|
703
725
|
await operationIdContext.fresh(async () => {
|
|
704
726
|
try {
|
|
705
727
|
const live = Array.from(fleet.entries(), ([id]) => id);
|
|
@@ -1,25 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { liveControllerWaits } from '../../core/canvas/waits.js';
|
|
2
2
|
import { operationIdContext } from '../../core/events/operation-id.js';
|
|
3
|
-
import {
|
|
3
|
+
import { enqueueControllerDeathNotice } from '../../core/feed/messages.js';
|
|
4
4
|
export class ControllerDeathReconciler {
|
|
5
5
|
run(now, ctx) {
|
|
6
|
-
void now;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// watcher; an idle-released node is revived by the node-lifecycle tick.
|
|
10
|
-
for (const row of ctx.rows) {
|
|
11
|
-
const current = getRow(row.node_id);
|
|
12
|
-
const controllerId = current?.waiting_for;
|
|
13
|
-
if (controllerId === null || controllerId === undefined)
|
|
14
|
-
continue;
|
|
15
|
-
// Read-gate before the write boundary: only a dead controller warrants a
|
|
16
|
-
// notice. appendControllerDeathNotice re-checks liveness inside its
|
|
17
|
-
// transaction, so this gate cannot misfire.
|
|
18
|
-
const controller = getRow(controllerId);
|
|
19
|
-
if (controller !== null && (controller.status === 'active' || controller.status === 'idle'))
|
|
20
|
-
continue;
|
|
6
|
+
void now;
|
|
7
|
+
void ctx;
|
|
8
|
+
for (const wait of liveControllerWaits()) {
|
|
21
9
|
operationIdContext.fresh(() => {
|
|
22
|
-
|
|
10
|
+
enqueueControllerDeathNotice(wait.nodeId, wait.controllerId);
|
|
23
11
|
});
|
|
24
12
|
}
|
|
25
13
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DetachedWorkLifecycle } from './broker-supervision.js';
|
|
2
|
+
export declare const deliveryDaemonInstanceId: `${string}-${string}-${string}-${string}-${string}`;
|
|
3
|
+
export interface DeliveryLaneContext {
|
|
4
|
+
lifecycle: DetachedWorkLifecycle;
|
|
5
|
+
}
|
|
6
|
+
/** The one daemon lane for every configured external action. Node-inbox rows are claimed by their destination. */
|
|
7
|
+
export declare class DeliveryLaneReconciler {
|
|
8
|
+
run(now: number, ctx: DeliveryLaneContext): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { rmSync } from 'node:fs';
|
|
3
|
+
import { claimExternalActionDelivery, dueExternalActionDeliveries, messageBodyForDelivery, settleDelivery } from '../../core/canvas/deliveries.js';
|
|
4
|
+
import { getNode } from '../../core/canvas/canvas.js';
|
|
5
|
+
import { emitEvent } from '../../core/events/emit.js';
|
|
6
|
+
import { operationIdContext } from '../../core/events/operation-id.js';
|
|
7
|
+
import { createPageRequest, getRequest } from '../../core/human/requests.js';
|
|
8
|
+
import { synthesizedPageDocument, writeSynthesizedPage } from '../../core/human/page-synth.js';
|
|
9
|
+
import { preparePage } from '../../core/human/page-authoring.js';
|
|
10
|
+
import { resolvePageComponents } from '../../core/config.js';
|
|
11
|
+
import { spawnChild } from '../../core/runtime/spawn.js';
|
|
12
|
+
import { runDelivery } from '../delivery/run-delivery.js';
|
|
13
|
+
export const deliveryDaemonInstanceId = randomUUID();
|
|
14
|
+
function adapterFor(delivery) {
|
|
15
|
+
if (delivery.adapter === null || typeof delivery.adapter['adapter'] !== 'string') {
|
|
16
|
+
throw new Error(`delivery ${delivery.deliveryId} has no adapter`);
|
|
17
|
+
}
|
|
18
|
+
return delivery.adapter;
|
|
19
|
+
}
|
|
20
|
+
function failed(delivery, owner, message) {
|
|
21
|
+
settleDelivery(delivery.deliveryId, delivery.attempts, owner, {
|
|
22
|
+
state: 'permanent_failed', failure: { kind: 'spawn_error', message },
|
|
23
|
+
}, Date.now());
|
|
24
|
+
}
|
|
25
|
+
async function deliverSpawn(delivery, owner, adapter) {
|
|
26
|
+
const existing = getNode(adapter.nodeId);
|
|
27
|
+
if (existing !== null) {
|
|
28
|
+
if (existing.frozen_at !== null || existing.pi_session_id !== null || existing.status === 'idle' || existing.status === 'done' || existing.status === 'canceled') {
|
|
29
|
+
settleDelivery(delivery.deliveryId, delivery.attempts, owner, { state: 'acknowledged' }, Date.now());
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
failed(delivery, owner, existing.status === 'dead'
|
|
33
|
+
? `cron spawn ${adapter.nodeId} recorded a failed birth`
|
|
34
|
+
: `cron spawn ${adapter.nodeId} found an incomplete birth`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
await spawnChild({
|
|
39
|
+
nodeId: adapter.nodeId, kind: adapter.kind, cwd: adapter.cwd, prompt: adapter.prompt,
|
|
40
|
+
...(adapter.root ? { root: true, rootLifecycle: adapter.rootLifecycle } : { parent: adapter.parent }),
|
|
41
|
+
readiness: 'launch', profile: adapter.profile, wakeOrigin: adapter.wakeOrigin,
|
|
42
|
+
});
|
|
43
|
+
settleDelivery(delivery.deliveryId, delivery.attempts, owner, { state: 'acknowledged' }, Date.now());
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
failed(delivery, owner, `cron spawn ${adapter.nodeId} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
47
|
+
operationIdContext.fresh(() => emitEvent({ level: 'error', event: 'delivery.cron_spawn_failed', node_id: adapter.nodeId, error, fields: { delivery_id: delivery.deliveryId } }));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function preparedCronPage(adapter, stdout) {
|
|
51
|
+
const synthesized = writeSynthesizedPage(synthesizedPageDocument(adapter.title, adapter.summary, stdout, false));
|
|
52
|
+
try {
|
|
53
|
+
return preparePage({
|
|
54
|
+
sourceFile: synthesized.path,
|
|
55
|
+
source: { sessionName: adapter.sessionName, ...(adapter.profileName === null ? {} : { profileName: adapter.profileName }) },
|
|
56
|
+
productKinds: resolvePageComponents(),
|
|
57
|
+
delivery: { placement: 'inline', inbox: true, reply: false },
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
rmSync(synthesized.dir, { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function deliverHuman(delivery, owner, adapter, body) {
|
|
65
|
+
if (getRequest(adapter.requestId) !== null) {
|
|
66
|
+
settleDelivery(delivery.deliveryId, delivery.attempts, owner, { state: 'acknowledged' }, Date.now());
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const stdout = typeof body?.['stdout'] === 'string'
|
|
70
|
+
? body['stdout']
|
|
71
|
+
: null;
|
|
72
|
+
if (stdout === null) {
|
|
73
|
+
failed(delivery, owner, `cron human ${adapter.requestId} has no frozen stdout`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
createPageRequest({ request_id: adapter.requestId, prepared: preparedCronPage(adapter, stdout) });
|
|
78
|
+
settleDelivery(delivery.deliveryId, delivery.attempts, owner, { state: 'acknowledged' }, Date.now());
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
failed(delivery, owner, `cron human ${adapter.requestId} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function deliverExternal(delivery, owner) {
|
|
85
|
+
const adapter = adapterFor(delivery);
|
|
86
|
+
const body = messageBodyForDelivery(delivery.deliveryId);
|
|
87
|
+
if (body === null) {
|
|
88
|
+
failed(delivery, owner, `delivery ${delivery.deliveryId} has no message body`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (adapter.adapter === 'subprocess') {
|
|
92
|
+
await runDelivery({ argv: adapter.argv, cwd: adapter.cwd, document: body, attempt: delivery.attempts }, (settlement) => {
|
|
93
|
+
settleDelivery(delivery.deliveryId, delivery.attempts, owner, settlement, Date.now());
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (adapter.adapter === 'cron_spawn') {
|
|
98
|
+
await deliverSpawn(delivery, owner, adapter);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
deliverHuman(delivery, owner, adapter, body);
|
|
102
|
+
}
|
|
103
|
+
/** The one daemon lane for every configured external action. Node-inbox rows are claimed by their destination. */
|
|
104
|
+
export class DeliveryLaneReconciler {
|
|
105
|
+
run(now, ctx) {
|
|
106
|
+
if (!ctx.lifecycle.acceptsDetachedWork())
|
|
107
|
+
return;
|
|
108
|
+
let due;
|
|
109
|
+
try {
|
|
110
|
+
due = dueExternalActionDeliveries(now);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
operationIdContext.fresh(() => emitEvent({ level: 'error', event: 'delivery.scan_failed', error }));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
for (const candidate of due) {
|
|
117
|
+
try {
|
|
118
|
+
const claimed = claimExternalActionDelivery(candidate.deliveryId, now, deliveryDaemonInstanceId);
|
|
119
|
+
if (claimed === null)
|
|
120
|
+
continue;
|
|
121
|
+
ctx.lifecycle.registerDetached(deliverExternal(claimed, deliveryDaemonInstanceId).catch((error) => {
|
|
122
|
+
operationIdContext.fresh(() => emitEvent({ level: 'error', event: 'delivery.failed', error, fields: { delivery_id: claimed.deliveryId } }));
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
operationIdContext.fresh(() => emitEvent({ level: 'error', event: 'delivery.claim_failed', error, fields: { delivery_id: candidate.deliveryId } }));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { newestReportRef, hasActiveLiveSubscription, hasLiveMessageWait, hasLiveSubscriber, hasPendingCronWake, subscribersOf, } from '../../core/canvas/index.js';
|
|
2
2
|
import { fullName } from '../../core/canvas/labels.js';
|
|
3
3
|
import { countTickets } from '../../core/canvas/attention.js';
|
|
4
|
-
import { hasUnseenWakeMail
|
|
4
|
+
import { hasUnseenWakeMail } from '../../core/canvas/deliveries.js';
|
|
5
5
|
import { readOutputRequest } from '../../core/runtime/structured-output.js';
|
|
6
6
|
import { focusOf } from '../../core/runtime/placement.js';
|
|
7
7
|
import { hasAttachedBrokerViewers } from '../../core/runtime/broker/client-registry.js';
|
|
8
8
|
import { fanDoctrineWake } from '../../core/runtime/close.js';
|
|
9
|
-
import { readFault } from '../../core/
|
|
9
|
+
import { readFault } from '../../core/canvas/recovery.js';
|
|
10
10
|
import { transition } from '../../core/runtime/lifecycle.js';
|
|
11
11
|
/** Durable work that requires a node to remain automatically wakeable. */
|
|
12
12
|
export function hasLiveObligation(nodeId) {
|
|
@@ -56,7 +56,7 @@ export function parkEvent(nodeId, lifecycle) {
|
|
|
56
56
|
return 'release';
|
|
57
57
|
if (lifecycle === 'resident')
|
|
58
58
|
return hasUnseenWakeMail(nodeId) ? 'release' : 'park';
|
|
59
|
-
if (
|
|
59
|
+
if (hasUnseenWakeMail(nodeId))
|
|
60
60
|
return 'release';
|
|
61
61
|
return hasLiveSubscriber(nodeId) ? 'release' : 'cancel';
|
|
62
62
|
}
|
|
@@ -4,7 +4,7 @@ import { isProfilePaused } from '../../core/profiles/manifest.js';
|
|
|
4
4
|
import { operationIdContext } from '../../core/events/operation-id.js';
|
|
5
5
|
import { errorClassFromFault } from '../../core/fault-classifier.js';
|
|
6
6
|
import { fanDoctrineWake } from '../../core/runtime/close.js';
|
|
7
|
-
import { readFault } from '../../core/
|
|
7
|
+
import { readFault } from '../../core/canvas/recovery.js';
|
|
8
8
|
import { requestBrokerTeardown } from '../../core/runtime/host.js';
|
|
9
9
|
import { transition } from '../../core/runtime/lifecycle.js';
|
|
10
10
|
/** Enforce absolute spawn deadlines independently of the tick's live-row scan. */
|
|
@@ -39,7 +39,7 @@ export declare function compareThaw(a: ThawEntry, b: ThawEntry): number;
|
|
|
39
39
|
export declare class FreezeLaneReconciler {
|
|
40
40
|
private readonly relaunchState;
|
|
41
41
|
/** End a stuck-delivery episode the tick decided is over — the node's profile
|
|
42
|
-
*
|
|
42
|
+
* paused, or it has no wake-worthy mail left. */
|
|
43
43
|
endEpisode(nodeId: string): void;
|
|
44
44
|
run(now: number, ctx: FreezeLaneContext): Promise<FrozenCounts>;
|
|
45
45
|
private enact;
|
|
@@ -13,16 +13,14 @@ import { fullName } from '../../../core/canvas/labels.js';
|
|
|
13
13
|
import { isSafeNodeId } from '../../../core/canvas/paths.js';
|
|
14
14
|
import { emitEvent } from '../../../core/events/emit.js';
|
|
15
15
|
import { operationIdContext } from '../../../core/events/operation-id.js';
|
|
16
|
-
import {
|
|
16
|
+
import { acknowledgedNodeMailCount } from '../../../core/canvas/deliveries.js';
|
|
17
17
|
import { fanDoctrineWake } from '../../../core/runtime/close.js';
|
|
18
18
|
import { transition } from '../../../core/runtime/lifecycle.js';
|
|
19
19
|
import { reviveNode } from '../../../core/runtime/revive.js';
|
|
20
20
|
import { hasPendingWake } from '../live-obligation.js';
|
|
21
|
-
// Idle-release
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// is keyed on inbox-cursor progress, not row-state residency or pid liveness: a
|
|
25
|
-
// transiently-live broker does not prove that delivery completed.
|
|
21
|
+
// Idle-release delivery retry cap. The counter is keyed on acknowledged
|
|
22
|
+
// delivery progress, not row-state residency or pid liveness: a transiently
|
|
23
|
+
// live broker does not prove that a destination received its mail.
|
|
26
24
|
const IDLE_RELEASE_RELAUNCH_MAX_ATTEMPTS = 6;
|
|
27
25
|
const IDLE_RELEASE_RELAUNCH_MAX_BACKOFF_MS = 10 * 60_000;
|
|
28
26
|
const IDLE_RELEASE_RELAUNCH_BASE_MS = 20_000;
|
|
@@ -57,7 +55,7 @@ export function compareThaw(a, b) {
|
|
|
57
55
|
export class FreezeLaneReconciler {
|
|
58
56
|
relaunchState = new Map();
|
|
59
57
|
/** End a stuck-delivery episode the tick decided is over — the node's profile
|
|
60
|
-
*
|
|
58
|
+
* paused, or it has no wake-worthy mail left. */
|
|
61
59
|
endEpisode(nodeId) {
|
|
62
60
|
this.relaunchState.delete(nodeId);
|
|
63
61
|
}
|
|
@@ -118,10 +116,10 @@ export class FreezeLaneReconciler {
|
|
|
118
116
|
}
|
|
119
117
|
async enactIdleRelease(now, row) {
|
|
120
118
|
const id = row.node_id;
|
|
121
|
-
const
|
|
119
|
+
const acknowledgedCount = acknowledgedNodeMailCount(id);
|
|
122
120
|
const prior = this.relaunchState.get(id);
|
|
123
|
-
const
|
|
124
|
-
const attempts =
|
|
121
|
+
const deliveryProgressed = prior === undefined || prior.acknowledgedCount !== acknowledgedCount;
|
|
122
|
+
const attempts = deliveryProgressed ? 0 : prior.attempts;
|
|
125
123
|
if (attempts >= IDLE_RELEASE_RELAUNCH_MAX_ATTEMPTS) {
|
|
126
124
|
this.relaunchState.delete(id);
|
|
127
125
|
emitEvent({
|
|
@@ -148,7 +146,7 @@ export class FreezeLaneReconciler {
|
|
|
148
146
|
`dead until you revive it — \`crtr node lifecycle revive ${id}\`.`, { reason: 'child-crashed', child: id });
|
|
149
147
|
return 'skipped';
|
|
150
148
|
}
|
|
151
|
-
const lastAttempt =
|
|
149
|
+
const lastAttempt = deliveryProgressed ? undefined : prior?.lastAttempt;
|
|
152
150
|
const backoff = Math.min(IDLE_RELEASE_RELAUNCH_BASE_MS * 2 ** attempts, IDLE_RELEASE_RELAUNCH_MAX_BACKOFF_MS);
|
|
153
151
|
if (lastAttempt !== undefined && now - lastAttempt < backoff)
|
|
154
152
|
return 'skipped';
|
|
@@ -157,7 +155,7 @@ export class FreezeLaneReconciler {
|
|
|
157
155
|
// actually happened advances the episode toward exhaustion.
|
|
158
156
|
if ((await reviveNode(id, { resume: true, capacity: 'freeze' })).outcome === 'frozen')
|
|
159
157
|
return 'frozen';
|
|
160
|
-
this.relaunchState.set(id, {
|
|
158
|
+
this.relaunchState.set(id, { acknowledgedCount, attempts: attempts + 1, lastAttempt: now });
|
|
161
159
|
emitEvent({
|
|
162
160
|
level: 'info',
|
|
163
161
|
event: 'broker.revive.idle_release',
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
// to the freeze lane, which orders them by tier and spends the free slots in
|
|
8
8
|
// that order. Splitting the phases is what makes the tier order mean anything —
|
|
9
9
|
// a single interleaved pass would give slots out in row-creation order.
|
|
10
|
-
import { clearFrozen,
|
|
10
|
+
import { clearFrozen, endLiveExecution, getNode, } from '../../../core/canvas/index.js';
|
|
11
11
|
import { isSafeNodeId } from '../../../core/canvas/paths.js';
|
|
12
12
|
import { recordedPidLiveness } from '../../../core/canvas/pid.js';
|
|
13
13
|
import { emitEvent } from '../../../core/events/emit.js';
|
|
14
14
|
import { operationIdContext } from '../../../core/events/operation-id.js';
|
|
15
|
-
import {
|
|
15
|
+
import { readInterruptedTurn } from '../../../core/canvas/recovery.js';
|
|
16
16
|
import { isProfilePaused } from '../../../core/profiles/manifest.js';
|
|
17
17
|
import { classifyDeadNode } from '../../fleet.js';
|
|
18
18
|
import { applyParkEvent, hasPendingWake, parkEvent } from '../live-obligation.js';
|
|
@@ -51,7 +51,7 @@ export class NodeLifecycleReconciler {
|
|
|
51
51
|
if (handleTerminatingRow(row, ctx.fleet.has(id)))
|
|
52
52
|
return;
|
|
53
53
|
if (row.pi_pid != null && recordedPidLiveness(row.pi_pid, row.pi_pid_identity) === 'dead') {
|
|
54
|
-
|
|
54
|
+
endLiveExecution(id, { reason: 'observed-dead' });
|
|
55
55
|
}
|
|
56
56
|
if (row.frozen_at != null)
|
|
57
57
|
candidates.push({ row, enact: 'thaw' });
|
|
@@ -114,11 +114,11 @@ export class NodeLifecycleReconciler {
|
|
|
114
114
|
// its marker without failure or launch, and retaining the PID lets the
|
|
115
115
|
// second recognize the same dead row for ordinary recovery.
|
|
116
116
|
if (!ctx.fleet.hasProfilePauseInterruption(id))
|
|
117
|
-
|
|
117
|
+
endLiveExecution(id, { reason: 'observed-dead' });
|
|
118
118
|
// 7. The invariant-5 policy table, on the same inputs an exit event gives
|
|
119
119
|
// it. 8. A verdict that needs no broker is enacted right here, because it
|
|
120
120
|
// costs no slot; a respawn becomes a candidate for the freeze lane.
|
|
121
|
-
const action = classifyDeadNode(getNode(id),
|
|
121
|
+
const action = classifyDeadNode(getNode(id), readInterruptedTurn(id)?.interrupted === true, hasPendingWake(id));
|
|
122
122
|
if (action === 'respawn-fresh' || action === 'respawn-cycle' || action === 'respawn-resume') {
|
|
123
123
|
candidates.push({ row, enact: 'policy' });
|
|
124
124
|
return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasUnseenWakeMail } from '../../../core/
|
|
1
|
+
import { hasUnseenWakeMail } from '../../../core/canvas/deliveries.js';
|
|
2
2
|
/** Whether the lifecycle tick must ensure this row has an engine. The durable
|
|
3
3
|
* message handler calls this after appending its entry: that append makes the
|
|
4
4
|
* idle branch's unseen-wake-mail check true for a wake-capable entry. */
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// never coming.
|
|
9
9
|
import { emitEvent } from '../../core/events/emit.js';
|
|
10
10
|
import { operationIdContext } from '../../core/events/operation-id.js';
|
|
11
|
-
import {
|
|
11
|
+
import { listSubmitRequestedRequests } from '../../core/human/requests.js';
|
|
12
12
|
import { completeRequestedSubmit } from '../review/finish.js';
|
|
13
13
|
export class PendingReviewSubmitReconciler {
|
|
14
14
|
run(now, ctx) {
|
|
@@ -17,7 +17,7 @@ export class PendingReviewSubmitReconciler {
|
|
|
17
17
|
return;
|
|
18
18
|
let pending;
|
|
19
19
|
try {
|
|
20
|
-
pending =
|
|
20
|
+
pending = listSubmitRequestedRequests();
|
|
21
21
|
}
|
|
22
22
|
catch (error) {
|
|
23
23
|
operationIdContext.fresh(() => {
|
|
@@ -26,7 +26,7 @@ export class PendingReviewSubmitReconciler {
|
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
for (const review of pending) {
|
|
29
|
-
ctx.lifecycle.registerDetached(completeRequestedSubmit(review.
|
|
29
|
+
ctx.lifecycle.registerDetached(completeRequestedSubmit(review.request_id, { atCompanionTurnEnd: false }).then(() => { }, (error) => {
|
|
30
30
|
operationIdContext.fresh(() => {
|
|
31
31
|
emitEvent({ level: 'error', event: 'review.deferred_submit.failed', error });
|
|
32
32
|
});
|
|
@@ -12,17 +12,11 @@ export interface StorageMaintenanceContext {
|
|
|
12
12
|
* managed-worktree reconciliation gets its start-up pass. */
|
|
13
13
|
export declare class StorageMaintenanceReconciler {
|
|
14
14
|
private lastSpareSweepAt;
|
|
15
|
-
private lastGhostSweepAt;
|
|
16
15
|
private focusGcInFlight;
|
|
17
16
|
private readonly worktreeSweep;
|
|
18
17
|
run(now: number, ctx: StorageMaintenanceContext): void;
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
* so it sits in the roster jamming the surfaces that try. One readdir per sweep. */
|
|
22
|
-
private reapGhostRows;
|
|
23
|
-
/** Milliseconds since meta/telemetry was last written. Inbox activity is
|
|
24
|
-
* deliberately excluded: children can keep writing a dead manager's inbox. */
|
|
25
|
-
private msSinceDiskActivity;
|
|
18
|
+
/** Milliseconds since terminal state, launch/settlement, or disposable telemetry changed. Inbox activity is deliberately excluded: children can keep writing a dead manager's inbox. */
|
|
19
|
+
private msSinceTerminalActivity;
|
|
26
20
|
/** Reap only physical placement residue of terminal nodes. The durable row,
|
|
27
21
|
* graph, and on-disk state remain available for a later manual revive. */
|
|
28
22
|
private reapDeadResidue;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { statSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { closeFocusRow, getFocusById, listFocuses, listNodes,
|
|
4
|
-
import { isSafeNodeId, jobDir
|
|
3
|
+
import { closeFocusRow, getFocusById, listFocuses, listNodes, } from '../../core/canvas/index.js';
|
|
4
|
+
import { isSafeNodeId, jobDir } from '../../core/canvas/paths.js';
|
|
5
5
|
import { emitEvent } from '../../core/events/emit.js';
|
|
6
6
|
import { operationIdContext } from '../../core/events/operation-id.js';
|
|
7
7
|
import { listLivePanesAsync, tearDownNode } from '../../core/runtime/placement.js';
|
|
@@ -11,13 +11,11 @@ import { ManagedWorktreeSweepReconciler } from './managed-worktree-sweep.js';
|
|
|
11
11
|
// placement is reaped. A fresh crash keeps its pane for inspection.
|
|
12
12
|
export const DEAD_REAP_GRACE_MS = 10 * 60_000;
|
|
13
13
|
const SPARE_SWEEP_INTERVAL_MS = 60 * 1000;
|
|
14
|
-
const GHOST_SWEEP_INTERVAL_MS = 60 * 1000;
|
|
15
14
|
/** Recurring storage cleanup. The in-memory throttle clocks intentionally reset
|
|
16
15
|
* on daemon boot, so the first tick runs every full sweep — which is also how
|
|
17
16
|
* managed-worktree reconciliation gets its start-up pass. */
|
|
18
17
|
export class StorageMaintenanceReconciler {
|
|
19
18
|
lastSpareSweepAt = Number.NEGATIVE_INFINITY;
|
|
20
|
-
lastGhostSweepAt = Number.NEGATIVE_INFINITY;
|
|
21
19
|
focusGcInFlight = false;
|
|
22
20
|
worktreeSweep = new ManagedWorktreeSweepReconciler();
|
|
23
21
|
run(now, ctx) {
|
|
@@ -28,49 +26,21 @@ export class StorageMaintenanceReconciler {
|
|
|
28
26
|
// observes the post-reap state this ordering exists to give it.
|
|
29
27
|
this.reapDeadResidue(now);
|
|
30
28
|
this.scheduleFocusGc(ctx);
|
|
31
|
-
this.reapGhostRows(now);
|
|
32
29
|
this.gcWarmPool(now);
|
|
33
30
|
// Registers detached async Git work and returns immediately: this lane is
|
|
34
31
|
// called un-awaited from a 2-second supervision tick and must not block it.
|
|
35
32
|
this.worktreeSweep.run(now, ctx);
|
|
36
33
|
}
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (reaped.length > 0) {
|
|
48
|
-
emitEvent({
|
|
49
|
-
level: 'info',
|
|
50
|
-
event: 'canvas.ghost_rows.reaped',
|
|
51
|
-
outcome: 'succeeded',
|
|
52
|
-
fields: { reaped_count: reaped.length, reaped_ids: reaped },
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch (err) {
|
|
57
|
-
emitEvent({ level: 'error', event: 'canvas.ghost_rows.reap_failed', error: err });
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
/** Milliseconds since meta/telemetry was last written. Inbox activity is
|
|
62
|
-
* deliberately excluded: children can keep writing a dead manager's inbox. */
|
|
63
|
-
msSinceDiskActivity(nodeId, now) {
|
|
64
|
-
let latest = 0;
|
|
65
|
-
for (const path of [join(nodeDir(nodeId), 'meta.json'), join(jobDir(nodeId), 'telemetry.json')]) {
|
|
66
|
-
try {
|
|
67
|
-
const mtime = statSync(path).mtimeMs;
|
|
68
|
-
if (mtime > latest)
|
|
69
|
-
latest = mtime;
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
/* absent — contributes nothing */
|
|
73
|
-
}
|
|
34
|
+
/** Milliseconds since terminal state, launch/settlement, or disposable telemetry changed. Inbox activity is deliberately excluded: children can keep writing a dead manager's inbox. */
|
|
35
|
+
msSinceTerminalActivity(row, now) {
|
|
36
|
+
const timestamps = [row.created, row.launched_at, row.last_settled_at, row.finalized_at, row.outcome_at]
|
|
37
|
+
.map((value) => value === null || value === undefined ? Number.NaN : Date.parse(value));
|
|
38
|
+
let latest = Math.max(0, ...timestamps.filter(Number.isFinite));
|
|
39
|
+
try {
|
|
40
|
+
latest = Math.max(latest, statSync(join(jobDir(row.node_id), 'telemetry.json')).mtimeMs);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
/* telemetry is disposable */
|
|
74
44
|
}
|
|
75
45
|
return latest === 0 ? Number.POSITIVE_INFINITY : now - latest;
|
|
76
46
|
}
|
|
@@ -91,7 +61,7 @@ export class StorageMaintenanceReconciler {
|
|
|
91
61
|
if (row.pane == null && row.window == null && row.tmux_session == null)
|
|
92
62
|
continue;
|
|
93
63
|
try {
|
|
94
|
-
if (this.
|
|
64
|
+
if (this.msSinceTerminalActivity(row, now) < DEAD_REAP_GRACE_MS)
|
|
95
65
|
continue;
|
|
96
66
|
}
|
|
97
67
|
catch (err) {
|
|
@@ -3,25 +3,19 @@
|
|
|
3
3
|
// Two facts, both already maintained by the runtime, and neither one enough
|
|
4
4
|
// alone:
|
|
5
5
|
//
|
|
6
|
-
// - MID-TURN — the
|
|
7
|
-
//
|
|
8
|
-
// marker from a crashed broker reads false through the AND.
|
|
6
|
+
// - MID-TURN — the recovery store's live-turn witness. A cleared turn reads
|
|
7
|
+
// false without consulting a node-local runtime file.
|
|
9
8
|
// - QUEUED — an undelivered non-deferred inbox entry. Comments are pushed to
|
|
10
9
|
// the companion as inbox messages, so the ordinary rhythm is: the human
|
|
11
10
|
// comments, the entry lands, the companion's turn starts a moment later.
|
|
12
11
|
// Between those two moments the busy marker is cold while the companion is
|
|
13
12
|
// unmistakably about to work, and submitting into that gap kills the edit
|
|
14
13
|
// the wait exists to protect.
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { hasUnseenWakeMail } from '../../core/feed/inbox.js';
|
|
18
|
-
import { isBusy } from '../../core/runtime/busy.js';
|
|
14
|
+
import { readLiveTurn } from '../../core/canvas/recovery.js';
|
|
15
|
+
import { hasUnseenWakeMail } from '../../core/canvas/deliveries.js';
|
|
19
16
|
/** Is the companion inside a turn right now? */
|
|
20
17
|
export function companionMidTurn(nodeId) {
|
|
21
|
-
|
|
22
|
-
if (node === null || !isPidAlive(node.pi_pid))
|
|
23
|
-
return false;
|
|
24
|
-
return isBusy(nodeId);
|
|
18
|
+
return readLiveTurn(nodeId)?.busy ?? false;
|
|
25
19
|
}
|
|
26
20
|
/** Does the companion hold work it has not yet consumed? A deferred entry is
|
|
27
21
|
* excluded for the same reason the settlement policy excludes it: it never
|