@north-light/crouter 0.3.232 → 0.3.234
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.js +16 -15
- package/dist/builtin-memory/internal/memory-loading.md +4 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +14 -17
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -11
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +8 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +10 -11
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/pi-shell-runner.ts +2 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +3 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +7 -7
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +8 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +6 -6
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/sysprompt-window.ts +3 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +1 -1
- package/dist/clients/attach/chrome/ticket-panel.js +15 -2
- package/dist/clients/attach/overlays/graph.js +0 -3
- package/dist/clients/attach/overlays/mcp.js +1 -2
- package/dist/clients/attach/render/chat-view.js +0 -12
- package/dist/clients/attach/render/crtr-output.js +0 -2
- package/dist/clients/attach/render/tool-calls.js +0 -6
- package/dist/clients/attach/slash/dispatch.js +8 -11
- package/dist/clients/attach/viewer.js +532 -534
- package/dist/clients/conversation/projection.js +0 -2
- package/dist/clients/inbox/controller.js +22 -3
- package/dist/clients/inbox/review/companion-pane.js +0 -4
- package/dist/clients/inbox/review-adapter.js +0 -7
- package/dist/clients/inbox/tui/slots.d.ts +3 -3
- package/dist/clients/inbox/tui/types.d.ts +2 -2
- package/dist/commands/api-client.js +0 -8
- package/dist/commands/attention.js +0 -8
- package/dist/commands/canvas-browse.js +2 -1
- package/dist/commands/canvas-config.js +0 -10
- package/dist/commands/canvas-history.js +2 -1
- package/dist/commands/cron.js +5 -18
- package/dist/commands/dashboard.js +0 -2
- package/dist/commands/human/feedback.js +2 -1
- package/dist/commands/human/prompts.js +2 -5
- package/dist/commands/human/queue.js +2 -7
- package/dist/commands/human/request.js +0 -14
- package/dist/commands/human/review.js +2 -1
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +4 -4
- package/dist/commands/memory/find.js +2 -2
- package/dist/commands/memory/lint.js +2 -1
- package/dist/commands/memory/read.d.ts +2 -7
- package/dist/commands/memory/read.js +5 -16
- package/dist/commands/memory/shared.d.ts +1 -1
- package/dist/commands/memory/shared.js +7 -10
- package/dist/commands/memory/write.js +4 -3
- package/dist/commands/node/bash.js +2 -1
- package/dist/commands/node/create.js +2 -1
- package/dist/commands/node/inspect.js +0 -6
- package/dist/commands/node/lifecycle.js +4 -13
- package/dist/commands/node/message.js +3 -2
- package/dist/commands/node/subscription.js +2 -1
- package/dist/commands/node/wait.js +4 -3
- package/dist/commands/node-context.js +2 -1
- package/dist/commands/node-lifecycle-revive.js +0 -2
- package/dist/commands/node-worktree.js +2 -1
- package/dist/commands/pkg/browse/actions.d.ts +1 -1
- package/dist/commands/pkg/browse/actions.js +4 -4
- package/dist/commands/pkg/browse/catalog.d.ts +1 -1
- package/dist/commands/pkg/browse/catalog.js +15 -15
- package/dist/commands/pkg/browse/command-view.d.ts +1 -1
- package/dist/commands/pkg/browse/command-view.js +3 -3
- package/dist/commands/pkg/browse/detail-panel.d.ts +1 -1
- package/dist/commands/pkg/browse/detail-panel.js +8 -8
- package/dist/commands/pkg/browse/doc-view.d.ts +1 -1
- package/dist/commands/pkg/browse/doc-view.js +3 -3
- package/dist/commands/pkg/browse/list-panel.d.ts +1 -1
- package/dist/commands/pkg/browse/list-panel.js +3 -3
- package/dist/commands/pkg/browse/model.d.ts +3 -3
- package/dist/commands/pkg/browse/shell.d.ts +2 -2
- package/dist/commands/pkg/browse/shell.js +13 -13
- package/dist/commands/pkg/plugin-manage.js +2 -1
- package/dist/commands/pkg/shared.js +0 -2
- package/dist/commands/profile/delete.js +2 -1
- package/dist/commands/push.js +2 -3
- package/dist/commands/revive.js +0 -2
- package/dist/commands/surface/node/placement.js +2 -1
- package/dist/commands/surface-edit.js +2 -1
- package/dist/commands/sys/config.js +0 -4
- package/dist/commands/sys/daemon.js +0 -10
- package/dist/commands/sys/doctor.js +3 -2
- package/dist/commands/sys/panels/broker-limits-panel.d.ts +2 -2
- package/dist/commands/sys/panels/broker-limits-panel.js +4 -4
- package/dist/commands/sys/panels/dependencies-panel.d.ts +2 -2
- package/dist/commands/sys/panels/dependencies-panel.js +2 -2
- package/dist/commands/sys/panels/keybindings-panel.d.ts +1 -1
- package/dist/commands/sys/panels/keybindings-panel.js +5 -5
- package/dist/commands/sys/panels/models-panel.d.ts +4 -4
- package/dist/commands/sys/panels/models-panel.js +11 -10
- package/dist/commands/sys/panels/plugins-panel.d.ts +2 -2
- package/dist/commands/sys/panels/plugins-panel.js +2 -2
- package/dist/commands/sys/panels/prefs-panel.d.ts +2 -2
- package/dist/commands/sys/panels/prefs-panel.js +4 -4
- package/dist/commands/sys/panels/profiles-panel.d.ts +1 -1
- package/dist/commands/sys/panels/profiles-panel.js +14 -13
- package/dist/commands/sys/panels/provider-panel.d.ts +3 -3
- package/dist/commands/sys/panels/provider-panel.js +8 -8
- package/dist/commands/sys/settings-shell.d.ts +3 -3
- package/dist/commands/sys/settings-shell.js +15 -15
- package/dist/commands/sys/setup-settings-runner.d.ts +3 -3
- package/dist/commands/sys/setup-settings-runner.js +5 -5
- package/dist/commands/sys/setup-wizard.d.ts +4 -4
- package/dist/commands/sys/setup-wizard.js +8 -8
- package/dist/commands/sys/support/prepare.js +4 -4
- package/dist/commands/sys/support/submit.js +2 -2
- package/dist/commands/sys/sync-deps.js +0 -2
- package/dist/commands/sys/sync-project-guidance.js +2 -1
- package/dist/commands/sys/sysprompt.js +2 -1
- package/dist/core/__tests__/boot.test.js +0 -8
- package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +23 -1
- package/dist/core/__tests__/canvas-db-boundary.test.d.ts +1 -0
- package/dist/core/__tests__/canvas-db-boundary.test.js +46 -0
- package/dist/core/__tests__/canvas.test.js +1 -1
- package/dist/core/__tests__/child-death-wake.test.js +0 -2
- package/dist/core/__tests__/cron-armed-context-failure.test.d.ts +1 -0
- package/dist/core/__tests__/cron-armed-context-failure.test.js +145 -0
- package/dist/core/__tests__/daemon-wedge.test.js +0 -4
- package/dist/core/__tests__/dead-node-policy-table.test.js +0 -4
- package/dist/core/__tests__/fixtures/fake-engine.js +4 -14
- package/dist/core/__tests__/fixtures/fake-pi-host.js +5 -5
- package/dist/core/__tests__/focuses.test.js +0 -6
- package/dist/core/__tests__/fs-utils.test.d.ts +1 -0
- package/dist/core/__tests__/fs-utils.test.js +50 -0
- package/dist/core/__tests__/helpers/harness.js +1 -1
- package/dist/core/__tests__/inline-memory-refs.test.js +0 -2
- package/dist/core/__tests__/integration/broker-fork-seam.test.js +0 -2
- package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +3 -16
- package/dist/core/__tests__/integration/command-plugins.test.js +0 -24
- package/dist/core/__tests__/integration/deferred-no-wake.test.js +0 -6
- package/dist/core/__tests__/integration/flagship-lifecycle.test.js +0 -26
- package/dist/core/__tests__/integration/live-mutation-verbs.test.js +0 -4
- package/dist/core/__tests__/integration/live-mutation.test.js +1 -5
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +1 -5
- package/dist/core/__tests__/integration/revive.test.js +0 -12
- package/dist/core/__tests__/integration/subscription-delivery.test.js +0 -6
- package/dist/core/__tests__/integration/tmux-surface.test.js +24 -8
- package/dist/core/__tests__/integration/worktree-reap.test.js +0 -6
- package/dist/core/__tests__/lifecycle.test.js +0 -18
- package/dist/core/__tests__/memory-resolver-precedence.test.js +1 -1
- package/dist/core/__tests__/migration.test.js +2 -2
- package/dist/core/__tests__/on-read-dedup-resume.test.js +3 -3
- package/dist/core/__tests__/pid-identity-match.test.js +1 -1
- package/dist/core/__tests__/plugin-kinds.test.js +1 -1
- package/dist/core/__tests__/plugin-page-components.test.js +1 -1
- package/dist/core/__tests__/preview-registry-sync.test.js +42 -1
- package/dist/core/__tests__/respawn-throttle.test.js +0 -4
- package/dist/core/__tests__/revive-all.test.js +5 -5
- package/dist/core/__tests__/revive-parked-fresh.test.js +62 -11
- package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +0 -8
- package/dist/core/__tests__/seam/broker-attach-stream.test.js +0 -8
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +0 -2
- package/dist/core/__tests__/tmux-driver-boundary.test.d.ts +1 -0
- package/dist/core/__tests__/tmux-driver-boundary.test.js +50 -0
- package/dist/core/activity-signal.d.ts +7 -0
- package/dist/core/activity-signal.js +38 -0
- package/dist/core/auto-update.js +2 -1
- package/dist/core/bootstrap.js +2 -1
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-transport.test.js +1 -1
- package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
- package/dist/core/canvas/browse/app.js +3 -2
- package/dist/core/canvas/browse/model.d.ts +1 -1
- package/dist/core/canvas/browse/model.js +0 -4
- package/dist/core/canvas/canvas.d.ts +22 -0
- package/dist/core/canvas/canvas.js +50 -18
- package/dist/core/canvas/crons.js +0 -6
- package/dist/core/canvas/db.d.ts +14 -20
- package/dist/core/canvas/db.js +30 -1332
- package/dist/core/canvas/focuses.js +0 -4
- package/dist/core/canvas/history.js +2 -1
- package/dist/core/canvas/migrations.d.ts +20 -0
- package/dist/core/canvas/migrations.js +1339 -0
- package/dist/core/canvas/nav-model.js +1 -11
- package/dist/core/canvas/nav-render.js +0 -8
- package/dist/core/canvas/remote-canvas-source.js +0 -2
- package/dist/core/canvas/render-source.js +31 -13
- package/dist/core/command-manifests/manifest.js +0 -10
- package/dist/core/command-manifests/schema.js +0 -4
- package/dist/core/command-plugins/transport/http-invoke.js +0 -14
- package/dist/core/command.js +0 -4
- package/dist/core/config.js +2 -1
- package/dist/core/feed/inbox.d.ts +10 -0
- package/dist/core/feed/inbox.js +18 -12
- package/dist/core/fs-utils.d.ts +9 -2
- package/dist/core/fs-utils.js +7 -15
- package/dist/core/help.js +0 -8
- package/dist/core/host-exports/export.d.ts +2 -2
- package/dist/core/host-exports/export.js +9 -12
- package/dist/core/human/answer.d.ts +2 -2
- package/dist/core/human/convention.d.ts +2 -5
- package/dist/core/human/convention.js +3 -13
- package/dist/core/human/page-schema.d.ts +2 -2
- package/dist/core/human/page-schema.js +2 -2
- package/dist/core/human/root.js +6 -23
- package/dist/core/human/scan.js +24 -7
- package/dist/core/human/tickets.d.ts +4 -1
- package/dist/core/human/tickets.js +20 -6
- package/dist/core/io.js +0 -12
- package/dist/core/memory/identity.js +0 -4
- package/dist/core/memory/inline-ref-inventory.js +3 -2
- package/dist/core/memory/lint.js +3 -6
- package/dist/core/memory-resolver.d.ts +5 -0
- package/dist/core/memory-resolver.js +14 -12
- package/dist/core/pagination.js +0 -2
- package/dist/core/preview-registry.d.ts +14 -0
- package/dist/core/preview-registry.js +2 -4
- package/dist/core/preview-result-path.js +2 -1
- package/dist/core/profiles/manifest.d.ts +37 -5
- package/dist/core/profiles/manifest.js +44 -24
- package/dist/core/profiles/select.js +0 -4
- package/dist/core/review/signal.js +4 -23
- package/dist/core/review/ticket-filter.d.ts +3 -3
- package/dist/core/review/ticket-filter.js +4 -9
- package/dist/core/runtime/__tests__/engine-route.test.js +1 -1
- package/dist/core/runtime/__tests__/spawn-env.test.js +0 -8
- package/dist/core/runtime/bearings-render.d.ts +56 -0
- package/dist/core/runtime/bearings-render.js +241 -0
- package/dist/core/runtime/bearings.d.ts +2 -17
- package/dist/core/runtime/bearings.js +12 -198
- package/dist/core/runtime/broker/auth-reload.d.ts +39 -0
- package/dist/core/runtime/broker/auth-reload.js +149 -0
- package/dist/core/runtime/broker/engine-drive.d.ts +26 -0
- package/dist/core/runtime/broker/engine-drive.js +257 -0
- package/dist/core/runtime/broker/engine-routing.d.ts +143 -0
- package/dist/core/runtime/broker/engine-routing.js +211 -0
- package/dist/core/runtime/broker/event-projection.js +3 -2
- package/dist/core/runtime/broker/fault-retry.js +5 -4
- package/dist/core/runtime/broker/frame-client.d.ts +38 -0
- package/dist/core/runtime/broker/frame-client.js +129 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +12 -189
- package/dist/core/runtime/broker/frame-dispatch.js +89 -1476
- package/dist/core/runtime/broker/frame-engine-control.d.ts +20 -0
- package/dist/core/runtime/broker/frame-engine-control.js +231 -0
- package/dist/core/runtime/broker/frame-memory-refs.d.ts +19 -0
- package/dist/core/runtime/broker/frame-memory-refs.js +41 -0
- package/dist/core/runtime/broker/frame-model-query.d.ts +13 -0
- package/dist/core/runtime/broker/frame-model-query.js +69 -0
- package/dist/core/runtime/broker/frame-read.d.ts +19 -0
- package/dist/core/runtime/broker/frame-read.js +116 -0
- package/dist/core/runtime/broker/frame-replies.d.ts +24 -0
- package/dist/core/runtime/broker/frame-replies.js +64 -0
- package/dist/core/runtime/broker/frame-session.d.ts +34 -0
- package/dist/core/runtime/broker/frame-session.js +203 -0
- package/dist/core/runtime/broker/turn-ignition.d.ts +42 -0
- package/dist/core/runtime/broker/turn-ignition.js +74 -0
- package/dist/core/runtime/broker-extension-render.js +9 -85
- package/dist/core/runtime/broker-protocol.js +0 -4
- package/dist/core/runtime/broker-sdk.js +2 -1
- package/dist/core/runtime/broker.d.ts +1 -1
- package/dist/core/runtime/broker.js +5 -23
- package/dist/core/runtime/fault.js +3 -2
- package/dist/core/runtime/headless-pi.js +3 -2
- package/dist/core/runtime/host.js +2 -3
- package/dist/core/runtime/kickoff.js +0 -4
- package/dist/core/runtime/launch-target.js +2 -1
- package/dist/core/runtime/launch.js +3 -6
- package/dist/core/runtime/lifecycle.js +17 -26
- package/dist/core/runtime/managed-provider-cooling.d.ts +1 -1
- package/dist/core/runtime/naming.js +0 -2
- package/dist/core/runtime/nodes.js +2 -1
- package/dist/core/runtime/persona.js +0 -6
- package/dist/core/runtime/placement-tmux.js +2 -5
- package/dist/core/runtime/placement.js +4 -11
- package/dist/core/runtime/recap.js +4 -3
- package/dist/core/runtime/reopen.js +4 -7
- package/dist/core/runtime/reset.js +0 -4
- package/dist/core/runtime/resume-root.js +0 -2
- package/dist/core/runtime/revive.js +17 -13
- package/dist/core/runtime/session-list-cache.js +2 -2
- package/dist/core/runtime/shell-expansion.js +0 -2
- package/dist/core/runtime/spawn.js +0 -2
- package/dist/core/runtime/stream-watchdog.d.ts +0 -1
- package/dist/core/runtime/stream-watchdog.js +13 -18
- package/dist/core/runtime/tmux-bindings.js +0 -2
- package/dist/core/runtime/tmux-driver.d.ts +12 -0
- package/dist/core/runtime/tmux-driver.js +18 -13
- package/dist/core/runtime/warm-pool.js +0 -4
- package/dist/core/scope.js +4 -3
- package/dist/core/spawn.js +2 -3
- package/dist/core/subscription-state.js +10 -17
- package/dist/core/substrate/__tests__/surface-match-pre-command.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-pre-command.test.js +92 -0
- package/dist/core/substrate/frontmatter-validation.js +1 -1
- package/dist/core/substrate/injected-store.d.ts +6 -0
- package/dist/core/substrate/injected-store.js +24 -0
- package/dist/core/substrate/on-read.d.ts +17 -1
- package/dist/core/substrate/on-read.js +36 -2
- package/dist/core/substrate/render.js +0 -12
- package/dist/core/substrate/schema.d.ts +3 -3
- package/dist/core/substrate/schema.js +4 -16
- package/dist/core/substrate/surface-match.d.ts +19 -0
- package/dist/core/substrate/surface-match.js +60 -2
- package/dist/core/termrender/display.js +2 -1
- package/dist/core/tui/page-host.d.ts +3 -3
- package/dist/core/tui/page-host.js +4 -4
- package/dist/core/tui/panel.js +3 -3
- package/dist/core/wake.js +0 -12
- package/dist/core/worktree.js +0 -2
- package/dist/daemon/__tests__/reconciler-signature.test.d.ts +1 -0
- package/dist/daemon/__tests__/reconciler-signature.test.js +36 -0
- package/dist/daemon/api/__tests__/seam/api-server.test.js +2 -16
- package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +3 -11
- package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +0 -6
- package/dist/daemon/api/handlers/attach.js +2 -1
- package/dist/daemon/api/handlers/broker-ops.js +3 -6
- package/dist/daemon/api/handlers/canvas.js +12 -18
- package/dist/daemon/api/handlers/feedback-comments.js +0 -8
- package/dist/daemon/api/handlers/health.js +3 -2
- package/dist/daemon/api/handlers/human-requests.js +0 -10
- package/dist/daemon/api/handlers/human.js +1 -5
- package/dist/daemon/api/handlers/inbox.js +0 -18
- package/dist/daemon/api/handlers/memory.js +1 -1
- package/dist/daemon/api/handlers/messages.js +4 -8
- package/dist/daemon/api/handlers/nodes.js +10 -21
- package/dist/daemon/api/handlers/reports.js +1 -1
- package/dist/daemon/api/handlers/reviews.js +1 -6
- package/dist/daemon/api/map.js +0 -6
- package/dist/daemon/api/router.js +0 -4
- package/dist/daemon/api/server.js +2 -5
- package/dist/daemon/boot-migrations.d.ts +14 -0
- package/dist/daemon/boot-migrations.js +31 -0
- package/dist/daemon/cron/armed-context.d.ts +21 -0
- package/dist/daemon/cron/armed-context.js +100 -0
- package/dist/daemon/cron/capture.d.ts +20 -0
- package/dist/daemon/cron/capture.js +45 -0
- package/dist/daemon/cron/disposition.d.ts +19 -0
- package/dist/daemon/cron/disposition.js +52 -0
- package/dist/daemon/cron/escalation.d.ts +12 -0
- package/dist/daemon/cron/escalation.js +124 -0
- package/dist/daemon/cron/held.d.ts +4 -0
- package/dist/daemon/cron/held.js +20 -0
- package/dist/daemon/cron/lease-recovery.d.ts +3 -0
- package/dist/daemon/cron/lease-recovery.js +119 -0
- package/dist/daemon/cron/live-runs.d.ts +11 -0
- package/dist/daemon/cron/live-runs.js +23 -0
- package/dist/daemon/cron/passes.d.ts +11 -0
- package/dist/daemon/cron/passes.js +78 -0
- package/dist/daemon/cron/settle.d.ts +28 -0
- package/dist/daemon/cron/settle.js +37 -0
- package/dist/daemon/cron/sinks.d.ts +2 -0
- package/dist/daemon/cron/sinks.js +100 -0
- package/dist/daemon/cron-run.d.ts +5 -41
- package/dist/daemon/cron-run.js +193 -882
- package/dist/daemon/crtrd-cli.js +2 -1
- package/dist/daemon/crtrd.js +22 -22
- package/dist/daemon/fleet.js +0 -4
- package/dist/daemon/human/deliver-action.js +2 -1
- package/dist/daemon/human/sweep.js +15 -4
- package/dist/daemon/manage.d.ts +2 -2
- package/dist/daemon/manage.js +4 -13
- package/dist/daemon/pidfile.js +2 -2
- package/dist/daemon/reconcilers/broker-supervision.js +26 -29
- package/dist/daemon/reconcilers/dormant-inbox.d.ts +1 -1
- package/dist/daemon/reconcilers/dormant-inbox.js +2 -1
- package/dist/daemon/reconcilers/live-obligation.js +2 -2
- package/dist/daemon/reconcilers/pending-review-submit.d.ts +1 -1
- package/dist/daemon/reconcilers/pending-review-submit.js +2 -1
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -1
- package/dist/daemon/reconcilers/storage-maintenance.js +2 -1
- package/dist/daemon/review/companion.js +2 -2
- package/dist/daemon/review/sweep.js +8 -3
- package/dist/migrations/001-surfaces-frontmatter.js +0 -6
- package/dist/migrations/corpus.js +2 -5
- package/dist/migrations/registry.js +15 -2
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +0 -4
- package/dist/pi-extensions/__tests__/pre-command-gate.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/pre-command-gate.test.js +220 -0
- package/dist/pi-extensions/broker-local.js +2 -1
- package/dist/pi-extensions/canvas-bash-valve.js +3 -17
- package/dist/pi-extensions/canvas-context-intro.js +2 -7
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +14 -0
- package/dist/pi-extensions/canvas-doc-substrate.js +77 -5
- package/dist/pi-extensions/canvas-goal-capture.js +2 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +5 -12
- package/dist/pi-extensions/canvas-passive-context.js +2 -5
- package/dist/pi-extensions/canvas-preview-result.js +2 -1
- package/dist/pi-extensions/canvas-prompt-scrub.js +2 -1
- package/dist/pi-extensions/canvas-recap.js +3 -16
- package/dist/pi-extensions/canvas-review-boundary.js +2 -5
- package/dist/pi-extensions/canvas-stophook.js +2 -26
- package/dist/pi-extensions/canvas-structured-output.js +2 -1
- package/dist/pi-extensions/canvas-tool-guide.js +2 -1
- package/dist/shared/__tests__/env-boundary.test.d.ts +1 -0
- package/dist/shared/__tests__/env-boundary.test.js +93 -0
- package/dist/shared/env.d.ts +68 -0
- package/dist/shared/env.js +171 -0
- package/package.json +3 -1
- package/runtime.lock.json +2 -2
- package/dist/core/canvas/render.d.ts +0 -47
- package/dist/core/canvas/render.js +0 -245
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { envNodeId } from '../../shared/env.js';
|
|
1
2
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
import { jobDir } from '../canvas/paths.js';
|
|
@@ -140,7 +141,7 @@ export function recordFault(nodeId, input) {
|
|
|
140
141
|
updateMarker(nodeId, fault);
|
|
141
142
|
}
|
|
142
143
|
export function recordFaultForCurrentNode(input) {
|
|
143
|
-
const nodeId =
|
|
144
|
+
const nodeId = envNodeId();
|
|
144
145
|
if (nodeId === undefined || nodeId.trim() === '')
|
|
145
146
|
return false;
|
|
146
147
|
if (!existsSync(jobDir(nodeId)))
|
|
@@ -186,7 +187,7 @@ export function clearFault(nodeId, opts) {
|
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
export function clearFaultForCurrentNode(opts) {
|
|
189
|
-
const nodeId =
|
|
190
|
+
const nodeId = envNodeId();
|
|
190
191
|
if (nodeId === undefined || nodeId.trim() === '')
|
|
191
192
|
return false;
|
|
192
193
|
if (!existsSync(jobDir(nodeId)))
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Each call uses crouter's credential pool and route policy, an in-memory session,
|
|
4
4
|
// and only the inline provider-rotation extension. No conversation resources or
|
|
5
5
|
// canvas lifecycle hooks enter the helper session.
|
|
6
|
+
import { envNodeCwd, envProfileId } from '../../shared/env.js';
|
|
6
7
|
import { join } from 'node:path';
|
|
7
8
|
import { PoolCredentialStore } from '../pool-credential-store.js';
|
|
8
9
|
import { modelRequestFromConfig, selectInitialModelRoute } from '../model-routes.js';
|
|
@@ -55,8 +56,8 @@ async function runHeadless(request, fallback, collect) {
|
|
|
55
56
|
]);
|
|
56
57
|
if (timedOut)
|
|
57
58
|
return fallback;
|
|
58
|
-
const nodeCwd =
|
|
59
|
-
const profileId =
|
|
59
|
+
const nodeCwd = envNodeCwd() ?? process.cwd();
|
|
60
|
+
const profileId = envProfileId();
|
|
60
61
|
const agentDir = sdk.getAgentDir();
|
|
61
62
|
const modelRuntime = await sdk.ModelRuntime.create({
|
|
62
63
|
credentials: new PoolCredentialStore(join(agentDir, 'auth.json')),
|
|
@@ -25,12 +25,11 @@ import { assertMandatoryCanvasExtensions } from './canvas-extensions.js';
|
|
|
25
25
|
import { boundFleet } from './fleet.js';
|
|
26
26
|
import { emitEvent } from '../events/emit.js';
|
|
27
27
|
import { bindBrokerEventSource, eventSource } from '../events/source.js';
|
|
28
|
-
|
|
28
|
+
import { envBrokerEngine } from '../../shared/env.js';
|
|
29
29
|
// HeadlessBrokerHost (design §3.3): a detached broker PROCESS, no tmux pane. It
|
|
30
30
|
// boots the dedicated broker-cli entry directly (NEVER src/cli.ts), supervises
|
|
31
31
|
// via the broker pid recorded as pi_pid, and tears down over the node's unix
|
|
32
32
|
// socket (graceful `shutdown` frame, SIGTERM fallback).
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
33
|
/** Resolve the broker entry beside this module. A broker must run from the
|
|
35
34
|
* same generation as the crtrd launching it, so module-relative resolution
|
|
36
35
|
* preserves that invariant. */
|
|
@@ -48,7 +47,7 @@ const DEFAULT_BROKER_ENGINE = '@earendil-works/pi-coding-agent';
|
|
|
48
47
|
* why: the default-deny allowlist in spawn-env.ts strips `CRTR_*` from
|
|
49
48
|
* ambient env, so the child can no longer re-derive this itself). */
|
|
50
49
|
function resolveEngineSpec(inv) {
|
|
51
|
-
return inv.env['CRTR_BROKER_ENGINE'] ??
|
|
50
|
+
return inv.env['CRTR_BROKER_ENGINE'] ?? envBrokerEngine();
|
|
52
51
|
}
|
|
53
52
|
function isPlainLaunchSessionPath(token) {
|
|
54
53
|
return token.includes('/') || token.endsWith('.jsonl');
|
|
@@ -30,10 +30,8 @@ import { formatCard } from '../../shared/generated-context.js';
|
|
|
30
30
|
/** The runtime-restart continuation exactly as delivered. `revive.ts` injects
|
|
31
31
|
* it as the prompt of a strict resume that follows a cleanly aborted turn. */
|
|
32
32
|
export const RUNTIME_RESTART_CONTINUATION = formatCard('restart-continuation', {}, 'continue');
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
33
|
// Companion context files: the goal (the spawning mandate) and the one-shot
|
|
35
34
|
// yield message (a note from the prior self to the revived self).
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
35
|
/** The goal file — the prompt/task a node was spawned with, persisted at birth
|
|
38
36
|
* so a fresh revive can re-read its mandate. */
|
|
39
37
|
export function goalPath(nodeId) {
|
|
@@ -230,9 +228,7 @@ function backgroundJobsBlock(nodeId) {
|
|
|
230
228
|
lines.push('', 'Read output so far: `tail -n 50 <log>` (or `crtr node bash list --tail 20`). Abandon one: `crtr node bash kill <job-id>`.');
|
|
231
229
|
return `<background-jobs>\n${lines.join('\n')}\n</background-jobs>`;
|
|
232
230
|
}
|
|
233
|
-
// ---------------------------------------------------------------------------
|
|
234
231
|
// buildReviveKickoff — assemble the full fresh-revive first message.
|
|
235
|
-
// ---------------------------------------------------------------------------
|
|
236
232
|
/** Assemble the auto-injected first message for a FRESH revive of `meta` from its
|
|
237
233
|
* already-drained `bearings` (see drainBearings) plus pure on-disk reads of the
|
|
238
234
|
* node's goal, roadmap, and context dir, framed so the revived node can rebuild
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { envProfileId } from '../../shared/env.js';
|
|
1
2
|
import { resolve } from 'node:path';
|
|
2
3
|
import { getNode } from '../canvas/canvas.js';
|
|
3
4
|
import { usage } from '../errors.js';
|
|
@@ -10,7 +11,7 @@ export function resolveCreateKind(kind, profile) {
|
|
|
10
11
|
/** Validate a kind against the same cwd/profile configuration used to build its launch.
|
|
11
12
|
* Resolution is subprocess-isolated: crtrd is single-threaded and a config read can
|
|
12
13
|
* block indefinitely on a network filesystem. */
|
|
13
|
-
export async function assertLaunchKind(kind, cwd = process.cwd(), profileId =
|
|
14
|
+
export async function assertLaunchKind(kind, cwd = process.cwd(), profileId = envProfileId() || null) {
|
|
14
15
|
const kinds = Object.keys((await readMergedLaunchConfigAsync(cwd, profileId)).kinds);
|
|
15
16
|
if (!kinds.includes(kind)) {
|
|
16
17
|
throw usage(`unknown kind: ${kind}`, { received: kind, next: `Valid kinds: ${kinds.sort().join(', ')}.` });
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// fresh cycle as a new branch rather than starting a new file.
|
|
9
9
|
// The spec is rewritten on every polymorph (base→orchestrator) so a node
|
|
10
10
|
// always comes back as its *current* self.
|
|
11
|
+
import { envProfileId } from '../../shared/env.js';
|
|
11
12
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
12
13
|
import { join } from 'node:path';
|
|
13
14
|
import { readMergedLaunchConfig } from '../config.js';
|
|
@@ -25,7 +26,6 @@ import { modelLadders, defaultProvider, STRENGTH_ALIASES, MODEL_PROVIDER_KEYS, M
|
|
|
25
26
|
// file's openDb-reaching graph. Re-exported here so existing importers of these
|
|
26
27
|
// launch.ts symbols keep working.
|
|
27
28
|
export { OPENAI_PROVIDER_ENV, configuredLadderProviders, inferPortableModelSelection } from './model-selection.js';
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
29
|
// Two-axis model resolution: (provider × strength) → concrete `model:thinking`.
|
|
30
30
|
//
|
|
31
31
|
// A persona declares ONE `model:` field valued `provider/strength` (e.g.
|
|
@@ -36,7 +36,6 @@ export { OPENAI_PROVIDER_ENV, configuredLadderProviders, inferPortableModelSelec
|
|
|
36
36
|
// pi accepts the `:thinking` suffix on `--model` (off,minimal,low,medium,high,
|
|
37
37
|
// xhigh — confirmed via `pi --help`), so the resolved spec is exactly the
|
|
38
38
|
// string buildPiArgv passes to `--model`; no extra launch plumbing.
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
39
|
/** Model-ladder resolution goes through `readMergedLaunchConfig` — the SAME
|
|
41
40
|
* project-stack > profile > user > builtin precedence `resolveKindConfig`
|
|
42
41
|
* already uses for `kinds` — so a project/profile override of `modelLadders`
|
|
@@ -258,7 +257,7 @@ function buildLaunchSpecFromMerged(kind, mode, opts, merged) {
|
|
|
258
257
|
}
|
|
259
258
|
/** Build a launch recipe for callers that are not on crtrd's event loop. */
|
|
260
259
|
export function buildLaunchSpec(kind, mode, opts) {
|
|
261
|
-
return buildLaunchSpecFromMerged(kind, mode, opts, readMergedLaunchConfig(opts.cwd ?? process.cwd(), opts.profileId !== undefined ? opts.profileId : (
|
|
260
|
+
return buildLaunchSpecFromMerged(kind, mode, opts, readMergedLaunchConfig(opts.cwd ?? process.cwd(), opts.profileId !== undefined ? opts.profileId : (envProfileId() || null)));
|
|
262
261
|
}
|
|
263
262
|
/**
|
|
264
263
|
* Resolve launch config in a bounded subprocess before building a recipe.
|
|
@@ -268,7 +267,7 @@ export function buildLaunchSpec(kind, mode, opts) {
|
|
|
268
267
|
*/
|
|
269
268
|
export async function buildLaunchSpecAsync(kind, mode, opts) {
|
|
270
269
|
const cwd = opts.cwd ?? process.cwd();
|
|
271
|
-
const profileId = opts.profileId !== undefined ? opts.profileId : (
|
|
270
|
+
const profileId = opts.profileId !== undefined ? opts.profileId : (envProfileId() || null);
|
|
272
271
|
const merged = await readMergedLaunchConfigAsync(cwd, profileId);
|
|
273
272
|
return buildLaunchSpecFromMerged(kind, mode, opts, merged);
|
|
274
273
|
}
|
|
@@ -308,9 +307,7 @@ export function readMergedLaunchConfigAsync(cwd, profileId) {
|
|
|
308
307
|
});
|
|
309
308
|
});
|
|
310
309
|
}
|
|
311
|
-
// ---------------------------------------------------------------------------
|
|
312
310
|
// Build the pi argv to launch / revive a node
|
|
313
|
-
// ---------------------------------------------------------------------------
|
|
314
311
|
/** Persist a node's (possibly large) system prompt to a file in its node dir and
|
|
315
312
|
* return the absolute path, so callers can pass a short path to pi instead of
|
|
316
313
|
* the inline text. Returns null if the write fails — the caller then falls back
|
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
// `cancel`.
|
|
26
26
|
//
|
|
27
27
|
// Layering note: lifecycle.ts is runtime, but it is the canvas write surface's
|
|
28
|
-
// `transition` verb (the only writer of status+intent), so it
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
28
|
+
// `transition` verb (the only writer of status+intent), so it drives the
|
|
29
|
+
// atomic status+intent write through canvas.ts's `transitionStatusIntent`
|
|
30
|
+
// (its own decision logic — the legality table below, and the
|
|
31
|
+
// unknown-node/illegal-move/already-finalized-cancel branching — stays here;
|
|
32
|
+
// only the raw conditional UPDATE lives in canvas.ts, keeping every `.prepare(`
|
|
33
|
+
// inside `core/canvas/`).
|
|
34
|
+
import { getNode, cancelCronsOnWake, transitionStatusIntent } from '../canvas/index.js';
|
|
33
35
|
const ANY = '*';
|
|
34
36
|
/** The supervised statuses — a live node the daemon watches. */
|
|
35
37
|
const LIVE = ['active', 'idle'];
|
|
@@ -71,28 +73,17 @@ export function transition(nodeId, event) {
|
|
|
71
73
|
const spec = TRANSITIONS[event];
|
|
72
74
|
const writeStatus = Object.prototype.hasOwnProperty.call(spec, 'status');
|
|
73
75
|
const writeIntent = Object.prototype.hasOwnProperty.call(spec, 'intent');
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
if (writeIntent) {
|
|
81
|
-
assignments.push('intent = ?');
|
|
82
|
-
values.push(spec.intent ?? null);
|
|
83
|
-
}
|
|
84
|
-
const predicates = ['node_id = ?'];
|
|
85
|
-
values.push(nodeId);
|
|
86
|
-
if (spec.from !== ANY) {
|
|
87
|
-
predicates.push(`status IN (${spec.from.map(() => '?').join(', ')})`);
|
|
88
|
-
values.push(...spec.from);
|
|
89
|
-
}
|
|
76
|
+
const fields = {};
|
|
77
|
+
if (writeStatus)
|
|
78
|
+
fields.status = spec.status;
|
|
79
|
+
if (writeIntent)
|
|
80
|
+
fields.intent = spec.intent ?? null;
|
|
90
81
|
// A committed canonical result is protected from a concurrent close.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (
|
|
82
|
+
const changes = transitionStatusIntent(nodeId, fields, {
|
|
83
|
+
fromStatuses: spec.from === ANY ? undefined : spec.from,
|
|
84
|
+
requireNoFinalReport: event === 'cancel',
|
|
85
|
+
});
|
|
86
|
+
if (changes === 0) {
|
|
96
87
|
const cur = getNode(nodeId);
|
|
97
88
|
if (cur === null)
|
|
98
89
|
throw new Error(`transition: unknown node ${nodeId}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantMessageDiagnostic } from
|
|
1
|
+
import type { AssistantMessageDiagnostic } from '@earendil-works/pi-ai';
|
|
2
2
|
/** Diagnostic `type` tag carrying the cooling deadline; also the wire contract the
|
|
3
3
|
* stophook matches on. */
|
|
4
4
|
export declare const MANAGED_PROVIDER_COOLING_DIAGNOSTIC = "crtr:managed-provider-cooling";
|
|
@@ -217,10 +217,8 @@ export async function headlessName(body, sourceCap = PROMPT_CAP) {
|
|
|
217
217
|
export function headlessNameFromConversation(conversation) {
|
|
218
218
|
return headlessName(conversation, 30_000);
|
|
219
219
|
}
|
|
220
|
-
// ---------------------------------------------------------------------------
|
|
221
220
|
// Issue titles — turn a free-text todo/idea/bug note into a short, human-readable
|
|
222
221
|
// GitHub-issue title (sentence case, NOT kebab). Used by `crtr canvas issue new`.
|
|
223
|
-
// ---------------------------------------------------------------------------
|
|
224
222
|
const TITLE_SYSTEM_PROMPT = 'You write GitHub issue titles. Given a free-text note (a todo, idea, or bug ' +
|
|
225
223
|
'report), reply with ONLY a concise issue title: sentence case, 4-10 words, no ' +
|
|
226
224
|
'trailing period, no quotes, no prefix like "Bug:" or "Idea:", no markdown. ' +
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
// learns when the work finishes — this seeds the subscription
|
|
13
13
|
// graph to mirror the spawn structure. A `spawned_by` audit edge
|
|
14
14
|
// is also recorded.
|
|
15
|
+
import { envHomeOverride } from '../../shared/env.js';
|
|
15
16
|
import { randomBytes } from 'node:crypto';
|
|
16
17
|
import { mkdirSync } from 'node:fs';
|
|
17
18
|
import { createNode, getNode, getRow, subscribe, recordSpawn, contextDir, } from '../canvas/index.js';
|
|
@@ -200,7 +201,7 @@ export function nodeEnv(meta) {
|
|
|
200
201
|
if (meta.parent)
|
|
201
202
|
env['CRTR_PARENT_NODE_ID'] = meta.parent;
|
|
202
203
|
// Propagate an explicit canvas home so children share the same canvas.
|
|
203
|
-
const home =
|
|
204
|
+
const home = envHomeOverride();
|
|
204
205
|
if (home !== undefined && home !== '')
|
|
205
206
|
env['CRTR_HOME'] = home;
|
|
206
207
|
// Propagate the subtree-root node id. The broker uses it to group a subtree;
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
import { getNode, updateNode } from '../canvas/index.js';
|
|
5
5
|
import { readRoadmap, roadmapPath } from './roadmap.js';
|
|
6
6
|
import { orchestratorContextNote } from './bearings.js';
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
7
|
// Base → orchestrator guidance
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
8
|
/** Fresh-context framing for a base → orchestrator change. */
|
|
11
9
|
function orchestrationGuidance(nodeId, kind) {
|
|
12
10
|
const roadmap = readRoadmap(nodeId) ?? '(no roadmap yet)';
|
|
@@ -18,9 +16,7 @@ function orchestrationGuidance(nodeId, kind) {
|
|
|
18
16
|
parts.push('', `Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using your orchestration protocol. Current contents:`, '', roadmap, '', orchestratorContextNote(nodeId), '', 'Your three long-term memory scopes (user-global, project, node-local) already surface in your bearings at boot. What promotion changes is USE, not access: you now persist across refresh cycles, so node-local carries what THIS goal needs between your refreshes, and durable facts you commit to user-global or project outlive this node. Use `crtr memory write` to save a document; `crtr memory list` to browse; `crtr memory find` to search (see "Your long-term memory" above for the full substrate flow).', '', 'Then delegate each phase with `crtr node new --kind <kind>`. When your context fills, run `crtr node yield` to refresh against this roadmap.');
|
|
19
17
|
return parts.join('\n');
|
|
20
18
|
}
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
19
|
// Orchestrator → base guidance
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
20
|
/** orchestrator → base (demote): hands-on again, finish in-window. */
|
|
25
21
|
function baseModeGuidance() {
|
|
26
22
|
return ('You are HANDS-ON again — base mode. Do the work yourself in THIS window and finish it here; ' +
|
|
@@ -36,9 +32,7 @@ export function transitionGuidance(nodeId, from, to) {
|
|
|
36
32
|
const node = getNode(nodeId);
|
|
37
33
|
return orchestrationGuidance(nodeId, node?.kind ?? 'general');
|
|
38
34
|
}
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
35
|
// Detector + ack commit.
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
36
|
/** Compare the live persona with the last acknowledged context delivery.
|
|
43
37
|
* `guidance` contains only fresh-context framing; current snapshots supply its
|
|
44
38
|
* documents. An absent or pre-kind acknowledgment cannot fabricate drift. */
|
|
@@ -15,19 +15,16 @@
|
|
|
15
15
|
// §5.1 import-lint: placement-tmux.ts is an allowed direct importer of
|
|
16
16
|
// tmux-driver.ts — it is the sanctioned pure-tmux seam, re-exporting the driver
|
|
17
17
|
// verbs other modules need so they never import the driver directly.
|
|
18
|
+
import { envHomeOverride } from '../../shared/env.js';
|
|
18
19
|
import { paneLocation, ensureSession, openNodeWindow, splitWindow, selectLayout, } from './tmux-driver.js';
|
|
19
20
|
import { BROKER_VIEW_SOCKET_WAIT_MS } from './view-socket.js';
|
|
20
21
|
// Re-export the pure tmux driver verbs the CLI/viewer consumers need without
|
|
21
22
|
// exposing the canvas focuses table or sending command modules to the raw driver.
|
|
22
23
|
export { paneExists, paneLocation, paneOfNode, paneCurrentPath, paneSize, paneRunning, ensureSession, openNodeWindow, splitWindow, closePane, currentTmux, switchClient, selectWindow, selectLayout, selectPane, getPaneOption, setPaneOption, breakPane, inTmux, piCommand, shellQuote, renameWindow, displayPopup, windowOfPane, respawnPaneSync, respawnPaneInBackground, respawnPaneInPlace, paneStartCommand, panePid, windowAlive, listLivePanes, VIEWER_STAMP_OPTION, stampViewerPaneAsync, liveViewerPanes, sendAttachControl, displayClientMessage, } from './tmux-driver.js';
|
|
23
24
|
export { installTmuxBindings } from './tmux-bindings.js';
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
25
|
// view.sock readiness — the one shared broker-cold-start primitive
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
26
|
export { BROKER_VIEW_SOCKET_WAIT_MS, waitForBrokerViewSocket } from './view-socket.js';
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
27
|
// Viewer pane opener (tmux half, NO focus-row write)
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
28
|
/** Env for a `crtr surface attach` VIEWER pane (the focus split, the spawn viewer
|
|
32
29
|
* window, AND recycle's re-attach respawn): propagate CRTR_HOME so the viewer
|
|
33
30
|
* resolves the SAME canvas home — and thus the right view.sock — under a
|
|
@@ -35,7 +32,7 @@ export { BROKER_VIEW_SOCKET_WAIT_MS, waitForBrokerViewSocket } from './view-sock
|
|
|
35
32
|
* server env, like every other crtr chrome pane). One helper so the viewer-pane
|
|
36
33
|
* sites can't drift. */
|
|
37
34
|
export function viewerSplitEnv() {
|
|
38
|
-
const crtrHome =
|
|
35
|
+
const crtrHome = envHomeOverride();
|
|
39
36
|
return crtrHome !== undefined ? { CRTR_HOME: crtrHome } : {};
|
|
40
37
|
}
|
|
41
38
|
/** Open a `crtr surface attach` VIEWER pane for `nodeId` — the PURE tmux half of
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
// • The chrome/viewer tmux re-exports (§2.1): placement stays the sanctioned
|
|
23
23
|
// model-over-driver. Raw driver access is confined to placement, its pure
|
|
24
24
|
// tmux half, the bindings owner, and tmux chrome.
|
|
25
|
+
import { envNodeId } from '../../shared/env.js';
|
|
25
26
|
import { getNode, fullName, deleteNode, listNodes, openFocusRow, closeFocusRow, getFocusByNode, getFocusByPane, getFocusById, listFocuses as listFocusRows, } from '../canvas/index.js';
|
|
26
|
-
import { nodeHasAssistantMessage,
|
|
27
|
+
import { nodeHasAssistantMessage, isStreaming } from '../canvas/render-source.js';
|
|
27
28
|
import { headlessBrokerHost } from './host.js';
|
|
28
29
|
import { paneExists, paneLocation, closePane, currentTmux, switchClient, selectWindow, selectLayout, selectPane, getPaneOption, setPaneOption, respawnPaneSync, windowOfPane, renameWindow, } from './tmux-driver.js';
|
|
29
30
|
// The PURE tmux + broker-socket half now lives in placement-tmux.ts. placement
|
|
@@ -47,7 +48,6 @@ import { isPidAlive } from '../canvas/pid.js';
|
|
|
47
48
|
// tmux-driver.ts directly.
|
|
48
49
|
export { splitWindow, breakPane, selectLayout, selectPane, focusWindow, selectWindow, switchClient, openNodeWindow, ensureSession, paneLocation, paneExists, paneRunning, paneCurrentPath, currentTmux, inTmux, paneOfNode, setPaneOption, getPaneOption, piCommand, shellQuote, respawnPaneSync, closePane, displayPopup, windowOfPane, renameWindow, paneOfWindow, windowAlive, listLivePanes, } from './tmux-driver.js';
|
|
49
50
|
export { installTmuxBindings } from './tmux-bindings.js';
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
51
|
// Viewer registry reads — COMPOSE over the canvas focuses table (§2.3/§4).
|
|
52
52
|
//
|
|
53
53
|
// placement is the front door for "which nodes have a viewer on screen"; the SQL
|
|
@@ -57,7 +57,6 @@ export { installTmuxBindings } from './tmux-bindings.js';
|
|
|
57
57
|
// GC is lazy on read: a row whose viewer pane no longer exists (the user closed
|
|
58
58
|
// the window, the attach client died) is pruned the next time it is read, so the
|
|
59
59
|
// registry self-heals without a sweeper.
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
60
|
/** Prune a viewer row whose pane is gone; return it iff its pane is still live. */
|
|
62
61
|
function liveOrPrune(f) {
|
|
63
62
|
if (f === null)
|
|
@@ -131,9 +130,7 @@ export function openViewerWindow(nodeId, session, opts = {}) {
|
|
|
131
130
|
return null;
|
|
132
131
|
return registerViewerFocus(nodeId, opened.pane, opened.session, opened.window);
|
|
133
132
|
}
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
133
|
// focus — the front door for `surface node focus` / `surface node cycle` / nav (§A.4)
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
134
|
/** Bring a node's ONE viewer pane forefront (§A.4). Every node is a detached
|
|
138
135
|
* broker, so there is no engine pane to swap into the caller's viewport — focus
|
|
139
136
|
* instead ensures the node's single surface-attach viewer is on screen beside (or
|
|
@@ -283,9 +280,7 @@ export async function focus(nodeId, opts) {
|
|
|
283
280
|
surfaceNewPane(opened.pane);
|
|
284
281
|
return { focused: true, session: opened.session, inPlace: false, revived };
|
|
285
282
|
}
|
|
286
|
-
// ---------------------------------------------------------------------------
|
|
287
283
|
// Teardown / detach — close a node's viewer pane + row
|
|
288
|
-
// ---------------------------------------------------------------------------
|
|
289
284
|
/** Tear a node off its viewer (close/reset/cancel teardown). The broker engine
|
|
290
285
|
* is killed by its own host teardown over view.sock — this only closes the
|
|
291
286
|
* on-screen viewer: kill its pane (the window collapses once its last pane goes)
|
|
@@ -326,9 +321,7 @@ export function detachToBackground(nodeId, pane) {
|
|
|
326
321
|
reapIfEmpty(nodeId);
|
|
327
322
|
return closed;
|
|
328
323
|
}
|
|
329
|
-
// ---------------------------------------------------------------------------
|
|
330
324
|
// Reaping empty nodes (no AI output) on close / detach
|
|
331
|
-
// ---------------------------------------------------------------------------
|
|
332
325
|
/** Is this node an EMPTY SHELL right now — a node whose engine never produced an
|
|
333
326
|
* assistant message? Such nodes are pure dead weight on the graph. A node
|
|
334
327
|
* actively generating its FIRST turn is NOT empty (its output just hasn't landed
|
|
@@ -337,7 +330,7 @@ function isEmptyNode(nodeId) {
|
|
|
337
330
|
const m = getNode(nodeId);
|
|
338
331
|
if (m === null)
|
|
339
332
|
return false;
|
|
340
|
-
if (
|
|
333
|
+
if (isStreaming(nodeId, getNode(nodeId)?.pi_pid ?? null))
|
|
341
334
|
return false;
|
|
342
335
|
return !nodeHasAssistantMessage(m.pi_session_file);
|
|
343
336
|
}
|
|
@@ -366,7 +359,7 @@ export function reapIfEmpty(nodeId, keepPane) {
|
|
|
366
359
|
* deleting. Returns the reaped (or, under dryRun, reapable) node ids. */
|
|
367
360
|
export function reapEmptyNodes(opts = {}) {
|
|
368
361
|
const dryRun = opts.dryRun ?? false;
|
|
369
|
-
const selfId =
|
|
362
|
+
const selfId = envNodeId() ?? '';
|
|
370
363
|
const reaped = [];
|
|
371
364
|
for (const row of listNodes()) {
|
|
372
365
|
const id = row.node_id;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// there is no local fallback, unlike naming, because a wrong recap is worse than
|
|
12
12
|
// none.
|
|
13
13
|
import { headlessText } from './headless-pi.js';
|
|
14
|
+
import { envRecapModelOverride } from '../../shared/env.js';
|
|
14
15
|
/** Wall-clock budget for the headless pi call before we give up (no recap). */
|
|
15
16
|
const RECAP_TIMEOUT_MS = 25_000;
|
|
16
17
|
/** Fallback model when the live session has not resolved one yet. Ordinarily the
|
|
@@ -59,9 +60,9 @@ function recapUserPrompt(conversation, roadmap) {
|
|
|
59
60
|
/** Model precedence is explicit env override → the conversation's live
|
|
60
61
|
* working model → the small fallback. */
|
|
61
62
|
function recapModel(liveModel) {
|
|
62
|
-
const override =
|
|
63
|
-
if (override !== undefined
|
|
64
|
-
return override
|
|
63
|
+
const override = envRecapModelOverride();
|
|
64
|
+
if (override !== undefined)
|
|
65
|
+
return override;
|
|
65
66
|
if (liveModel !== undefined && liveModel.trim() !== '')
|
|
66
67
|
return liveModel.trim();
|
|
67
68
|
return DEFAULT_RECAP_MODEL;
|
|
@@ -38,11 +38,10 @@
|
|
|
38
38
|
// mutating the live pointer — the explicit operation must not retract a
|
|
39
39
|
// canonical final unless it can actually enact the requested re-task.
|
|
40
40
|
import { join } from 'node:path';
|
|
41
|
-
import {
|
|
41
|
+
import { clearFinalReportIfMatches, getFinalReport, reportsDir, updateNode } from '../canvas/index.js';
|
|
42
42
|
import { InputError } from '../io.js';
|
|
43
43
|
function currentFinalReport(nodeId) {
|
|
44
|
-
|
|
45
|
-
return row?.final_report ?? null;
|
|
44
|
+
return getFinalReport(nodeId);
|
|
46
45
|
}
|
|
47
46
|
function throwNodeFinalized(nodeId, finalReport) {
|
|
48
47
|
const reportPath = join(reportsDir(nodeId), finalReport);
|
|
@@ -90,10 +89,8 @@ export function assertFinalizedForReopen(nodeId) {
|
|
|
90
89
|
* canonical result. Callers MUST NOT proceed to revive/deliver when this
|
|
91
90
|
* throws. */
|
|
92
91
|
export function commitReopen(nodeId, expectedFinalReport) {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
.run(nodeId, expectedFinalReport);
|
|
96
|
-
if (result.changes === 0) {
|
|
92
|
+
const cleared = clearFinalReportIfMatches(nodeId, expectedFinalReport);
|
|
93
|
+
if (!cleared) {
|
|
97
94
|
throw new InputError({
|
|
98
95
|
error: 'reopen_latch_changed',
|
|
99
96
|
message: `${nodeId}'s finalization latch changed since it was last inspected — a concurrent --reopen (or a fresh push final) already moved it, so clearing it now would risk erasing a newer canonical result.`,
|
|
@@ -27,9 +27,7 @@ import { tearDownNode, focusOf, registerViewerFocus, respawnPaneSync, windowOfPa
|
|
|
27
27
|
import { viewerSplitEnv, waitForBrokerViewSocket } from './placement-tmux.js';
|
|
28
28
|
import { buildLaunchSpecAsync, buildPiArgv } from './launch.js';
|
|
29
29
|
import { spawnNode, rootOfSpine } from './nodes.js';
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
30
|
// reapDescendants — tear down a root's descendant sub-DAG (shared helper)
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
31
|
/** Reap the descendant sub-DAG of `rootId`: mark each **canceled** (the user
|
|
34
32
|
* moved on — a clean teardown, NOT a fault) + clear intent FIRST, then kill its
|
|
35
33
|
* window (closes the daemon revive race). Edges are LEFT INTACT — descendants
|
|
@@ -175,9 +173,7 @@ export function handleNewSession(nodeId, newSessionId, newSessionFile) {
|
|
|
175
173
|
resetRoot(nodeId, newSessionId, newSessionFile);
|
|
176
174
|
return { path: 'reset-child' };
|
|
177
175
|
}
|
|
178
|
-
// ---------------------------------------------------------------------------
|
|
179
176
|
// markCleanExitDone — the clean-exit→done termination guard
|
|
180
|
-
// ---------------------------------------------------------------------------
|
|
181
177
|
/** Resolve a cleanly-exiting node to `done`. Returns true iff it transitioned.
|
|
182
178
|
* Guard: only a real quit, and only a node still active|idle with no pending
|
|
183
179
|
* intent — so it never clobbers a node already routed by agent_end to done
|
|
@@ -113,9 +113,7 @@ async function attachInline(nodeId) {
|
|
|
113
113
|
});
|
|
114
114
|
process.exit(code);
|
|
115
115
|
}
|
|
116
|
-
// ---------------------------------------------------------------------------
|
|
117
116
|
// `-r` picker — a minimal raw-mode keypress menu (pre-boot, we own the TTY).
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
117
|
const HILITE = accent('\u25b8');
|
|
120
118
|
const cursorUp = (n) => (n > 0 ? `\u001b[${n}A\r` : '');
|
|
121
119
|
const CLEAR_DOWN = '\u001b[0J';
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
//
|
|
18
18
|
// These are requests. Durable `intent=refresh` or `cycle_pending=true` always
|
|
19
19
|
// vetoes strict resume so focus and daemon callers cannot reopen an old
|
|
20
|
-
// context while a fresh cycle is still unconfirmed.
|
|
20
|
+
// context while a fresh cycle is still unconfirmed. A parked node vetoes it
|
|
21
|
+
// too, but only when the revival carries new input — unseen wake-worthy mail —
|
|
22
|
+
// and there is goal/roadmap ground state to re-enter on.
|
|
21
23
|
//
|
|
22
24
|
// reviveNode remains the ONLY sanctioned launcher of the node engine.
|
|
23
25
|
import { existsSync } from 'node:fs';
|
|
@@ -30,6 +32,7 @@ import { InputError } from '../io.js';
|
|
|
30
32
|
import { buildPiArgv } from './launch.js';
|
|
31
33
|
import { buildReviveKickoff, drainBearings, readGoal, readYieldMessage, RUNTIME_RESTART_CONTINUATION } from './kickoff.js';
|
|
32
34
|
import { readRoadmap } from './roadmap.js';
|
|
35
|
+
import { hasUnseenWakeMail } from '../feed/inbox.js';
|
|
33
36
|
import { headlessBrokerHost } from './host.js';
|
|
34
37
|
import { boundFleet } from './fleet.js';
|
|
35
38
|
import { clearFault, beginBootFaultAttempt } from './fault.js';
|
|
@@ -37,9 +40,7 @@ import { clearContextExposureState, initializeForkContextExposure, } from '../su
|
|
|
37
40
|
import { rootOfSpine } from './nodes.js';
|
|
38
41
|
import { isReviewCompanionBound } from '../review/companion.js';
|
|
39
42
|
import { assertProfileActive } from '../profiles/manifest.js';
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
43
|
// resumeArgs — which session source a revive resumes from
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
44
|
/** Pick the `--session` source for a revive. Both resume=true (a true resume)
|
|
44
45
|
* and resume=false (a refresh-yield) resume by the absolute session-file path
|
|
45
46
|
* (immune to cwd; pi opens it directly) — but ONLY when that file actually
|
|
@@ -62,9 +63,7 @@ export function resumeArgs(meta, resume) {
|
|
|
62
63
|
return {};
|
|
63
64
|
return resume ? { resumeSessionPath: path } : { resumeSessionPath: path, newCycle: true };
|
|
64
65
|
}
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
66
|
// Return type
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
67
|
/** A relaunch that has NO transcript to resume or branch from, and no durable
|
|
69
68
|
* work state on disk (no roadmap, no yield note), is not a revive at all — the
|
|
70
69
|
* node never got a conversation off the ground, so there is no "previous
|
|
@@ -91,9 +90,7 @@ export function hasFreshGroundState(nodeId) {
|
|
|
91
90
|
const roadmap = readRoadmap(nodeId);
|
|
92
91
|
return roadmap !== null && roadmap.trim() !== '';
|
|
93
92
|
}
|
|
94
|
-
// ---------------------------------------------------------------------------
|
|
95
93
|
// reviveNode
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
94
|
/** Relaunch `nodeId`'s broker engine from its persisted recipe and update canvas
|
|
98
95
|
* meta. Opens no viewer (engine-only).
|
|
99
96
|
*
|
|
@@ -194,14 +191,21 @@ export function reviveNode(nodeId, opts) {
|
|
|
194
191
|
// selects only an existing absolute session path: strict
|
|
195
192
|
// resume continues its leaf, while fresh/cycle mode roots a new one.
|
|
196
193
|
// A parked node re-enters on a FRESH cycle grounded in goal + roadmap rather
|
|
197
|
-
// than resuming the transcript its parking turn concluded —
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
194
|
+
// than resuming the transcript its parking turn concluded — but ONLY when the
|
|
195
|
+
// revival carries new input. Mail that arrived after the park is what a fresh
|
|
196
|
+
// window is for: every doorway that wakes a parked node because something
|
|
197
|
+
// arrived appends the durable entry BEFORE calling in, so the unseen set IS
|
|
198
|
+
// that input. An input-less revival — a bare `node lifecycle revive`, a viewer
|
|
199
|
+
// attach, a focus — has nothing to answer, so a fresh cycle would only buy a
|
|
200
|
+
// nothing-to-do turn and a re-park 15 minutes later: it quietly resumes the
|
|
201
|
+
// old transcript instead, injects no kickoff, and generates nothing. The park
|
|
202
|
+
// clock re-parks it in due course.
|
|
203
|
+
// The second exception is unchanged: a park with nothing on disk to ground a
|
|
204
|
+
// fresh window (reachable only on the degraded path, which writes no summary)
|
|
205
|
+
// resumes rather than waking amnesiac.
|
|
202
206
|
// The transition below clears the marker, and this snapshot precedes it.
|
|
203
207
|
const refreshPending = meta.intent === 'refresh' || meta.cycle_pending === true;
|
|
204
|
-
const parkedReentry = isParked(meta) && hasFreshGroundState(nodeId);
|
|
208
|
+
const parkedReentry = isParked(meta) && hasFreshGroundState(nodeId) && hasUnseenWakeMail(nodeId);
|
|
205
209
|
const effectiveResume = refreshPending || parkedReentry ? false : opts.resume;
|
|
206
210
|
const resume = forkBirthPending ? {} : resumeArgs(meta, effectiveResume);
|
|
207
211
|
// hasSessionPath: there is a real `.jsonl` to hand pi via `--session`, whether
|
|
@@ -77,7 +77,7 @@ function isValidEntry(e) {
|
|
|
77
77
|
const c = e;
|
|
78
78
|
return isFiniteNonNeg(c['mtimeMs']) && isFiniteNonNeg(c['size']) && isValidWireInfo(c['info']);
|
|
79
79
|
}
|
|
80
|
-
//
|
|
80
|
+
// faithful mirror of pi's private buildSessionInfo
|
|
81
81
|
function parseLine(line) {
|
|
82
82
|
if (!line.trim())
|
|
83
83
|
return null;
|
|
@@ -187,7 +187,7 @@ async function buildWireSessionInfo(filePath) {
|
|
|
187
187
|
return null;
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
-
//
|
|
190
|
+
// the cache
|
|
191
191
|
/** In-memory + file-backed cache keyed by absolute `.jsonl` path. One instance
|
|
192
192
|
* per broker; the file is shared across brokers (last-writer-wins via atomic
|
|
193
193
|
* rename — a lost update just costs one file's rebuild next open, never
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
-
// ---------------------------------------------------------------------------
|
|
3
2
|
// Shell expansion inside Markdown bodies: `!`cmd`` inline and ```! fenced
|
|
4
3
|
// blocks are replaced by the command's output.
|
|
5
4
|
//
|
|
@@ -13,7 +12,6 @@ import { execFile } from 'node:child_process';
|
|
|
13
12
|
// Node built-ins ONLY. This module is reachable from `src/commands/**`, where a
|
|
14
13
|
// `@earendil-works/pi-coding-agent` import would load the whole pi runtime just
|
|
15
14
|
// to read a document.
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
15
|
// Built fresh per use, never shared. A module-level /g regex carries mutable
|
|
18
16
|
// `lastIndex`, and both `.test()` and `matchAll` (which copies it into its
|
|
19
17
|
// internal clone) would make one call silently skip matches another consumed.
|
|
@@ -35,10 +35,8 @@ import { modelRequestFromConfig, unregisteredConcreteModel } from '../model-rout
|
|
|
35
35
|
import { openModelRegistry } from './model-registry.js';
|
|
36
36
|
import { MODEL_SPEC_NEXT } from './model-selection.js';
|
|
37
37
|
import { initializeForkContextExposure } from '../substrate/injected-store.js';
|
|
38
|
-
// ---------------------------------------------------------------------------
|
|
39
38
|
// Shared broker-launch wrapper — catch launch/preflight failures at the call
|
|
40
39
|
// site, mark the node crashed, then rethrow a clear error.
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
40
|
function launchBrokerOrCrash(nodeId, inv, opts) {
|
|
43
41
|
try {
|
|
44
42
|
return headlessBrokerHost.launch(nodeId, inv, opts);
|