@north-light/crouter 0.3.233 → 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-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.js +4 -7
- 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.js +3 -3
- 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__/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/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.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
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { captureSessionChatCapabilities } from '../command-surface.js';
|
|
6
|
+
import { isPreBoundaryReviewEntry } from './frame-replies.js';
|
|
7
|
+
/** True iff a parsed JSONL branch-entry line is a hidden (`display:false`)
|
|
8
|
+
* custom message — the general display boundary crossed if left in an
|
|
9
|
+
* exported branch (not just `crtr-memory-refs` guidance; any hidden custom
|
|
10
|
+
* content). Covers both persisted shapes a branch entry can take: a
|
|
11
|
+
* `type:'custom_message'` entry (display at the entry's own top level) and a
|
|
12
|
+
* `type:'message'` entry wrapping a `role:'custom'` message (display nested
|
|
13
|
+
* under `.message`). */
|
|
14
|
+
export function isHiddenJsonlBranchEntry(entry) {
|
|
15
|
+
if (entry === null || typeof entry !== 'object')
|
|
16
|
+
return false;
|
|
17
|
+
const e = entry;
|
|
18
|
+
if (e.type === 'custom_message' && e.display === false)
|
|
19
|
+
return true;
|
|
20
|
+
if (e.type === 'message' && e.message?.role === 'custom' && e.message.display === false)
|
|
21
|
+
return true;
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
/** Rewrite an already-exported JSONL file in place, dropping every hidden
|
|
25
|
+
* (`display:false`) branch entry while re-chaining `parentId` so the
|
|
26
|
+
* remaining entries stay a single valid linear branch — applying the general
|
|
27
|
+
* display:false boundary to `session.exportToJsonl`'s own output, which
|
|
28
|
+
* serializes every entry verbatim regardless of display. The header line
|
|
29
|
+
* (line 1) is copied through untouched. */
|
|
30
|
+
export function stripHiddenEntriesFromJsonlExport(filePath) {
|
|
31
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
32
|
+
const lines = raw.split('\n').filter((l) => l !== '');
|
|
33
|
+
if (lines.length === 0)
|
|
34
|
+
return;
|
|
35
|
+
const [headerLine, ...entryLines] = lines;
|
|
36
|
+
let prevId = null;
|
|
37
|
+
const kept = [];
|
|
38
|
+
for (const line of entryLines) {
|
|
39
|
+
const entry = JSON.parse(line);
|
|
40
|
+
if (isHiddenJsonlBranchEntry(entry))
|
|
41
|
+
continue;
|
|
42
|
+
kept.push(JSON.stringify({ ...entry, parentId: prevId }));
|
|
43
|
+
if (typeof entry.id === 'string')
|
|
44
|
+
prevId = entry.id;
|
|
45
|
+
}
|
|
46
|
+
writeFileSync(filePath, `${[headerLine, ...kept].join('\n')}\n`);
|
|
47
|
+
}
|
|
48
|
+
export function createSessionHandler(deps) {
|
|
49
|
+
const { registry, replies, rebind, session, boundaryReviewId, memoryRefs, reWelcomeAll } = deps;
|
|
50
|
+
return (client, frame) => {
|
|
51
|
+
switch (frame.type) {
|
|
52
|
+
case 'navigate_tree': {
|
|
53
|
+
if (replies.notWritable(client, 'navigate the session tree'))
|
|
54
|
+
break;
|
|
55
|
+
if (isPreBoundaryReviewEntry(session(), boundaryReviewId, frame.targetId)) {
|
|
56
|
+
replies.reviewBoundaryRefusalTo(client, frame.targetId);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
// navigateTree rewinds IN-PLACE (same session file, new leaf) and emits no
|
|
60
|
+
// relayed event, so every viewer must be re-snapshotted onto the rewound
|
|
61
|
+
// transcript — same reWelcomeAll the session-replacing ops use. The ack's
|
|
62
|
+
// detail carries the navigated-to user message's text (pi parity: the
|
|
63
|
+
// interactive tree navigator restores it to the editor for re-editing).
|
|
64
|
+
void session()
|
|
65
|
+
.navigateTree(frame.targetId, frame.options)
|
|
66
|
+
.then((r) => {
|
|
67
|
+
if (!r.cancelled)
|
|
68
|
+
reWelcomeAll();
|
|
69
|
+
replies.ackTo(client, 'navigate_tree', !r.cancelled, r.editorText);
|
|
70
|
+
})
|
|
71
|
+
.catch(replies.engineErrorTo(client));
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'reload': {
|
|
75
|
+
if (replies.notWritable(client, 'reload'))
|
|
76
|
+
break;
|
|
77
|
+
// A SUCCESSFUL reload invalidates the memoized ref inventory (work item
|
|
78
|
+
// 3) so the next read-op/submission re-walks the corpus. A FAILED
|
|
79
|
+
// reload (`.catch`) leaves it untouched: nothing about the corpus is
|
|
80
|
+
// known to have changed.
|
|
81
|
+
void session()
|
|
82
|
+
.reload({ beforeSessionStart: () => captureSessionChatCapabilities(session()) })
|
|
83
|
+
.then(() => {
|
|
84
|
+
// pi's reload() re-syncs queue modes from the shared settings.json
|
|
85
|
+
// (its ONE post-bind syncQueueModesFromSettings call site) —
|
|
86
|
+
// re-apply the broker's 'all' modes so a reload cannot silently
|
|
87
|
+
// restore one-at-a-time draining (see rebind.enqueueRebind).
|
|
88
|
+
const agent = session().agent;
|
|
89
|
+
if (agent) {
|
|
90
|
+
agent.steeringMode = 'all';
|
|
91
|
+
agent.followUpMode = 'all';
|
|
92
|
+
}
|
|
93
|
+
memoryRefs.invalidate();
|
|
94
|
+
registry.forEachHelloed((observer) => {
|
|
95
|
+
if (observer.role === 'observer')
|
|
96
|
+
registry.sendFrame(observer, { type: 'resources_reloaded' });
|
|
97
|
+
});
|
|
98
|
+
replies.ackTo(client, 'reload');
|
|
99
|
+
})
|
|
100
|
+
.catch(replies.engineErrorTo(client));
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case 'export': {
|
|
104
|
+
if (replies.notWritable(client, 'export the session'))
|
|
105
|
+
break;
|
|
106
|
+
if (frame.format === 'jsonl') {
|
|
107
|
+
try {
|
|
108
|
+
const outPath = session().exportToJsonl(frame.path);
|
|
109
|
+
stripHiddenEntriesFromJsonlExport(outPath);
|
|
110
|
+
replies.ackTo(client, 'export');
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
replies.engineErrorTo(client)(err);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
void session()
|
|
118
|
+
.exportToHtml(frame.path)
|
|
119
|
+
.then(() => replies.ackTo(client, 'export'))
|
|
120
|
+
.catch(replies.engineErrorTo(client));
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
case 'new_session': {
|
|
125
|
+
if (replies.notWritable(client, 'start a new session'))
|
|
126
|
+
break;
|
|
127
|
+
rebind.runReplacement(client, 'new_session', (rt) => rt.newSession());
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'switch_session': {
|
|
131
|
+
if (replies.notWritable(client, 'switch sessions'))
|
|
132
|
+
break;
|
|
133
|
+
rebind.runReplacement(client, 'switch_session', (rt) => rt.switchSession(frame.path));
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case 'fork': {
|
|
137
|
+
if (replies.notWritable(client, 'fork the session'))
|
|
138
|
+
break;
|
|
139
|
+
if (isPreBoundaryReviewEntry(session(), boundaryReviewId, frame.entryId)) {
|
|
140
|
+
replies.reviewBoundaryRefusalTo(client, frame.entryId);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
rebind.runReplacement(client, 'fork', (rt) => rt.fork(frame.entryId));
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case 'clone': {
|
|
147
|
+
if (replies.notWritable(client, 'clone the session'))
|
|
148
|
+
break;
|
|
149
|
+
rebind.runReplacement(client, 'clone', async (rt) => {
|
|
150
|
+
const sm = session().sessionManager;
|
|
151
|
+
const leafId = sm.getLeafId();
|
|
152
|
+
if (leafId === null) {
|
|
153
|
+
return { cancelled: true, detail: 'no current leaf to clone from' };
|
|
154
|
+
}
|
|
155
|
+
const newPath = sm.createBranchedSession(leafId);
|
|
156
|
+
if (newPath === undefined) {
|
|
157
|
+
return {
|
|
158
|
+
cancelled: true,
|
|
159
|
+
detail: 'createBranchedSession returned no path',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return rt.switchSession(newPath);
|
|
163
|
+
});
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case 'share': {
|
|
167
|
+
if (replies.notWritable(client, 'share the session'))
|
|
168
|
+
break;
|
|
169
|
+
const tmpPath = join(tmpdir(), `pi-share-${Date.now()}.html`);
|
|
170
|
+
void session()
|
|
171
|
+
.exportToHtml(tmpPath)
|
|
172
|
+
.then(() => new Promise((resolve, reject) => {
|
|
173
|
+
execFile('gh', ['gist', 'create', '--secret', tmpPath], { timeout: 30_000, maxBuffer: 1024 * 1024 }, (err, stdout) => {
|
|
174
|
+
try {
|
|
175
|
+
if (existsSync(tmpPath))
|
|
176
|
+
unlinkSync(tmpPath);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
/* cleanup */
|
|
180
|
+
}
|
|
181
|
+
if (err) {
|
|
182
|
+
reject(err);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
resolve(stdout.trim());
|
|
186
|
+
});
|
|
187
|
+
}))
|
|
188
|
+
.then((url) => {
|
|
189
|
+
replies.ackTo(client, 'share', true, url);
|
|
190
|
+
})
|
|
191
|
+
.catch((err) => {
|
|
192
|
+
const msg = String(err?.message ?? err);
|
|
193
|
+
replies.ackTo(client, 'share', false, msg.includes('not found') ||
|
|
194
|
+
msg.includes('gh: command not found') ||
|
|
195
|
+
msg.includes('ENOENT')
|
|
196
|
+
? '`gh` CLI not found — install GitHub CLI and authenticate with `gh auth login`'
|
|
197
|
+
: `share failed: ${msg}`);
|
|
198
|
+
});
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { BrokerSession } from './read-ops.js';
|
|
2
|
+
/**
|
|
3
|
+
* Prompt dispatches whose promise has not yet settled. `session.prompt()` does
|
|
4
|
+
* async setup (input transforms, model/auth validation, compaction) BEFORE
|
|
5
|
+
* `_isAgentRunActive` flips, so for a short window after a routed deliver the
|
|
6
|
+
* engine reads idle and `session.abort()` is a silent no-op — an interrupt
|
|
7
|
+
* racing that window would miss the turn entirely (verified live). The counter
|
|
8
|
+
* lets a caller distinguish "genuinely idle" from "turn still igniting".
|
|
9
|
+
*/
|
|
10
|
+
export type PromptDispatchTracker = {
|
|
11
|
+
track(p: Promise<unknown>): Promise<unknown>;
|
|
12
|
+
pending(): number;
|
|
13
|
+
/** Wait (bounded) for a pending dispatch to reach an abortable turn — or for
|
|
14
|
+
* every dispatch to settle without one (an early return). Returns
|
|
15
|
+
* immediately when the engine is already streaming or nothing is pending. */
|
|
16
|
+
waitUntilAbortable(): Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
export declare function createPromptDispatchTracker(session: () => BrokerSession): PromptDispatchTracker;
|
|
19
|
+
/**
|
|
20
|
+
* The one-shot waiters a `deliver` frame parks until its prompt is ACCEPTED by
|
|
21
|
+
* the engine (the broker's `agent_start`). Every waiter fires once, on the
|
|
22
|
+
* first acceptance after it was added.
|
|
23
|
+
*/
|
|
24
|
+
export type TurnAcceptanceGate = {
|
|
25
|
+
waitFor(waiter: () => void): void;
|
|
26
|
+
cancel(waiter: () => void): void;
|
|
27
|
+
notifyAccepted(): void;
|
|
28
|
+
};
|
|
29
|
+
export declare function createTurnAcceptanceGate(): TurnAcceptanceGate;
|
|
30
|
+
/**
|
|
31
|
+
* The launch prompt, held until the session actually has a model. A model-less
|
|
32
|
+
* tier-3 boot leaves it pending; `drivePending` is a no-op until the
|
|
33
|
+
* `reload_auth` re-drive resolves a model, at which point it fires.
|
|
34
|
+
*/
|
|
35
|
+
export type KickoffDriver = {
|
|
36
|
+
kickoff(prompt: string): void;
|
|
37
|
+
drivePending(): void;
|
|
38
|
+
};
|
|
39
|
+
export declare function createKickoffDriver(deps: {
|
|
40
|
+
session: () => BrokerSession;
|
|
41
|
+
dispatches: PromptDispatchTracker;
|
|
42
|
+
}): KickoffDriver;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { emitEvent } from '../../events/emit.js';
|
|
2
|
+
import { operationIdContext } from '../../events/operation-id.js';
|
|
3
|
+
import { isUnknownModel } from '../model-selection.js';
|
|
4
|
+
/** How long a caller waits for a dispatched prompt to become abortable, and how
|
|
5
|
+
* often it re-reads the engine while waiting. */
|
|
6
|
+
const ABORTABLE_WAIT_MS = 5_000;
|
|
7
|
+
const ABORTABLE_POLL_MS = 25;
|
|
8
|
+
export function createPromptDispatchTracker(session) {
|
|
9
|
+
let pending = 0;
|
|
10
|
+
return {
|
|
11
|
+
track(p) {
|
|
12
|
+
pending++;
|
|
13
|
+
const tracked = Promise.resolve(p);
|
|
14
|
+
void tracked.finally(() => {
|
|
15
|
+
pending--;
|
|
16
|
+
});
|
|
17
|
+
return tracked;
|
|
18
|
+
},
|
|
19
|
+
pending: () => pending,
|
|
20
|
+
async waitUntilAbortable() {
|
|
21
|
+
if (session().isStreaming || pending === 0)
|
|
22
|
+
return;
|
|
23
|
+
const deadline = Date.now() + ABORTABLE_WAIT_MS;
|
|
24
|
+
while (Date.now() < deadline && !session().isStreaming && pending > 0) {
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, ABORTABLE_POLL_MS));
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function createTurnAcceptanceGate() {
|
|
31
|
+
const waiters = new Set();
|
|
32
|
+
return {
|
|
33
|
+
waitFor: (waiter) => void waiters.add(waiter),
|
|
34
|
+
cancel: (waiter) => void waiters.delete(waiter),
|
|
35
|
+
notifyAccepted() {
|
|
36
|
+
const pending = [...waiters];
|
|
37
|
+
waiters.clear();
|
|
38
|
+
for (const waiter of pending)
|
|
39
|
+
waiter();
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function createKickoffDriver(deps) {
|
|
44
|
+
const { session, dispatches } = deps;
|
|
45
|
+
let pendingPrompt;
|
|
46
|
+
let inFlight = false;
|
|
47
|
+
const sessionHasNoModel = () => session().model === undefined || isUnknownModel(session().model);
|
|
48
|
+
const drivePending = () => {
|
|
49
|
+
if (pendingPrompt === undefined || inFlight || sessionHasNoModel())
|
|
50
|
+
return;
|
|
51
|
+
const prompt = pendingPrompt;
|
|
52
|
+
pendingPrompt = undefined;
|
|
53
|
+
inFlight = true;
|
|
54
|
+
dispatches.track(operationIdContext.fresh(() => session()
|
|
55
|
+
.prompt(prompt)
|
|
56
|
+
.catch((err) => {
|
|
57
|
+
emitEvent({
|
|
58
|
+
level: 'error',
|
|
59
|
+
event: 'broker.prompt.first_failed',
|
|
60
|
+
error: err,
|
|
61
|
+
});
|
|
62
|
+
})
|
|
63
|
+
.finally(() => {
|
|
64
|
+
inFlight = false;
|
|
65
|
+
})));
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
kickoff(prompt) {
|
|
69
|
+
pendingPrompt = prompt;
|
|
70
|
+
drivePending();
|
|
71
|
+
},
|
|
72
|
+
drivePending,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -1,118 +1,42 @@
|
|
|
1
1
|
// Pure broker-side render adapters. crtrd supplies the fixed extension-state
|
|
2
2
|
// DTO; these functions only combine it with node-local/filesystem information.
|
|
3
3
|
import { contextDir, nodeDir } from '../canvas/paths.js';
|
|
4
|
-
import { execSync, spawnSync } from 'node:child_process';
|
|
5
|
-
import { readdirSync } from 'node:fs';
|
|
6
|
-
import { hostname } from 'node:os';
|
|
7
4
|
import { interpolateNodePaths } from '../canvas/paths.js';
|
|
8
5
|
import { fullName } from '../canvas/labels.js';
|
|
9
6
|
import { renderKnowledgeForSubject, renderPreferencesForSubject } from '../substrate/render.js';
|
|
10
7
|
import { renderWorkspaceOpenDocsForSubject } from '../substrate/on-read.js';
|
|
11
|
-
import {
|
|
8
|
+
import { buildProjectContextBlock, contextDirNote as contextDirNoteShared, profileNote as profileNoteShared, relativeLabel as relativeLabelShared, renderGraphMap, buildIdentityAssertion, worktreeNote as worktreeNoteShared, } from './bearings-render.js';
|
|
12
9
|
export function editorLabelForBrokerNode(node) {
|
|
13
10
|
const icon = (node.icon ?? '').trim();
|
|
14
11
|
const handle = node.name !== node.kind ? node.name.trim() : '';
|
|
15
12
|
const name = handle !== '' ? handle : (node.description ?? '').trim() || node.name;
|
|
16
13
|
return icon === '' ? name : `${icon} ${name}`;
|
|
17
14
|
}
|
|
18
|
-
function command(cwd, text) {
|
|
19
|
-
try {
|
|
20
|
-
return execSync(text, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return '';
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
15
|
/** Same environment-only project context rendered at normal boot, without a
|
|
27
16
|
* canvas import. */
|
|
28
17
|
export function buildProjectContextBlockForBroker(cwd) {
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
const entries = readdirSync(cwd, { withFileTypes: true }).sort((a, b) => Number(b.isDirectory()) - Number(a.isDirectory()) || a.name.localeCompare(b.name));
|
|
32
|
-
const shown = entries.slice(0, 50).map((entry) => `${entry.name}${entry.isDirectory() ? '/' : ''}`);
|
|
33
|
-
listing = entries.length > shown.length ? [...shown, `[+${entries.length - shown.length} more]`] : shown;
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
listing = ['Directory listing: unavailable (cwd missing or unreadable).'];
|
|
37
|
-
}
|
|
38
|
-
const tmux = spawnSync('tmux', ['list-sessions'], { encoding: 'utf8', stdio: 'ignore' }).status === 0;
|
|
39
|
-
let git = 'Git: not a git repository.';
|
|
40
|
-
let worktrees = 'Worktrees: none.';
|
|
41
|
-
if (command(cwd, 'git rev-parse --is-inside-work-tree') !== '') {
|
|
42
|
-
const branch = command(cwd, 'git branch --show-current') || command(cwd, 'git rev-parse --abbrev-ref HEAD') || 'HEAD';
|
|
43
|
-
const sha = command(cwd, 'git rev-parse --short HEAD') || 'unknown';
|
|
44
|
-
const changes = command(cwd, 'git status --porcelain').split('\n').filter(Boolean).length;
|
|
45
|
-
const raw = command(cwd, 'git worktree list --porcelain').split('\n');
|
|
46
|
-
const found = [];
|
|
47
|
-
let path = '';
|
|
48
|
-
let wtBranch = 'detached HEAD';
|
|
49
|
-
for (const line of raw) {
|
|
50
|
-
if (line.startsWith('worktree ')) {
|
|
51
|
-
if (path !== '')
|
|
52
|
-
found.push(`${path} (${wtBranch})`);
|
|
53
|
-
path = line.slice(9);
|
|
54
|
-
wtBranch = 'detached HEAD';
|
|
55
|
-
}
|
|
56
|
-
else if (line.startsWith('branch '))
|
|
57
|
-
wtBranch = line.slice(7).replace(/^refs\/heads\//, '');
|
|
58
|
-
}
|
|
59
|
-
if (path !== '')
|
|
60
|
-
found.push(`${path} (${wtBranch})`);
|
|
61
|
-
git = `Git: ${branch} @ ${sha}; ${changes === 0 ? 'clean' : `${changes} uncommitted change${changes === 1 ? '' : 's'}`}; ${found.length} worktree${found.length === 1 ? '' : 's'}.`;
|
|
62
|
-
worktrees = found.length === 0 ? 'Worktrees: none.' : `Worktrees: ${found.join(', ')}`;
|
|
63
|
-
}
|
|
64
|
-
return `<project_context>\n\n<environment cwd="${cwd.replaceAll('&', '&').replaceAll('"', '"').replaceAll('<', '<')}">\nHost: ${process.platform} ${process.arch} · ${hostname()} · node ${process.version} · tmux server: ${tmux ? 'reachable' : 'none'}\nDirectory:\n${listing.map((line) => ` ${line}`).join('\n')}\n${git}\n${worktrees}\n</environment>\n</project_context>`;
|
|
18
|
+
return buildProjectContextBlock(cwd);
|
|
65
19
|
}
|
|
66
20
|
function contextDirNote(nodeId) {
|
|
67
|
-
return
|
|
21
|
+
return contextDirNoteShared(nodeId);
|
|
68
22
|
}
|
|
69
23
|
function profileNote(node) {
|
|
70
|
-
|
|
71
|
-
return '';
|
|
72
|
-
try {
|
|
73
|
-
const entry = loadProfileManifest(node.profile_id);
|
|
74
|
-
if (entry.manifest.projects.length === 0)
|
|
75
|
-
return '';
|
|
76
|
-
return `Your working dir is \`${node.cwd}\`. Your profile **${entry.manifest.name}** also has purview over: ${entry.manifest.projects.map((project) => project.path).join(', ')}.`;
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return '';
|
|
80
|
-
}
|
|
24
|
+
return profileNoteShared(node.cwd, node.profile_id);
|
|
81
25
|
}
|
|
82
26
|
function relativeLabel(node) {
|
|
83
|
-
|
|
84
|
-
const descPart = desc !== '' && desc !== node.kind && desc !== node.name ? ` — ${desc}` : '';
|
|
85
|
-
return `${node.node_id} (${node.kind} · ${node.status})${descPart}`;
|
|
27
|
+
return relativeLabelShared(node);
|
|
86
28
|
}
|
|
87
29
|
function graphMap(state) {
|
|
88
30
|
const { ancestors, children, node } = state;
|
|
89
|
-
|
|
90
|
-
return '';
|
|
91
|
-
const indent = (depth) => ' '.repeat(depth);
|
|
92
|
-
const lines = ['Your place in the canvas graph (ancestry above you, your direct children below):'];
|
|
93
|
-
ancestors.forEach((ancestor, index) => lines.push(`${indent(index)}${index === 0 ? '' : '└ '}${relativeLabel(ancestor)}`));
|
|
94
|
-
const depth = ancestors.length;
|
|
95
|
-
lines.push(`${indent(depth)}${depth === 0 ? '' : '└ '}● you: ${node.node_id} (${node.kind}, ${node.mode})`);
|
|
96
|
-
const shown = children.slice(0, 5);
|
|
97
|
-
shown.forEach((child, index) => lines.push(`${indent(depth + 1)}${index === shown.length - 1 && children.length === shown.length ? '└ ' : '├ '}${relativeLabel(child)}`));
|
|
98
|
-
if (children.length > shown.length)
|
|
99
|
-
lines.push(`${indent(depth + 1)}└ +${children.length - shown.length} more`);
|
|
100
|
-
return lines.join('\n');
|
|
31
|
+
return renderGraphMap(node.node_id, node.kind, node.mode, ancestors, children);
|
|
101
32
|
}
|
|
102
33
|
function identity(state) {
|
|
103
34
|
const { node, fork_source: source } = state;
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
const sourceLabel = source === null ? node.fork_from : `${node.fork_from} ("${fullName(source)}")`;
|
|
107
|
-
lines.push('', `You are a new FORK of ${sourceLabel}, not ${sourceLabel}. Pi copied the earlier conversation into this session. Treat that first-person history as reference material, not your own past.`);
|
|
108
|
-
}
|
|
109
|
-
return lines.join('\n');
|
|
35
|
+
const sourceLabel = source === null ? (node.fork_from ?? '') : `${node.fork_from} ("${fullName(source)}")`;
|
|
36
|
+
return buildIdentityAssertion(node.node_id, node.kind, node.mode, node.fork_from, sourceLabel);
|
|
110
37
|
}
|
|
111
38
|
function worktreeNote(node) {
|
|
112
|
-
|
|
113
|
-
if (worktree === null || worktree === undefined || worktree.state !== 'open')
|
|
114
|
-
return '';
|
|
115
|
-
return `Branch: \`${worktree.branch}\`\nWorktree: \`${worktree.path}\`\nBase: \`${worktree.base_ref}\` @ \`${worktree.base_sha}\`\nCommit work here. Land it with \`crtr node worktree close\` before finishing. Auto-drop applies only when the worktree is clean (no uncommitted changes) and nothing remains to land (zero commits ahead of the base, or its commits are already contained in the base); unlanded commits (commits not already contained in the base) or uncommitted changes require an explicit close/landing decision.`;
|
|
39
|
+
return worktreeNoteShared(node.managed_worktree);
|
|
116
40
|
}
|
|
117
41
|
/** The companion note — rendered ONLY when a node carries its immutable
|
|
118
42
|
* companion binding. Names the target (the document under review, or the page
|
|
@@ -16,9 +16,7 @@
|
|
|
16
16
|
// pi. pi names the correlation field `id` (design §5.2 wrote `request_id`); we
|
|
17
17
|
// follow pi.
|
|
18
18
|
import { StringDecoder } from 'node:string_decoder';
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
19
|
// Client → broker frames
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
20
|
/** Largest transcript window a client may request in its welcome snapshot. */
|
|
23
21
|
export const SNAPSHOT_TAIL_MAX = 500;
|
|
24
22
|
/** Invalid requests preserve the complete welcome; valid requests are capped. */
|
|
@@ -27,10 +25,8 @@ export function normalizeSnapshotTail(value) {
|
|
|
27
25
|
return undefined;
|
|
28
26
|
return Math.min(value, SNAPSHOT_TAIL_MAX);
|
|
29
27
|
}
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
28
|
// Newline-delimited JSON codec (the framing pi solves with `jsonl`, reproduced
|
|
32
29
|
// locally so the protocol module owns no transport dependency)
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
30
|
/** Encode one frame as a single newline-terminated JSON line. */
|
|
35
31
|
export function encodeFrame(frame) {
|
|
36
32
|
return JSON.stringify(frame) + '\n';
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { execFileSync } from 'node:child_process';
|
|
11
11
|
import { readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
|
+
import { envBrokerEngine } from '../../shared/env.js';
|
|
13
14
|
import { createAgentSessionServices, createAgentSessionFromServices, createAgentSessionRuntime, SessionManager, VERSION, } from '@earendil-works/pi-coding-agent';
|
|
14
15
|
import { crtrHome } from '../canvas/paths.js';
|
|
15
16
|
import { emitEvent } from '../events/emit.js';
|
|
@@ -24,7 +25,7 @@ export { createAgentSessionServices, createAgentSessionFromServices, createAgent
|
|
|
24
25
|
* through here so the seam stays a single, auditable swap.
|
|
25
26
|
*/
|
|
26
27
|
export async function loadBrokerEngine() {
|
|
27
|
-
const spec =
|
|
28
|
+
const spec = envBrokerEngine();
|
|
28
29
|
const mod = (await import(spec));
|
|
29
30
|
if (typeof mod.createAgentSessionServices !== 'function' ||
|
|
30
31
|
typeof mod.createAgentSessionFromServices !== 'function' ||
|
|
@@ -7,7 +7,7 @@ import { type BrokerToClient, type RpcExtensionUIRequest, type RpcExtensionUIRes
|
|
|
7
7
|
/** A review companion may only navigate or fork within a branch whose ancestry
|
|
8
8
|
* has crossed its durable transcript boundary. The broker sends this typed local
|
|
9
9
|
* failure as a dedicated error frame without invoking pi's tree mutation. */
|
|
10
|
-
export { ReviewBoundaryNavigationError, REVIEW_BOUNDARY_NAVIGATION_CODE } from './broker/frame-
|
|
10
|
+
export { ReviewBoundaryNavigationError, REVIEW_BOUNDARY_NAVIGATION_CODE } from './broker/frame-replies.js';
|
|
11
11
|
export type { SubscriptionGeneration } from './broker/rebind.js';
|
|
12
12
|
/** A blocking dialog awaiting a response from ANY writable client, the
|
|
13
13
|
* broker-side default timeout, or the engine's abort. The dialog is fanned out
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// + model registry are real SDK construction helpers (NOT part of the swappable
|
|
17
17
|
// engine seam — the fake engine receives the real, extension-loaded loader so
|
|
18
18
|
// the real canvas-stophook fires session_start).
|
|
19
|
+
import { envModelIntent, envNodeCwd, envProfileId } from '../../shared/env.js';
|
|
19
20
|
import { createServer } from 'node:net';
|
|
20
21
|
import { existsSync, readFileSync, unlinkSync } from 'node:fs';
|
|
21
22
|
import { join } from 'node:path';
|
|
@@ -54,7 +55,7 @@ import { FrameOverflowError, normalizeSnapshotTail, } from './broker-protocol.js
|
|
|
54
55
|
/** A review companion may only navigate or fork within a branch whose ancestry
|
|
55
56
|
* has crossed its durable transcript boundary. The broker sends this typed local
|
|
56
57
|
* failure as a dedicated error frame without invoking pi's tree mutation. */
|
|
57
|
-
export { ReviewBoundaryNavigationError, REVIEW_BOUNDARY_NAVIGATION_CODE } from './broker/frame-
|
|
58
|
+
export { ReviewBoundaryNavigationError, REVIEW_BOUNDARY_NAVIGATION_CODE } from './broker/frame-replies.js';
|
|
58
59
|
/**
|
|
59
60
|
* pi 0.82 made `ModelRuntime` the canonical model/auth surface and demoted
|
|
60
61
|
* `ModelRegistry` to the synchronous facade over it (`find`/`getAll`/
|
|
@@ -83,9 +84,7 @@ function formatExactModelSpec(model, thinkingLevel) {
|
|
|
83
84
|
const base = formatModelSpec(model, undefined);
|
|
84
85
|
return base === null ? null : `${base}:${thinkingLevel === undefined || thinkingLevel === '' ? 'off' : thinkingLevel}`;
|
|
85
86
|
}
|
|
86
|
-
// ---------------------------------------------------------------------------
|
|
87
87
|
// Tunables (backpressure / dialog anti-deadlock)
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
88
|
/** Broker-side default dialog timeout (anti-deadlock). When an extension
|
|
90
89
|
* dialog is forwarded to every writable client, the broker ALWAYS arms a timeout
|
|
91
90
|
* (this default, or a shorter per-dialog `opts.timeout` if the extension passed
|
|
@@ -93,14 +92,12 @@ function formatExactModelSpec(model, thinkingLevel) {
|
|
|
93
92
|
* — can never hang the agent turn forever. On fire it resolves to the SAFE default
|
|
94
93
|
* (deny for confirm; cancel/undefined for select/input/editor). */
|
|
95
94
|
const DEFAULT_DIALOG_TIMEOUT_MS = 120_000; // 120 s
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
95
|
// M3 (scout mq5thyli): the active engine session, exposed so broker-cli's FATAL
|
|
98
96
|
// handlers (uncaughtException / unhandledRejection / the runBroker reject) can
|
|
99
97
|
// dispose it before process.exit. The bash tool spawns children `detached` (own
|
|
100
98
|
// pgid); only session.dispose() (→ abortBash / agent.abort → killProcessTree)
|
|
101
99
|
// reaps them. The graceful exit path already disposes; without this the crash
|
|
102
100
|
// path would `process.exit(1)` and ORPHAN every in-flight bash subprocess.
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
101
|
let activeSession = null;
|
|
105
102
|
/** Dispose the live engine session if one exists (idempotent). Called by
|
|
106
103
|
* broker-cli's fatal handlers before exit so a crash never orphans detached
|
|
@@ -117,9 +114,7 @@ export function disposeActiveSession() {
|
|
|
117
114
|
}
|
|
118
115
|
}
|
|
119
116
|
}
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
117
|
// runBroker — the broker process body (plan T4 steps 1–6)
|
|
122
|
-
// ---------------------------------------------------------------------------
|
|
123
118
|
export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), startupTs = new Date().toISOString()) {
|
|
124
119
|
// A clean-abort certificate belongs only to the dead broker that wrote it.
|
|
125
120
|
// Fleet classification consumes it as proof that the dead broker had a turn
|
|
@@ -275,9 +270,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
275
270
|
// N3: the node's display name is re-applied by the rebind lane below so it
|
|
276
271
|
// survives a session replacement (new_session/switch_session/fork), not only at
|
|
277
272
|
// boot.
|
|
278
|
-
// -------------------------------------------------------------------------
|
|
279
273
|
// Socket fan-out, dialogs, and transcript tool groups
|
|
280
|
-
// -------------------------------------------------------------------------
|
|
281
274
|
const sockPath = viewSocketPath(nodeId);
|
|
282
275
|
const registry = new BrokerClientRegistry(nodeId);
|
|
283
276
|
const pendingDialogs = new Map();
|
|
@@ -445,10 +438,8 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
445
438
|
const reWelcomeAll = () => {
|
|
446
439
|
registry.forEachHelloed(sendWelcome);
|
|
447
440
|
};
|
|
448
|
-
// -------------------------------------------------------------------------
|
|
449
441
|
// The single exit helper: dispose the engine, close + unlink the
|
|
450
442
|
// socket, exit 0. Idempotent — every trigger converges here.
|
|
451
|
-
// -------------------------------------------------------------------------
|
|
452
443
|
const completeExit = (status) => {
|
|
453
444
|
if (disposed)
|
|
454
445
|
return;
|
|
@@ -509,7 +500,6 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
509
500
|
}
|
|
510
501
|
completeExit(status);
|
|
511
502
|
};
|
|
512
|
-
// -------------------------------------------------------------------------
|
|
513
503
|
// Extension-dialog routing (C2). makeBrokerUiContext owns the dialogPromise;
|
|
514
504
|
// here we just give it the three broker-side hooks it needs: the live set of
|
|
515
505
|
// writable clients (empty = nobody can answer → noOp fallback), how to forward a
|
|
@@ -517,7 +507,6 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
517
507
|
// first settles through. The zero-writable path NEVER hangs and NEVER waits on a
|
|
518
508
|
// per-dialog timeout (design §5.4's timeout premise is false — see
|
|
519
509
|
// makeBrokerUiContext).
|
|
520
|
-
// -------------------------------------------------------------------------
|
|
521
510
|
uiContext = makeBrokerUiContext({
|
|
522
511
|
writable: () => registry.writableClients(),
|
|
523
512
|
forward: (client, request) => registry.sendFrame(client, request),
|
|
@@ -542,9 +531,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
542
531
|
});
|
|
543
532
|
notifyTurnAccepted = frameDispatch.notifyTurnAccepted;
|
|
544
533
|
resetFrameState = frameDispatch.resetForSession;
|
|
545
|
-
// -------------------------------------------------------------------------
|
|
546
534
|
// 4 (plan): the socket listener — unlink any stale socket first.
|
|
547
|
-
// -------------------------------------------------------------------------
|
|
548
535
|
try {
|
|
549
536
|
if (existsSync(sockPath))
|
|
550
537
|
unlinkSync(sockPath);
|
|
@@ -671,7 +658,6 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
671
658
|
process.on('SIGTERM', () => { void abortAndExit('SIGTERM'); });
|
|
672
659
|
process.on('SIGINT', () => { void abortAndExit('SIGINT'); });
|
|
673
660
|
}
|
|
674
|
-
// ---------------------------------------------------------------------------
|
|
675
661
|
// snapshotMessages — the catch-up snapshot's ordered message history.
|
|
676
662
|
//
|
|
677
663
|
// The broker is the SOLE writer of the node's session `.jsonl`, so the session
|
|
@@ -722,7 +708,6 @@ export function snapshotMessages(session, boundaryReviewId) {
|
|
|
722
708
|
}
|
|
723
709
|
return messages === undefined ? undefined : visibleMessages(messages, { boundaryReviewId });
|
|
724
710
|
}
|
|
725
|
-
// ---------------------------------------------------------------------------
|
|
726
711
|
// resolveLaunchModel — the SOLE launch model-selection policy, module-scoped
|
|
727
712
|
// so both buildForManager (initial boot, inside buildBrokerSession below) and
|
|
728
713
|
// the reload_auth re-drive (inside runBroker, a DIFFERENT function scope) can
|
|
@@ -731,15 +716,14 @@ export function snapshotMessages(session, boundaryReviewId) {
|
|
|
731
716
|
// viable at all (tier 3) — this is NOT a fault; the caller decides what to do
|
|
732
717
|
// with it (construct a model-less session at boot, or leave a running session
|
|
733
718
|
// alone on a failed re-drive attempt).
|
|
734
|
-
// ---------------------------------------------------------------------------
|
|
735
719
|
async function resolveLaunchModel(services, cfg) {
|
|
736
|
-
const routeRequest = modelRequestFromConfig(cfg.model,
|
|
720
|
+
const routeRequest = modelRequestFromConfig(cfg.model, envModelIntent(), cfg.modelExact === true);
|
|
737
721
|
if (routeRequest === undefined)
|
|
738
722
|
return undefined;
|
|
739
723
|
const registry = registryOf(services);
|
|
740
724
|
const routeOptions = {
|
|
741
|
-
cwd:
|
|
742
|
-
profileId:
|
|
725
|
+
cwd: envNodeCwd() ?? cfg.cwd,
|
|
726
|
+
profileId: envProfileId(),
|
|
743
727
|
};
|
|
744
728
|
const unknown = unregisteredConcreteModel(routeRequest, registry, routeOptions);
|
|
745
729
|
if (unknown !== undefined) {
|
|
@@ -812,11 +796,9 @@ function modelLessRegistry(real) {
|
|
|
812
796
|
});
|
|
813
797
|
return { runtime, reveal: () => { masked = false; } };
|
|
814
798
|
}
|
|
815
|
-
// ---------------------------------------------------------------------------
|
|
816
799
|
// buildBrokerSession (plan T4 steps 2–4) — turn the launch recipe into a live
|
|
817
800
|
// engine session via the pi SDK SERVICES path. Exported so the C3/C4 real-SDK
|
|
818
801
|
// regression tests can drive the EXACT production wiring (not the mock).
|
|
819
|
-
// ---------------------------------------------------------------------------
|
|
820
802
|
export async function buildBrokerSession(engine, cfg) {
|
|
821
803
|
// 2. Build cwd-bound runtime services via the SERVICES path (C3) — NOT plain
|
|
822
804
|
// createAgentSession. createAgentSessionServices builds + reloads the
|