@north-light/crouter 0.3.233 → 0.3.235
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/api/dto/nodes.d.ts +11 -0
- 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 +535 -537
- 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 +1 -9
- 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 +10 -23
- 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/human.js +1 -1
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +4 -4
- package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.d.ts +1 -0
- package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +96 -0
- package/dist/commands/memory/delete.js +1 -1
- package/dist/commands/memory/edit.js +6 -3
- package/dist/commands/memory/find.js +2 -2
- package/dist/commands/memory/lint.js +2 -1
- package/dist/commands/memory/list.d.ts +4 -0
- package/dist/commands/memory/list.js +20 -14
- package/dist/commands/memory/move.js +1 -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 -0
- package/dist/commands/memory/shared.js +17 -8
- package/dist/commands/memory/write.js +8 -4
- package/dist/commands/node/bash.js +2 -1
- package/dist/commands/node/create.js +2 -1
- package/dist/commands/node/inspect.js +11 -10
- package/dist/commands/node/lifecycle.js +5 -13
- package/dist/commands/node/message.js +11 -6
- package/dist/commands/node/subscription.js +7 -2
- package/dist/commands/node/wait.js +9 -4
- 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 +3 -2
- package/dist/commands/pkg/shared.js +0 -2
- package/dist/commands/pkg.js +1 -0
- package/dist/commands/profile/delete.js +2 -1
- package/dist/commands/profile/rename.js +1 -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 +1 -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/commands/sys.js +1 -0
- 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__/cli-help-contract.test.d.ts +1 -0
- package/dist/core/__tests__/cli-help-contract.test.js +150 -0
- 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 +21 -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/__tests__/watchdog-abort-arms-retry.test.js +11 -6
- 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 +61 -19
- 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/meta-file.js +18 -27
- 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.d.ts +8 -0
- package/dist/core/canvas/remote-canvas-source.js +9 -3
- package/dist/core/canvas/render-source.js +31 -13
- package/dist/core/command-manifests/manifest.js +0 -10
- package/dist/core/command-manifests/registry.d.ts +2 -0
- package/dist/core/command-manifests/schema.js +0 -4
- package/dist/core/command-plugins/compose.js +4 -2
- package/dist/core/command-plugins/discovery.d.ts +1 -0
- package/dist/core/command-plugins/discovery.js +1 -1
- package/dist/core/command-plugins/transport/http-invoke.js +0 -14
- package/dist/core/command.js +2 -6
- package/dist/core/config.js +2 -1
- package/dist/core/fault-classifier.js +8 -0
- 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.d.ts +2 -0
- package/dist/core/pagination.js +2 -3
- 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 +11 -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/rebind.js +50 -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.d.ts +2 -2
- package/dist/core/runtime/launch.js +20 -10
- 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/stop-guard.js +28 -16
- 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.js +3 -3
- package/dist/core/substrate/frontmatter-validation.d.ts +1 -0
- package/dist/core/substrate/frontmatter-validation.js +1 -1
- package/dist/core/substrate/render.js +0 -12
- package/dist/core/substrate/schema.js +0 -12
- package/dist/core/substrate/surface-match.js +0 -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__/error-seam.test.js +27 -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 +16 -20
- package/dist/daemon/api/handlers/nodes.js +23 -22
- package/dist/daemon/api/handlers/reports.js +1 -1
- package/dist/daemon/api/handlers/reviews.js +1 -6
- package/dist/daemon/api/handlers/worktree.js +6 -22
- package/dist/daemon/api/map.d.ts +4 -2
- package/dist/daemon/api/map.js +22 -8
- 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 +27 -30
- 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/broker-local.js +2 -1
- package/dist/pi-extensions/canvas-bash-valve.js +7 -20
- package/dist/pi-extensions/canvas-context-intro.js +2 -7
- package/dist/pi-extensions/canvas-doc-substrate.js +2 -3
- 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
|
@@ -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);
|
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
// • finished/asked → it pushed --final (done) or called `crtr ask` this turn.
|
|
18
18
|
// Also fine.
|
|
19
19
|
// • structured → a TERMINAL node has a pending structured-output request.
|
|
20
|
-
//
|
|
21
|
-
//
|
|
20
|
+
// A valid request reprompts with the schema unless the node
|
|
21
|
+
// also holds a wake source, which it may sleep on first;
|
|
22
|
+
// an invalid request reprompts with an error and requires
|
|
23
|
+
// manual cleanup.
|
|
22
24
|
// • otherwise → a TERMINAL node with nothing live to wait for and no
|
|
23
25
|
// final pushed. Re-prompt it to finish or escalate.
|
|
24
26
|
import { hasActiveLiveSubscription, hasLiveMessageWait, hasPendingCancelOnWakeCron, getNode, contextDir } from '../canvas/index.js';
|
|
@@ -39,6 +41,19 @@ function formatInvalidStructuredOutputReprompt(error) {
|
|
|
39
41
|
`After resolving the file, retry the submission. ` +
|
|
40
42
|
`If this is unexpected, escalate to the user using \`crtr human send\`.`);
|
|
41
43
|
}
|
|
44
|
+
/** The wake sources that legitimize a terminal node's dormancy: a pending
|
|
45
|
+
* cancel-on-wake deadline, an active subscription to a live publisher, an
|
|
46
|
+
* explicit live controller wait, or a live background bash job whose completion
|
|
47
|
+
* sends an inbox message. Parentage alone is deliberately absent. */
|
|
48
|
+
function wakeCapableWait(nodeId, backgroundJobsRunning) {
|
|
49
|
+
if (hasPendingCancelOnWakeCron(nodeId))
|
|
50
|
+
return 'scheduled';
|
|
51
|
+
if (hasActiveLiveSubscription(nodeId)
|
|
52
|
+
|| hasLiveMessageWait(nodeId)
|
|
53
|
+
|| backgroundJobsRunning)
|
|
54
|
+
return 'awaiting';
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
42
57
|
/** Decide what to do when a node stops. crtrd supplies every current-canvas
|
|
43
58
|
* fact; the broker contributes only whether its Pi-owned background job registry
|
|
44
59
|
* is still running work. */
|
|
@@ -49,9 +64,14 @@ export function evaluateStop(nodeId, signals, backgroundJobsRunning = activeBack
|
|
|
49
64
|
return { action: 'allow', reason: 'escalated' };
|
|
50
65
|
// A pending structured-output request overrides every stored-state dormancy
|
|
51
66
|
// path: a post-final one-off follow-up still owes `submit` before it may stop.
|
|
52
|
-
//
|
|
67
|
+
// A wake source is the exception — the request outlives the sleep, so
|
|
68
|
+
// reprompting instead would busy-wait the node against its own child, deadline,
|
|
69
|
+
// or job. Invalid requests surface an explicit error and require manual cleanup.
|
|
53
70
|
const outputState = readOutputRequest(nodeId);
|
|
54
71
|
if (outputState.state === 'valid') {
|
|
72
|
+
const pendingWait = wakeCapableWait(nodeId, backgroundJobsRunning);
|
|
73
|
+
if (pendingWait !== null)
|
|
74
|
+
return { action: 'allow', reason: pendingWait };
|
|
55
75
|
return {
|
|
56
76
|
action: 'reprompt',
|
|
57
77
|
reason: 'stalled',
|
|
@@ -82,19 +102,11 @@ export function evaluateStop(nodeId, signals, backgroundJobsRunning = activeBack
|
|
|
82
102
|
if (node !== null && node.lifecycle === 'resident') {
|
|
83
103
|
return { action: 'allow', reason: 'dormant' };
|
|
84
104
|
}
|
|
85
|
-
// A terminal node
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
return { action: 'allow', reason:
|
|
90
|
-
// A terminal node holding something live to wake it is legitimately awaiting.
|
|
91
|
-
// Parentage alone is deliberately absent: only an active subscription, an
|
|
92
|
-
// explicit controller wait, or a live background bash job whose completion
|
|
93
|
-
// sends an inbox message can suppress the stall guard.
|
|
94
|
-
if (hasActiveLiveSubscription(nodeId)
|
|
95
|
-
|| hasLiveMessageWait(nodeId)
|
|
96
|
-
|| backgroundJobsRunning)
|
|
97
|
-
return { action: 'allow', reason: 'awaiting' };
|
|
105
|
+
// A terminal node holding a wake source is legitimately waiting: it should
|
|
106
|
+
// idle-release rather than busy-wait holding its broker.
|
|
107
|
+
const pendingWait = wakeCapableWait(nodeId, backgroundJobsRunning);
|
|
108
|
+
if (pendingWait !== null)
|
|
109
|
+
return { action: 'allow', reason: pendingWait };
|
|
98
110
|
// A terminal node with nothing live and no final pushed has stalled.
|
|
99
111
|
return {
|
|
100
112
|
action: 'reprompt',
|
|
@@ -13,23 +13,18 @@
|
|
|
13
13
|
// generation so settlement records an automatic pi→provider connection fault;
|
|
14
14
|
// the existing fault-retry machinery owns re-driving the turn. The broker
|
|
15
15
|
// process — and its in-memory queues — survives instead of being killed.
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (raw === undefined)
|
|
29
|
-
return DEFAULT_STREAM_WATCHDOG_MS;
|
|
30
|
-
const n = Number(raw);
|
|
31
|
-
return Number.isFinite(n) && n > 0 ? n : DEFAULT_STREAM_WATCHDOG_MS;
|
|
32
|
-
}
|
|
16
|
+
// The default is 5 minutes, comfortably under the daemon's 20-minute
|
|
17
|
+
// WEDGE_QUIET_MS hard kick (crtrd.ts), so this is the common-case recovery
|
|
18
|
+
// path and the daemon kick becomes the rare backstop. Generous enough that a
|
|
19
|
+
// live, healthy stream never trips it: a single LLM turn's own network
|
|
20
|
+
// round-trips (message_update deltas, tool_execution_start/update/end) land
|
|
21
|
+
// well inside 5 minutes. The watchdog covers ONLY time awaiting the provider:
|
|
22
|
+
// the broker disarms it while a tool is executing (a legitimately silent tool
|
|
23
|
+
// can run longer than the timeout — see broker.ts relayEvent) and rearms it
|
|
24
|
+
// once the last in-flight tool ends, so a slow-but-live turn is never mistaken
|
|
25
|
+
// for a dead one. See `envStreamWatchdogMs()` (`shared/env.ts`) for the parsed
|
|
26
|
+
// `CRTR_STREAM_WATCHDOG_MS` accessor and default value.
|
|
27
|
+
import { envStreamWatchdogMs } from '../../shared/env.js';
|
|
33
28
|
/**
|
|
34
29
|
* A single-timer dead-man's switch: `poke(token)` (re)arms it, `clear()`
|
|
35
30
|
* disarms it, and `onStall(token)` fires exactly once if `poke()` isn't called
|
|
@@ -50,7 +45,7 @@ export class StreamWatchdog {
|
|
|
50
45
|
ms;
|
|
51
46
|
constructor(onStall, ms) {
|
|
52
47
|
this.onStall = onStall;
|
|
53
|
-
this.ms = ms ??
|
|
48
|
+
this.ms = ms ?? envStreamWatchdogMs();
|
|
54
49
|
}
|
|
55
50
|
/** Proof of progress — (re)arm the timer for `token`, canceling any prior
|
|
56
51
|
* countdown. The fired `onStall` receives THIS arm's token. Inert while
|
|
@@ -10,9 +10,7 @@ import { normalizeTmuxPassthrough } from '../config.js';
|
|
|
10
10
|
import { withExclusiveDirectoryLock } from '../exclusive-lock.js';
|
|
11
11
|
import { surfaceTmuxStyleArgs } from './surface-bg.js';
|
|
12
12
|
import { runTmux as tmux, shellQuote } from './tmux-driver.js';
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
13
|
// Crouter-owned tmux bindings
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
14
|
const TMUX_BINDING_MANIFEST = '@crtr_keybindings_manifest';
|
|
17
15
|
const ROOT_TABLE = 'root';
|
|
18
16
|
const LEAF_MENU_ACTIONS = {
|
|
@@ -7,6 +7,18 @@ declare function tmux(args: string[]): {
|
|
|
7
7
|
stderr: string;
|
|
8
8
|
};
|
|
9
9
|
export declare function inTmux(): boolean;
|
|
10
|
+
/** Is any tmux SESSION currently up? `tmux list-sessions` exits 0 iff a server
|
|
11
|
+
* is running with at least one session, non-zero otherwise (including "no
|
|
12
|
+
* server"). This is the boot-time/bash-tooling probe — distinct from
|
|
13
|
+
* `tmuxClientsReachable`, which asks whether a server is reachable at all
|
|
14
|
+
* regardless of session count and is the human-surface gate. */
|
|
15
|
+
export declare function tmuxSessionsReachable(): boolean;
|
|
16
|
+
/** Is the tmux SERVER reachable at all? `tmux list-clients` exits 0 whenever a
|
|
17
|
+
* server is up (even with zero attached clients), non-zero when no server is
|
|
18
|
+
* running. This is the human-surface gate (core/spawn.ts's
|
|
19
|
+
* `tmuxServerReachable`) — distinct from `tmuxSessionsReachable`, which
|
|
20
|
+
* additionally requires a session to exist. */
|
|
21
|
+
export declare function tmuxClientsReachable(): boolean;
|
|
10
22
|
/** The pane currently displaying `nodeId`, found by the authoritative `@crtr_node`
|
|
11
23
|
* pane tag — set by EVERY viewer (a bare `surface attach`, a focus split, a
|
|
12
24
|
* spawned viewer) on connect. Scans all panes (`list-panes -a`); the reverse of
|
|
@@ -7,9 +7,8 @@ import { execFile, spawnSync } from 'node:child_process';
|
|
|
7
7
|
import { encodeAttachControlInput, } from '../keybindings/index.js';
|
|
8
8
|
import { bundledPiCommandForShell } from './pi-cli.js';
|
|
9
9
|
import { shellQuote } from '../../shared/shell-quote.js';
|
|
10
|
-
|
|
10
|
+
import { envPiBinaryOverride } from '../../shared/env.js';
|
|
11
11
|
// tmux invocation
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
12
|
export { shellQuote };
|
|
14
13
|
function tmux(args) {
|
|
15
14
|
const r = spawnSync('tmux', args, { encoding: 'utf8' });
|
|
@@ -25,6 +24,22 @@ function tmuxOnSocket(socket, args) {
|
|
|
25
24
|
export function inTmux() {
|
|
26
25
|
return process.env['TMUX'] !== undefined && process.env['TMUX'] !== '';
|
|
27
26
|
}
|
|
27
|
+
/** Is any tmux SESSION currently up? `tmux list-sessions` exits 0 iff a server
|
|
28
|
+
* is running with at least one session, non-zero otherwise (including "no
|
|
29
|
+
* server"). This is the boot-time/bash-tooling probe — distinct from
|
|
30
|
+
* `tmuxClientsReachable`, which asks whether a server is reachable at all
|
|
31
|
+
* regardless of session count and is the human-surface gate. */
|
|
32
|
+
export function tmuxSessionsReachable() {
|
|
33
|
+
return spawnSync('tmux', ['list-sessions'], { encoding: 'utf8', stdio: 'ignore' }).status === 0;
|
|
34
|
+
}
|
|
35
|
+
/** Is the tmux SERVER reachable at all? `tmux list-clients` exits 0 whenever a
|
|
36
|
+
* server is up (even with zero attached clients), non-zero when no server is
|
|
37
|
+
* running. This is the human-surface gate (core/spawn.ts's
|
|
38
|
+
* `tmuxServerReachable`) — distinct from `tmuxSessionsReachable`, which
|
|
39
|
+
* additionally requires a session to exist. */
|
|
40
|
+
export function tmuxClientsReachable() {
|
|
41
|
+
return spawnSync('tmux', ['list-clients', '-F', '#{client_name}'], { encoding: 'utf8' }).status === 0;
|
|
42
|
+
}
|
|
28
43
|
/** The pane currently displaying `nodeId`, found by the authoritative `@crtr_node`
|
|
29
44
|
* pane tag — set by EVERY viewer (a bare `surface attach`, a focus split, a
|
|
30
45
|
* spawned viewer) on connect. Scans all panes (`list-panes -a`); the reverse of
|
|
@@ -63,9 +78,7 @@ export function currentTmux() {
|
|
|
63
78
|
const [session, window, pane] = r.stdout.split('\t');
|
|
64
79
|
return { session, window, pane };
|
|
65
80
|
}
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
81
|
// Sessions + windows
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
82
|
export function sessionExists(name) {
|
|
70
83
|
return tmux(['has-session', '-t', name]).ok;
|
|
71
84
|
}
|
|
@@ -517,9 +530,7 @@ export function panePid(pane) {
|
|
|
517
530
|
export function respawnPaneInPlace(pane) {
|
|
518
531
|
return tmux(['respawn-pane', '-k', '-t', pane]).ok;
|
|
519
532
|
}
|
|
520
|
-
// ---------------------------------------------------------------------------
|
|
521
533
|
// pi command assembly
|
|
522
|
-
// ---------------------------------------------------------------------------
|
|
523
534
|
/** Turn a pi argv array into a single shell command string.
|
|
524
535
|
*
|
|
525
536
|
* The binary defaults to `CRTR_PI_BINARY` when that env var is set, else the
|
|
@@ -530,12 +541,10 @@ export function respawnPaneInPlace(pane) {
|
|
|
530
541
|
* explicit `binary` arg still overrides the env. The value may be a multi-word
|
|
531
542
|
* launcher (e.g. `node --import tsx/esm host.ts`); only argv entries are
|
|
532
543
|
* shell-quoted, so a multi-word binary is spliced verbatim ahead of them. */
|
|
533
|
-
export function piCommand(argv, binary =
|
|
544
|
+
export function piCommand(argv, binary = envPiBinaryOverride() ?? bundledPiCommandForShell(shellQuote)) {
|
|
534
545
|
return [binary, ...argv.map(shellQuote)].join(' ');
|
|
535
546
|
}
|
|
536
|
-
// ---------------------------------------------------------------------------
|
|
537
547
|
// Window liveness helpers (used by the supervisor daemon)
|
|
538
|
-
// ---------------------------------------------------------------------------
|
|
539
548
|
/** List all window ids present in `session`. Returns [] if the session does
|
|
540
549
|
* not exist or tmux fails for any reason. Each entry is the raw window id
|
|
541
550
|
* string reported by tmux (e.g. `@1`, `@2`, …). */
|
|
@@ -553,9 +562,7 @@ export function windowAlive(session, window) {
|
|
|
553
562
|
return false;
|
|
554
563
|
return listWindowIds(session).includes(window);
|
|
555
564
|
}
|
|
556
|
-
// ---------------------------------------------------------------------------
|
|
557
565
|
// Focus helpers (used by the placement layer)
|
|
558
|
-
// ---------------------------------------------------------------------------
|
|
559
566
|
/** Activate a window within its session (same-session navigation). Equivalent
|
|
560
567
|
* to `tmux select-window -t <session>:<window>`. Best-effort; never throws. */
|
|
561
568
|
export function selectWindow(session, window) {
|
|
@@ -582,9 +589,7 @@ export function selectPane(pane) {
|
|
|
582
589
|
export function switchClient(session) {
|
|
583
590
|
return tmux(['switch-client', '-t', session]).ok;
|
|
584
591
|
}
|
|
585
|
-
// ---------------------------------------------------------------------------
|
|
586
592
|
// send-keys (chrome — used by the prefix menu + nav bindings)
|
|
587
|
-
// ---------------------------------------------------------------------------
|
|
588
593
|
/** Type a literal (e.g. a `/graph` slash command) into a pane and press Enter
|
|
589
594
|
* (`tmux send-keys -t <pane> '<text>' Enter`). Requires the pane's editor be
|
|
590
595
|
* empty, same limitation as the menu's `/promote` item. Best-effort. */
|
|
@@ -182,9 +182,7 @@ function abandonSpare(nodeId, what, err) {
|
|
|
182
182
|
catch { /* best-effort */ }
|
|
183
183
|
return null;
|
|
184
184
|
}
|
|
185
|
-
// ---------------------------------------------------------------------------
|
|
186
185
|
// Reaping
|
|
187
|
-
// ---------------------------------------------------------------------------
|
|
188
186
|
/** How long an UNCLAIMED spare is worth its resident broker (~150-300 MB).
|
|
189
187
|
* A spare exists to make the NEXT create instant; one nobody has claimed in
|
|
190
188
|
* this long is memory rented for nothing, so it is returned. Over-reaping
|
|
@@ -308,9 +306,7 @@ function mint(recipe) {
|
|
|
308
306
|
}
|
|
309
307
|
})();
|
|
310
308
|
}
|
|
311
|
-
// ---------------------------------------------------------------------------
|
|
312
309
|
// Daemon-start pre-warm
|
|
313
|
-
// ---------------------------------------------------------------------------
|
|
314
310
|
/** How many profiles get a spare at daemon start. Each spare is a resident
|
|
315
311
|
* broker at ~150-300 MB, so this is deliberately tiny. */
|
|
316
312
|
const PREWARM_PROFILES = 2;
|
package/dist/core/scope.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { envProfileId } from '../shared/env.js';
|
|
1
2
|
import { homedir } from 'node:os';
|
|
2
3
|
import { existsSync, statSync } from 'node:fs';
|
|
3
4
|
import { join, resolve, dirname } from 'node:path';
|
|
@@ -105,7 +106,7 @@ function collectAncestorScopeRoots(startDir, userRoot) {
|
|
|
105
106
|
* (`findProjectScopeRootsWithMemory`), a projection layered onto this order
|
|
106
107
|
* rather than part of it: discovery provenance decides precedence, the
|
|
107
108
|
* relationship decides the ceiling, and the two never influence each other. */
|
|
108
|
-
export function findProjectScopeRoots(startDir = process.cwd(), profileId =
|
|
109
|
+
export function findProjectScopeRoots(startDir = process.cwd(), profileId = envProfileId() || null) {
|
|
109
110
|
return resolveProjectScopeRoots(startDir, profileId).roots;
|
|
110
111
|
}
|
|
111
112
|
/** `findProjectScopeRoots` with each root's profile memory ceiling attached —
|
|
@@ -113,7 +114,7 @@ export function findProjectScopeRoots(startDir = process.cwd(), profileId = proc
|
|
|
113
114
|
* walk takes that entry's `memory` (first entry in manifest order wins, even
|
|
114
115
|
* when cwd also reaches the root); every other root takes the neutral
|
|
115
116
|
* `content` ceiling. */
|
|
116
|
-
export function findProjectScopeRootsWithMemory(startDir = process.cwd(), profileId =
|
|
117
|
+
export function findProjectScopeRootsWithMemory(startDir = process.cwd(), profileId = envProfileId() || null) {
|
|
117
118
|
return resolveProjectScopeRoots(startDir, profileId).annotated;
|
|
118
119
|
}
|
|
119
120
|
function resolveProjectScopeRoots(startDir, profileId) {
|
|
@@ -195,7 +196,7 @@ export function ensureProjectScopeRoot(startDir = process.cwd()) {
|
|
|
195
196
|
// cache hit.
|
|
196
197
|
const resolvedStart = resolve(startDir);
|
|
197
198
|
const root = join(resolvedStart, CRTR_DIR_NAME);
|
|
198
|
-
const profileId =
|
|
199
|
+
const profileId = envProfileId() || null;
|
|
199
200
|
// Neutral ceiling: the empty cached stack proves no profile entry's ancestor
|
|
200
201
|
// walk reached this root, since it did not exist to be walked into.
|
|
201
202
|
scopeRootsCache.set(scopeRootsCacheKey(resolvedStart, profileId), {
|
package/dist/core/spawn.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// (tmuxServerReachable) so it can decide whether to surface a prompt or degrade
|
|
5
5
|
// to the inbox-drain follow-up. The canvas runtime has its own
|
|
6
6
|
// one-window-per-node machinery in core/runtime/placement-tmux.ts.
|
|
7
|
-
import {
|
|
7
|
+
import { tmuxClientsReachable } from './runtime/tmux-driver.js';
|
|
8
8
|
/** Does THIS process sit inside a tmux client (its own $TMUX is set)? NOT the
|
|
9
9
|
* gate for the human surface — see tmuxServerReachable. A headless-broker child
|
|
10
10
|
* strips TMUX/TMUX_PANE (broker.ts, anti-stophook-hijack) yet can still drive
|
|
@@ -22,6 +22,5 @@ export function isInTmux() {
|
|
|
22
22
|
* no server is running — the true "no tmux to surface into" case that should
|
|
23
23
|
* degrade to the inbox-drain follow-up. */
|
|
24
24
|
export function tmuxServerReachable() {
|
|
25
|
-
|
|
26
|
-
return r.status === 0;
|
|
25
|
+
return tmuxClientsReachable();
|
|
27
26
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createHash } from
|
|
3
|
-
import { homedir } from
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { atomicWriteJson } from './fs-utils.js';
|
|
6
|
+
import { LockedJsonFile } from './locked-file.js';
|
|
6
7
|
export const ANTHROPIC_PROVIDER_ID = "anthropic";
|
|
7
8
|
export const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
|
|
8
9
|
export const INVALID_REFRESH_TOKEN_BACKOFF_MS = 30 * 24 * 60 * 60 * 1000;
|
|
@@ -123,14 +124,6 @@ function parsePoolUnderLock(path, current) {
|
|
|
123
124
|
throw new Error(`Subscription pool ${path} is corrupt: ${error instanceof Error ? error.message : String(error)}`);
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
|
-
function writeJsonFile(path, value, mode) {
|
|
127
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
128
|
-
const tmp = `${path}.${process.pid}.tmp`;
|
|
129
|
-
writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, mode === undefined ? "utf8" : { encoding: "utf8", mode });
|
|
130
|
-
if (mode !== undefined)
|
|
131
|
-
chmodSync(tmp, mode);
|
|
132
|
-
renameSync(tmp, path);
|
|
133
|
-
}
|
|
134
127
|
function normalizeStrength(value) {
|
|
135
128
|
return value === "ultra" || value === "strong" || value === "medium" || value === "light" ? value : undefined;
|
|
136
129
|
}
|
|
@@ -212,13 +205,13 @@ function assertCandidateRowsStorable(providerId, candidate) {
|
|
|
212
205
|
// The ONE door every write to a pool file goes through. Both pool invariants -- one account
|
|
213
206
|
// identity per pool, and every candidate row carrying a storable credential -- are properties
|
|
214
207
|
// of the FILE, not of whichever code path happens to be writing, so they are asserted here
|
|
215
|
-
// against the state this write replaces, under the caller's already-held lock. `
|
|
216
|
-
// is reached for the pool NOWHERE ELSE: that makes the invariants unbypassable.
|
|
208
|
+
// against the state this write replaces, under the caller's already-held lock. `atomicWriteJson`
|
|
209
|
+
// is reached for the pool NOWHERE ELSE in this file: that makes the invariants unbypassable.
|
|
217
210
|
function persistPool(providerId, poolFile, base, candidate) {
|
|
218
211
|
assertCandidateRowsStorable(providerId, candidate);
|
|
219
212
|
const next = normalizeCredentials(candidate);
|
|
220
213
|
logCredentialSetChange(providerId, base, next);
|
|
221
|
-
|
|
214
|
+
atomicWriteJson(poolFile, next, { mode: 0o600 });
|
|
222
215
|
return next;
|
|
223
216
|
}
|
|
224
217
|
// Accounts vanishing from a pool is the failure users actually feel ("I keep having to log in
|
|
@@ -721,7 +714,7 @@ export function writeRotationConfig(next) {
|
|
|
721
714
|
defaultFallbackStrength: normalizeStrength(next.defaultFallbackStrength) ?? current.defaultFallbackStrength ?? DEFAULT_FALLBACK_STRENGTH,
|
|
722
715
|
revertWhenAvailable: next.revertWhenAvailable ?? current.revertWhenAvailable ?? true,
|
|
723
716
|
};
|
|
724
|
-
|
|
717
|
+
atomicWriteJson(rotationConfigFile(), merged);
|
|
725
718
|
return merged;
|
|
726
719
|
}
|
|
727
720
|
export function formatStatusLine(providerId, now = Date.now()) {
|
|
@@ -28,7 +28,7 @@ const SUBJECT = {
|
|
|
28
28
|
function entry(match, gate) {
|
|
29
29
|
return { on: 'pre-command', at: 'content', match, ...(gate !== undefined ? { gate } : {}) };
|
|
30
30
|
}
|
|
31
|
-
//
|
|
31
|
+
// the behaviour this event adds
|
|
32
32
|
test('a matched invocation inside a heredoc BODY does not hold the command', () => {
|
|
33
33
|
const command = [
|
|
34
34
|
"crtr memory write approvals --body <<'EOF'",
|
|
@@ -60,7 +60,7 @@ test('a `command` entry is never consumed by the pre-command matcher, nor the re
|
|
|
60
60
|
assert.equal(matchesPreCommandEntry(post, SUBJECT, 'crtr integration run --action send'), false);
|
|
61
61
|
assert.equal(matchesCommandEntry(entry(['crtr integration run*']), SUBJECT, 'crtr integration run --action send'), false);
|
|
62
62
|
});
|
|
63
|
-
//
|
|
63
|
+
// the rung fold
|
|
64
64
|
test('the rung fold returns the highest `at` across matching entries, and none when nothing matches', () => {
|
|
65
65
|
const doc = {
|
|
66
66
|
surfaces: [
|
|
@@ -72,7 +72,7 @@ test('the rung fold returns the highest `at` across matching entries, and none w
|
|
|
72
72
|
assert.equal(preCommandDeliveryRung(doc, SUBJECT, 'crtr integration run --action send'), 'content');
|
|
73
73
|
assert.equal(preCommandDeliveryRung(doc, SUBJECT, 'ls -la'), 'none');
|
|
74
74
|
});
|
|
75
|
-
//
|
|
75
|
+
// shared routing: one command-glob semantics, not two
|
|
76
76
|
test('outside a heredoc, pre-command matching is exactly the `command` matcher', () => {
|
|
77
77
|
// The guard against a second command tokenizer drifting away from the first.
|
|
78
78
|
// Whatever `command` globs mean today, `pre-command` means the same thing —
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type SurfaceEntry } from './schema.js';
|
|
2
|
+
export declare const SUPPRESSIBLE_RULES: readonly string[];
|
|
2
3
|
/** Strict validation of one raw routing entry. Runtime parsing deliberately
|
|
3
4
|
* tolerates malformed docs; this authoring/preflight contract does not. */
|
|
4
5
|
export declare function lintSubstrateSurfaces(value: unknown): string | null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isDocKind, parseSubstrateFrontmatter, SURFACE_EVENTS, SURFACE_RUNGS } from './schema.js';
|
|
2
2
|
const RETIRED_FIELDS = ['system-prompt-visibility', 'file-read-visibility', 'applies-to', 'read-when'];
|
|
3
3
|
const SURFACE_ENTRY_KEYS = ['on', 'match', 'match-frontmatter', 'gate', 'at'];
|
|
4
|
-
const SUPPRESSIBLE_RULES = ['length', 'broad-memory-read'];
|
|
4
|
+
export const SUPPRESSIBLE_RULES = ['length', 'broad-memory-read'];
|
|
5
5
|
/** Strict validation of one raw routing entry. Runtime parsing deliberately
|
|
6
6
|
* tolerates malformed docs; this authoring/preflight contract does not. */
|
|
7
7
|
export function lintSubstrateSurfaces(value) {
|