@north-light/crouter 0.3.232 → 0.3.234
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.js +16 -15
- package/dist/builtin-memory/internal/memory-loading.md +4 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +14 -17
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -11
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +8 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +10 -11
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/pi-shell-runner.ts +2 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +3 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +7 -7
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +8 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +6 -6
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/sysprompt-window.ts +3 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +1 -1
- package/dist/clients/attach/chrome/ticket-panel.js +15 -2
- package/dist/clients/attach/overlays/graph.js +0 -3
- package/dist/clients/attach/overlays/mcp.js +1 -2
- package/dist/clients/attach/render/chat-view.js +0 -12
- package/dist/clients/attach/render/crtr-output.js +0 -2
- package/dist/clients/attach/render/tool-calls.js +0 -6
- package/dist/clients/attach/slash/dispatch.js +8 -11
- package/dist/clients/attach/viewer.js +532 -534
- package/dist/clients/conversation/projection.js +0 -2
- package/dist/clients/inbox/controller.js +22 -3
- package/dist/clients/inbox/review/companion-pane.js +0 -4
- package/dist/clients/inbox/review-adapter.js +0 -7
- package/dist/clients/inbox/tui/slots.d.ts +3 -3
- package/dist/clients/inbox/tui/types.d.ts +2 -2
- package/dist/commands/api-client.js +0 -8
- package/dist/commands/attention.js +0 -8
- package/dist/commands/canvas-browse.js +2 -1
- package/dist/commands/canvas-config.js +0 -10
- package/dist/commands/canvas-history.js +2 -1
- package/dist/commands/cron.js +5 -18
- package/dist/commands/dashboard.js +0 -2
- package/dist/commands/human/feedback.js +2 -1
- package/dist/commands/human/prompts.js +2 -5
- package/dist/commands/human/queue.js +2 -7
- package/dist/commands/human/request.js +0 -14
- package/dist/commands/human/review.js +2 -1
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +4 -4
- package/dist/commands/memory/find.js +2 -2
- package/dist/commands/memory/lint.js +2 -1
- package/dist/commands/memory/read.d.ts +2 -7
- package/dist/commands/memory/read.js +5 -16
- package/dist/commands/memory/shared.d.ts +1 -1
- package/dist/commands/memory/shared.js +7 -10
- package/dist/commands/memory/write.js +4 -3
- package/dist/commands/node/bash.js +2 -1
- package/dist/commands/node/create.js +2 -1
- package/dist/commands/node/inspect.js +0 -6
- package/dist/commands/node/lifecycle.js +4 -13
- package/dist/commands/node/message.js +3 -2
- package/dist/commands/node/subscription.js +2 -1
- package/dist/commands/node/wait.js +4 -3
- package/dist/commands/node-context.js +2 -1
- package/dist/commands/node-lifecycle-revive.js +0 -2
- package/dist/commands/node-worktree.js +2 -1
- package/dist/commands/pkg/browse/actions.d.ts +1 -1
- package/dist/commands/pkg/browse/actions.js +4 -4
- package/dist/commands/pkg/browse/catalog.d.ts +1 -1
- package/dist/commands/pkg/browse/catalog.js +15 -15
- package/dist/commands/pkg/browse/command-view.d.ts +1 -1
- package/dist/commands/pkg/browse/command-view.js +3 -3
- package/dist/commands/pkg/browse/detail-panel.d.ts +1 -1
- package/dist/commands/pkg/browse/detail-panel.js +8 -8
- package/dist/commands/pkg/browse/doc-view.d.ts +1 -1
- package/dist/commands/pkg/browse/doc-view.js +3 -3
- package/dist/commands/pkg/browse/list-panel.d.ts +1 -1
- package/dist/commands/pkg/browse/list-panel.js +3 -3
- package/dist/commands/pkg/browse/model.d.ts +3 -3
- package/dist/commands/pkg/browse/shell.d.ts +2 -2
- package/dist/commands/pkg/browse/shell.js +13 -13
- package/dist/commands/pkg/plugin-manage.js +2 -1
- package/dist/commands/pkg/shared.js +0 -2
- package/dist/commands/profile/delete.js +2 -1
- package/dist/commands/push.js +2 -3
- package/dist/commands/revive.js +0 -2
- package/dist/commands/surface/node/placement.js +2 -1
- package/dist/commands/surface-edit.js +2 -1
- package/dist/commands/sys/config.js +0 -4
- package/dist/commands/sys/daemon.js +0 -10
- package/dist/commands/sys/doctor.js +3 -2
- package/dist/commands/sys/panels/broker-limits-panel.d.ts +2 -2
- package/dist/commands/sys/panels/broker-limits-panel.js +4 -4
- package/dist/commands/sys/panels/dependencies-panel.d.ts +2 -2
- package/dist/commands/sys/panels/dependencies-panel.js +2 -2
- package/dist/commands/sys/panels/keybindings-panel.d.ts +1 -1
- package/dist/commands/sys/panels/keybindings-panel.js +5 -5
- package/dist/commands/sys/panels/models-panel.d.ts +4 -4
- package/dist/commands/sys/panels/models-panel.js +11 -10
- package/dist/commands/sys/panels/plugins-panel.d.ts +2 -2
- package/dist/commands/sys/panels/plugins-panel.js +2 -2
- package/dist/commands/sys/panels/prefs-panel.d.ts +2 -2
- package/dist/commands/sys/panels/prefs-panel.js +4 -4
- package/dist/commands/sys/panels/profiles-panel.d.ts +1 -1
- package/dist/commands/sys/panels/profiles-panel.js +14 -13
- package/dist/commands/sys/panels/provider-panel.d.ts +3 -3
- package/dist/commands/sys/panels/provider-panel.js +8 -8
- package/dist/commands/sys/settings-shell.d.ts +3 -3
- package/dist/commands/sys/settings-shell.js +15 -15
- package/dist/commands/sys/setup-settings-runner.d.ts +3 -3
- package/dist/commands/sys/setup-settings-runner.js +5 -5
- package/dist/commands/sys/setup-wizard.d.ts +4 -4
- package/dist/commands/sys/setup-wizard.js +8 -8
- package/dist/commands/sys/support/prepare.js +4 -4
- package/dist/commands/sys/support/submit.js +2 -2
- package/dist/commands/sys/sync-deps.js +0 -2
- package/dist/commands/sys/sync-project-guidance.js +2 -1
- package/dist/commands/sys/sysprompt.js +2 -1
- package/dist/core/__tests__/boot.test.js +0 -8
- package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +23 -1
- package/dist/core/__tests__/canvas-db-boundary.test.d.ts +1 -0
- package/dist/core/__tests__/canvas-db-boundary.test.js +46 -0
- package/dist/core/__tests__/canvas.test.js +1 -1
- package/dist/core/__tests__/child-death-wake.test.js +0 -2
- package/dist/core/__tests__/cron-armed-context-failure.test.d.ts +1 -0
- package/dist/core/__tests__/cron-armed-context-failure.test.js +145 -0
- package/dist/core/__tests__/daemon-wedge.test.js +0 -4
- package/dist/core/__tests__/dead-node-policy-table.test.js +0 -4
- package/dist/core/__tests__/fixtures/fake-engine.js +4 -14
- package/dist/core/__tests__/fixtures/fake-pi-host.js +5 -5
- package/dist/core/__tests__/focuses.test.js +0 -6
- package/dist/core/__tests__/fs-utils.test.d.ts +1 -0
- package/dist/core/__tests__/fs-utils.test.js +50 -0
- package/dist/core/__tests__/helpers/harness.js +1 -1
- package/dist/core/__tests__/inline-memory-refs.test.js +0 -2
- package/dist/core/__tests__/integration/broker-fork-seam.test.js +0 -2
- package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +3 -16
- package/dist/core/__tests__/integration/command-plugins.test.js +0 -24
- package/dist/core/__tests__/integration/deferred-no-wake.test.js +0 -6
- package/dist/core/__tests__/integration/flagship-lifecycle.test.js +0 -26
- package/dist/core/__tests__/integration/live-mutation-verbs.test.js +0 -4
- package/dist/core/__tests__/integration/live-mutation.test.js +1 -5
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +1 -5
- package/dist/core/__tests__/integration/revive.test.js +0 -12
- package/dist/core/__tests__/integration/subscription-delivery.test.js +0 -6
- package/dist/core/__tests__/integration/tmux-surface.test.js +24 -8
- package/dist/core/__tests__/integration/worktree-reap.test.js +0 -6
- package/dist/core/__tests__/lifecycle.test.js +0 -18
- package/dist/core/__tests__/memory-resolver-precedence.test.js +1 -1
- package/dist/core/__tests__/migration.test.js +2 -2
- package/dist/core/__tests__/on-read-dedup-resume.test.js +3 -3
- package/dist/core/__tests__/pid-identity-match.test.js +1 -1
- package/dist/core/__tests__/plugin-kinds.test.js +1 -1
- package/dist/core/__tests__/plugin-page-components.test.js +1 -1
- package/dist/core/__tests__/preview-registry-sync.test.js +42 -1
- package/dist/core/__tests__/respawn-throttle.test.js +0 -4
- package/dist/core/__tests__/revive-all.test.js +5 -5
- package/dist/core/__tests__/revive-parked-fresh.test.js +62 -11
- package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +0 -8
- package/dist/core/__tests__/seam/broker-attach-stream.test.js +0 -8
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +0 -2
- package/dist/core/__tests__/tmux-driver-boundary.test.d.ts +1 -0
- package/dist/core/__tests__/tmux-driver-boundary.test.js +50 -0
- package/dist/core/activity-signal.d.ts +7 -0
- package/dist/core/activity-signal.js +38 -0
- package/dist/core/auto-update.js +2 -1
- package/dist/core/bootstrap.js +2 -1
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-transport.test.js +1 -1
- package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
- package/dist/core/canvas/browse/app.js +3 -2
- package/dist/core/canvas/browse/model.d.ts +1 -1
- package/dist/core/canvas/browse/model.js +0 -4
- package/dist/core/canvas/canvas.d.ts +22 -0
- package/dist/core/canvas/canvas.js +50 -18
- package/dist/core/canvas/crons.js +0 -6
- package/dist/core/canvas/db.d.ts +14 -20
- package/dist/core/canvas/db.js +30 -1332
- package/dist/core/canvas/focuses.js +0 -4
- package/dist/core/canvas/history.js +2 -1
- package/dist/core/canvas/migrations.d.ts +20 -0
- package/dist/core/canvas/migrations.js +1339 -0
- package/dist/core/canvas/nav-model.js +1 -11
- package/dist/core/canvas/nav-render.js +0 -8
- package/dist/core/canvas/remote-canvas-source.js +0 -2
- package/dist/core/canvas/render-source.js +31 -13
- package/dist/core/command-manifests/manifest.js +0 -10
- package/dist/core/command-manifests/schema.js +0 -4
- package/dist/core/command-plugins/transport/http-invoke.js +0 -14
- package/dist/core/command.js +0 -4
- package/dist/core/config.js +2 -1
- package/dist/core/feed/inbox.d.ts +10 -0
- package/dist/core/feed/inbox.js +18 -12
- package/dist/core/fs-utils.d.ts +9 -2
- package/dist/core/fs-utils.js +7 -15
- package/dist/core/help.js +0 -8
- package/dist/core/host-exports/export.d.ts +2 -2
- package/dist/core/host-exports/export.js +9 -12
- package/dist/core/human/answer.d.ts +2 -2
- package/dist/core/human/convention.d.ts +2 -5
- package/dist/core/human/convention.js +3 -13
- package/dist/core/human/page-schema.d.ts +2 -2
- package/dist/core/human/page-schema.js +2 -2
- package/dist/core/human/root.js +6 -23
- package/dist/core/human/scan.js +24 -7
- package/dist/core/human/tickets.d.ts +4 -1
- package/dist/core/human/tickets.js +20 -6
- package/dist/core/io.js +0 -12
- package/dist/core/memory/identity.js +0 -4
- package/dist/core/memory/inline-ref-inventory.js +3 -2
- package/dist/core/memory/lint.js +3 -6
- package/dist/core/memory-resolver.d.ts +5 -0
- package/dist/core/memory-resolver.js +14 -12
- package/dist/core/pagination.js +0 -2
- package/dist/core/preview-registry.d.ts +14 -0
- package/dist/core/preview-registry.js +2 -4
- package/dist/core/preview-result-path.js +2 -1
- package/dist/core/profiles/manifest.d.ts +37 -5
- package/dist/core/profiles/manifest.js +44 -24
- package/dist/core/profiles/select.js +0 -4
- package/dist/core/review/signal.js +4 -23
- package/dist/core/review/ticket-filter.d.ts +3 -3
- package/dist/core/review/ticket-filter.js +4 -9
- package/dist/core/runtime/__tests__/engine-route.test.js +1 -1
- package/dist/core/runtime/__tests__/spawn-env.test.js +0 -8
- package/dist/core/runtime/bearings-render.d.ts +56 -0
- package/dist/core/runtime/bearings-render.js +241 -0
- package/dist/core/runtime/bearings.d.ts +2 -17
- package/dist/core/runtime/bearings.js +12 -198
- package/dist/core/runtime/broker/auth-reload.d.ts +39 -0
- package/dist/core/runtime/broker/auth-reload.js +149 -0
- package/dist/core/runtime/broker/engine-drive.d.ts +26 -0
- package/dist/core/runtime/broker/engine-drive.js +257 -0
- package/dist/core/runtime/broker/engine-routing.d.ts +143 -0
- package/dist/core/runtime/broker/engine-routing.js +211 -0
- package/dist/core/runtime/broker/event-projection.js +3 -2
- package/dist/core/runtime/broker/fault-retry.js +5 -4
- package/dist/core/runtime/broker/frame-client.d.ts +38 -0
- package/dist/core/runtime/broker/frame-client.js +129 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +12 -189
- package/dist/core/runtime/broker/frame-dispatch.js +89 -1476
- package/dist/core/runtime/broker/frame-engine-control.d.ts +20 -0
- package/dist/core/runtime/broker/frame-engine-control.js +231 -0
- package/dist/core/runtime/broker/frame-memory-refs.d.ts +19 -0
- package/dist/core/runtime/broker/frame-memory-refs.js +41 -0
- package/dist/core/runtime/broker/frame-model-query.d.ts +13 -0
- package/dist/core/runtime/broker/frame-model-query.js +69 -0
- package/dist/core/runtime/broker/frame-read.d.ts +19 -0
- package/dist/core/runtime/broker/frame-read.js +116 -0
- package/dist/core/runtime/broker/frame-replies.d.ts +24 -0
- package/dist/core/runtime/broker/frame-replies.js +64 -0
- package/dist/core/runtime/broker/frame-session.d.ts +34 -0
- package/dist/core/runtime/broker/frame-session.js +203 -0
- package/dist/core/runtime/broker/turn-ignition.d.ts +42 -0
- package/dist/core/runtime/broker/turn-ignition.js +74 -0
- package/dist/core/runtime/broker-extension-render.js +9 -85
- package/dist/core/runtime/broker-protocol.js +0 -4
- package/dist/core/runtime/broker-sdk.js +2 -1
- package/dist/core/runtime/broker.d.ts +1 -1
- package/dist/core/runtime/broker.js +5 -23
- package/dist/core/runtime/fault.js +3 -2
- package/dist/core/runtime/headless-pi.js +3 -2
- package/dist/core/runtime/host.js +2 -3
- package/dist/core/runtime/kickoff.js +0 -4
- package/dist/core/runtime/launch-target.js +2 -1
- package/dist/core/runtime/launch.js +3 -6
- package/dist/core/runtime/lifecycle.js +17 -26
- package/dist/core/runtime/managed-provider-cooling.d.ts +1 -1
- package/dist/core/runtime/naming.js +0 -2
- package/dist/core/runtime/nodes.js +2 -1
- package/dist/core/runtime/persona.js +0 -6
- package/dist/core/runtime/placement-tmux.js +2 -5
- package/dist/core/runtime/placement.js +4 -11
- package/dist/core/runtime/recap.js +4 -3
- package/dist/core/runtime/reopen.js +4 -7
- package/dist/core/runtime/reset.js +0 -4
- package/dist/core/runtime/resume-root.js +0 -2
- package/dist/core/runtime/revive.js +17 -13
- package/dist/core/runtime/session-list-cache.js +2 -2
- package/dist/core/runtime/shell-expansion.js +0 -2
- package/dist/core/runtime/spawn.js +0 -2
- package/dist/core/runtime/stream-watchdog.d.ts +0 -1
- package/dist/core/runtime/stream-watchdog.js +13 -18
- package/dist/core/runtime/tmux-bindings.js +0 -2
- package/dist/core/runtime/tmux-driver.d.ts +12 -0
- package/dist/core/runtime/tmux-driver.js +18 -13
- package/dist/core/runtime/warm-pool.js +0 -4
- package/dist/core/scope.js +4 -3
- package/dist/core/spawn.js +2 -3
- package/dist/core/subscription-state.js +10 -17
- package/dist/core/substrate/__tests__/surface-match-pre-command.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-pre-command.test.js +92 -0
- package/dist/core/substrate/frontmatter-validation.js +1 -1
- package/dist/core/substrate/injected-store.d.ts +6 -0
- package/dist/core/substrate/injected-store.js +24 -0
- package/dist/core/substrate/on-read.d.ts +17 -1
- package/dist/core/substrate/on-read.js +36 -2
- package/dist/core/substrate/render.js +0 -12
- package/dist/core/substrate/schema.d.ts +3 -3
- package/dist/core/substrate/schema.js +4 -16
- package/dist/core/substrate/surface-match.d.ts +19 -0
- package/dist/core/substrate/surface-match.js +60 -2
- package/dist/core/termrender/display.js +2 -1
- package/dist/core/tui/page-host.d.ts +3 -3
- package/dist/core/tui/page-host.js +4 -4
- package/dist/core/tui/panel.js +3 -3
- package/dist/core/wake.js +0 -12
- package/dist/core/worktree.js +0 -2
- package/dist/daemon/__tests__/reconciler-signature.test.d.ts +1 -0
- package/dist/daemon/__tests__/reconciler-signature.test.js +36 -0
- package/dist/daemon/api/__tests__/seam/api-server.test.js +2 -16
- package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +3 -11
- package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +0 -6
- package/dist/daemon/api/handlers/attach.js +2 -1
- package/dist/daemon/api/handlers/broker-ops.js +3 -6
- package/dist/daemon/api/handlers/canvas.js +12 -18
- package/dist/daemon/api/handlers/feedback-comments.js +0 -8
- package/dist/daemon/api/handlers/health.js +3 -2
- package/dist/daemon/api/handlers/human-requests.js +0 -10
- package/dist/daemon/api/handlers/human.js +1 -5
- package/dist/daemon/api/handlers/inbox.js +0 -18
- package/dist/daemon/api/handlers/memory.js +1 -1
- package/dist/daemon/api/handlers/messages.js +4 -8
- package/dist/daemon/api/handlers/nodes.js +10 -21
- package/dist/daemon/api/handlers/reports.js +1 -1
- package/dist/daemon/api/handlers/reviews.js +1 -6
- package/dist/daemon/api/map.js +0 -6
- package/dist/daemon/api/router.js +0 -4
- package/dist/daemon/api/server.js +2 -5
- package/dist/daemon/boot-migrations.d.ts +14 -0
- package/dist/daemon/boot-migrations.js +31 -0
- package/dist/daemon/cron/armed-context.d.ts +21 -0
- package/dist/daemon/cron/armed-context.js +100 -0
- package/dist/daemon/cron/capture.d.ts +20 -0
- package/dist/daemon/cron/capture.js +45 -0
- package/dist/daemon/cron/disposition.d.ts +19 -0
- package/dist/daemon/cron/disposition.js +52 -0
- package/dist/daemon/cron/escalation.d.ts +12 -0
- package/dist/daemon/cron/escalation.js +124 -0
- package/dist/daemon/cron/held.d.ts +4 -0
- package/dist/daemon/cron/held.js +20 -0
- package/dist/daemon/cron/lease-recovery.d.ts +3 -0
- package/dist/daemon/cron/lease-recovery.js +119 -0
- package/dist/daemon/cron/live-runs.d.ts +11 -0
- package/dist/daemon/cron/live-runs.js +23 -0
- package/dist/daemon/cron/passes.d.ts +11 -0
- package/dist/daemon/cron/passes.js +78 -0
- package/dist/daemon/cron/settle.d.ts +28 -0
- package/dist/daemon/cron/settle.js +37 -0
- package/dist/daemon/cron/sinks.d.ts +2 -0
- package/dist/daemon/cron/sinks.js +100 -0
- package/dist/daemon/cron-run.d.ts +5 -41
- package/dist/daemon/cron-run.js +193 -882
- package/dist/daemon/crtrd-cli.js +2 -1
- package/dist/daemon/crtrd.js +22 -22
- package/dist/daemon/fleet.js +0 -4
- package/dist/daemon/human/deliver-action.js +2 -1
- package/dist/daemon/human/sweep.js +15 -4
- package/dist/daemon/manage.d.ts +2 -2
- package/dist/daemon/manage.js +4 -13
- package/dist/daemon/pidfile.js +2 -2
- package/dist/daemon/reconcilers/broker-supervision.js +26 -29
- package/dist/daemon/reconcilers/dormant-inbox.d.ts +1 -1
- package/dist/daemon/reconcilers/dormant-inbox.js +2 -1
- package/dist/daemon/reconcilers/live-obligation.js +2 -2
- package/dist/daemon/reconcilers/pending-review-submit.d.ts +1 -1
- package/dist/daemon/reconcilers/pending-review-submit.js +2 -1
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -1
- package/dist/daemon/reconcilers/storage-maintenance.js +2 -1
- package/dist/daemon/review/companion.js +2 -2
- package/dist/daemon/review/sweep.js +8 -3
- package/dist/migrations/001-surfaces-frontmatter.js +0 -6
- package/dist/migrations/corpus.js +2 -5
- package/dist/migrations/registry.js +15 -2
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +0 -4
- package/dist/pi-extensions/__tests__/pre-command-gate.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/pre-command-gate.test.js +220 -0
- package/dist/pi-extensions/broker-local.js +2 -1
- package/dist/pi-extensions/canvas-bash-valve.js +3 -17
- package/dist/pi-extensions/canvas-context-intro.js +2 -7
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +14 -0
- package/dist/pi-extensions/canvas-doc-substrate.js +77 -5
- package/dist/pi-extensions/canvas-goal-capture.js +2 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +5 -12
- package/dist/pi-extensions/canvas-passive-context.js +2 -5
- package/dist/pi-extensions/canvas-preview-result.js +2 -1
- package/dist/pi-extensions/canvas-prompt-scrub.js +2 -1
- package/dist/pi-extensions/canvas-recap.js +3 -16
- package/dist/pi-extensions/canvas-review-boundary.js +2 -5
- package/dist/pi-extensions/canvas-stophook.js +2 -26
- package/dist/pi-extensions/canvas-structured-output.js +2 -1
- package/dist/pi-extensions/canvas-tool-guide.js +2 -1
- package/dist/shared/__tests__/env-boundary.test.d.ts +1 -0
- package/dist/shared/__tests__/env-boundary.test.js +93 -0
- package/dist/shared/env.d.ts +68 -0
- package/dist/shared/env.js +171 -0
- package/package.json +3 -1
- package/runtime.lock.json +2 -2
- package/dist/core/canvas/render.d.ts +0 -47
- package/dist/core/canvas/render.js +0 -245
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentSessionServices, ModelRegistry } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import type { ClientToBroker } from '../broker-protocol.js';
|
|
3
|
+
import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
|
|
4
|
+
import type { FrameReplies } from './frame-replies.js';
|
|
5
|
+
import type { BrokerSession } from './read-ops.js';
|
|
6
|
+
/** The engine-command ops — every one of them a controller-gated mutation of
|
|
7
|
+
* the live engine's model, thinking level, queue, or session settings. */
|
|
8
|
+
export type EngineControlFrame = Extract<ClientToBroker, {
|
|
9
|
+
type: 'set_model' | 'deliver_custom_message' | 'cycle_model' | 'cycle_ladder' | 'cycle_thinking' | 'dequeue' | 'set_thinking_level' | 'set_auto_retry' | 'set_auto_compaction' | 'compact' | 'set_session_name';
|
|
10
|
+
}>;
|
|
11
|
+
export declare function createEngineControlHandler(deps: {
|
|
12
|
+
registry: BrokerClientRegistry;
|
|
13
|
+
replies: FrameReplies;
|
|
14
|
+
session: () => BrokerSession;
|
|
15
|
+
services: () => AgentSessionServices;
|
|
16
|
+
registryOf: (services: AgentSessionServices) => ModelRegistry;
|
|
17
|
+
persistModelChoice: (pinnedOverride?: boolean, userSelected?: boolean) => Promise<string | null>;
|
|
18
|
+
broadcastModelChanged: (persist?: boolean, pinnedOverride?: boolean, userSelected?: boolean) => Promise<void>;
|
|
19
|
+
formatModelSpec: (model: BrokerSession['model'], thinkingLevel?: string) => string | null;
|
|
20
|
+
}): (client: BrokerClient, frame: EngineControlFrame) => void;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { configuredLadderProviders, isProviderPinnedModelToken, nextLadderModel, normalizeModel, } from '../launch.js';
|
|
2
|
+
import { isManagedProvider, readSubscriptionPool, } from '../../subscription-state.js';
|
|
3
|
+
import { parseModelSpec } from '../model-selection.js';
|
|
4
|
+
import { resolveModelQuery } from './frame-model-query.js';
|
|
5
|
+
export function createEngineControlHandler(deps) {
|
|
6
|
+
const { registry, replies, session, services, registryOf, persistModelChoice, broadcastModelChanged, formatModelSpec, } = deps;
|
|
7
|
+
const { notWritable, ackTo, engineErrorTo } = replies;
|
|
8
|
+
const modelRegistry = () => registryOf(services());
|
|
9
|
+
const noSuchModel = (client, message) => registry.sendFrame(client, { type: 'error', code: 'engine_error', message });
|
|
10
|
+
const setModel = (client, frame) => {
|
|
11
|
+
if (notWritable(client, 'set the model'))
|
|
12
|
+
return;
|
|
13
|
+
const requested = parseModelSpec(frame.model);
|
|
14
|
+
let model;
|
|
15
|
+
let normalized;
|
|
16
|
+
try {
|
|
17
|
+
// Tier/alias specs (`opus`, `strong`, `anthropic/strong`, …) MUST resolve
|
|
18
|
+
// through the config ladder before touching the registry: the fuzzy
|
|
19
|
+
// registry search rank-ties `opus` between claude-3-opus-20240229 and
|
|
20
|
+
// claude-opus-4-8, and the deprecated 2024 model wins on catalog order.
|
|
21
|
+
// Concrete `provider/id` specs and genuine substrings (`fable`) pass
|
|
22
|
+
// through normalizeModel unchanged and take the exact/search path below.
|
|
23
|
+
normalized = parseModelSpec(normalizeModel(requested.modelSpec));
|
|
24
|
+
model = resolveModelQuery(modelRegistry(), normalized.modelSpec);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
// N2: registry.find should never throw on the real SDK, but a degenerate
|
|
28
|
+
// engine must still get a reply rather than a silently-dropped frame.
|
|
29
|
+
engineErrorTo(client)(err);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (model === undefined) {
|
|
33
|
+
noSuchModel(client, `no model matching '${frame.model}' in the registry`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
void session()
|
|
37
|
+
.setModel(model)
|
|
38
|
+
.then(async () => {
|
|
39
|
+
// The caller's explicit `:thinking` wins; else the ladder cell's.
|
|
40
|
+
const thinking = requested.thinkingLevel ?? normalized.thinkingLevel;
|
|
41
|
+
if (thinking !== undefined)
|
|
42
|
+
session().setThinkingLevel(thinking);
|
|
43
|
+
// frame.pinned, when the sender pre-normalized `frame.model` (e.g.
|
|
44
|
+
// setModelLive), carries the RAW-token pin decision explicitly.
|
|
45
|
+
// Otherwise frame.model itself IS the raw/explicit token (a
|
|
46
|
+
// slash-command arg, or a concrete picker pick) and derives it.
|
|
47
|
+
await broadcastModelChanged(true, frame.pinned !== undefined
|
|
48
|
+
? frame.pinned
|
|
49
|
+
: isProviderPinnedModelToken(frame.model), true);
|
|
50
|
+
ackTo(client, 'set_model');
|
|
51
|
+
})
|
|
52
|
+
.catch(engineErrorTo(client));
|
|
53
|
+
};
|
|
54
|
+
const deliverCustomMessage = (client, frame) => {
|
|
55
|
+
if (notWritable(client, 'deliver a custom message'))
|
|
56
|
+
return;
|
|
57
|
+
// Never triggers a turn either way. `nextTurn` folds the message into
|
|
58
|
+
// whatever turn comes next (situational context); omitting it pushes
|
|
59
|
+
// straight onto the message list so it lands FIRST in a still-empty
|
|
60
|
+
// session (a freshly-claimed spare's bearings). The inbox watcher's own
|
|
61
|
+
// situational send uses `followUp` + triggerTurn because there its job
|
|
62
|
+
// IS to wake the node.
|
|
63
|
+
void session()
|
|
64
|
+
.sendCustomMessage({
|
|
65
|
+
customType: frame.customType,
|
|
66
|
+
content: frame.content,
|
|
67
|
+
display: true,
|
|
68
|
+
...(frame.details !== undefined ? { details: frame.details } : {}),
|
|
69
|
+
}, ...(frame.deliverAs !== undefined ? [{ deliverAs: frame.deliverAs }] : []))
|
|
70
|
+
.then(() => ackTo(client, 'deliver_custom_message'))
|
|
71
|
+
.catch(engineErrorTo(client));
|
|
72
|
+
};
|
|
73
|
+
const cycleModel = (client, frame) => {
|
|
74
|
+
if (notWritable(client, 'cycle the model'))
|
|
75
|
+
return;
|
|
76
|
+
void session()
|
|
77
|
+
.cycleModel(frame.direction)
|
|
78
|
+
.then(async () => {
|
|
79
|
+
await broadcastModelChanged(true, undefined, true);
|
|
80
|
+
ackTo(client, 'cycle_model');
|
|
81
|
+
})
|
|
82
|
+
.catch(engineErrorTo(client));
|
|
83
|
+
};
|
|
84
|
+
const cycleLadder = (client, frame) => {
|
|
85
|
+
if (notWritable(client, 'cycle the model ladder'))
|
|
86
|
+
return;
|
|
87
|
+
// Resolve the next interleaved-ladder rung from the current model+thinking
|
|
88
|
+
// spec, then reuse the set_model path (registry resolve + thinking apply).
|
|
89
|
+
// The cycle only offers signed-in provider columns — same rule as the
|
|
90
|
+
// setup wizard's Models panel: pool occupancy for a managed provider
|
|
91
|
+
// (read live, so a login that landed mid-session counts immediately),
|
|
92
|
+
// else the registry's auth status. When nobody is signed in the helper
|
|
93
|
+
// returns every provider, so the cycle stays walkable pre-login.
|
|
94
|
+
const offeredProviders = (() => {
|
|
95
|
+
try {
|
|
96
|
+
const models = modelRegistry();
|
|
97
|
+
return configuredLadderProviders((providerId) => (isManagedProvider(providerId) &&
|
|
98
|
+
readSubscriptionPool(providerId).length > 0) ||
|
|
99
|
+
models.getProviderAuthStatus(providerId).configured);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return undefined; // auth state unreadable — cycle the full ladder
|
|
103
|
+
}
|
|
104
|
+
})();
|
|
105
|
+
const currentSpec = formatModelSpec(session().model, session().thinkingLevel) ?? '';
|
|
106
|
+
const nextSpec = parseModelSpec(nextLadderModel(currentSpec, frame.direction === 'backward' ? -1 : 1, offeredProviders));
|
|
107
|
+
let ladderModel;
|
|
108
|
+
try {
|
|
109
|
+
ladderModel = resolveModelQuery(modelRegistry(), nextSpec.modelSpec);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
engineErrorTo(client)(err);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (ladderModel === undefined) {
|
|
116
|
+
noSuchModel(client, `ladder model '${nextSpec.modelSpec}' not in the registry`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
void session()
|
|
120
|
+
.setModel(ladderModel)
|
|
121
|
+
.then(async () => {
|
|
122
|
+
if (nextSpec.thinkingLevel !== undefined)
|
|
123
|
+
session().setThinkingLevel(nextSpec.thinkingLevel);
|
|
124
|
+
await broadcastModelChanged(true, undefined, true);
|
|
125
|
+
ackTo(client, 'cycle_ladder');
|
|
126
|
+
})
|
|
127
|
+
.catch(engineErrorTo(client));
|
|
128
|
+
};
|
|
129
|
+
const cycleThinking = (client) => {
|
|
130
|
+
if (notWritable(client, 'cycle the thinking level'))
|
|
131
|
+
return;
|
|
132
|
+
void (async () => {
|
|
133
|
+
session().cycleThinkingLevel();
|
|
134
|
+
await persistModelChoice(undefined, true);
|
|
135
|
+
ackTo(client, 'cycle_thinking');
|
|
136
|
+
})().catch(engineErrorTo(client));
|
|
137
|
+
};
|
|
138
|
+
const dequeue = (client, frame) => {
|
|
139
|
+
if (notWritable(client, 'dequeue messages', frame.id))
|
|
140
|
+
return;
|
|
141
|
+
try {
|
|
142
|
+
const { steering, followUp } = session().clearQueue();
|
|
143
|
+
registry.sendFrame(client, {
|
|
144
|
+
type: 'data',
|
|
145
|
+
id: frame.id,
|
|
146
|
+
kind: 'dequeue',
|
|
147
|
+
steering,
|
|
148
|
+
followUp,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
engineErrorTo(client, frame.id)(err);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const setThinkingLevel = (client, frame) => {
|
|
156
|
+
if (notWritable(client, 'set the thinking level'))
|
|
157
|
+
return;
|
|
158
|
+
void (async () => {
|
|
159
|
+
session().setThinkingLevel(frame.level);
|
|
160
|
+
await persistModelChoice(undefined, true);
|
|
161
|
+
ackTo(client, 'set_thinking_level');
|
|
162
|
+
})().catch(engineErrorTo(client));
|
|
163
|
+
};
|
|
164
|
+
const setAutoRetry = (client, frame) => {
|
|
165
|
+
if (notWritable(client, 'set auto-retry'))
|
|
166
|
+
return;
|
|
167
|
+
try {
|
|
168
|
+
session().setAutoRetryEnabled(frame.enabled);
|
|
169
|
+
ackTo(client, 'set_auto_retry');
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
engineErrorTo(client)(err);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
const setAutoCompaction = (client, frame) => {
|
|
176
|
+
if (notWritable(client, 'set auto-compaction'))
|
|
177
|
+
return;
|
|
178
|
+
try {
|
|
179
|
+
session().setAutoCompactionEnabled(frame.enabled);
|
|
180
|
+
ackTo(client, 'set_auto_compaction');
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
engineErrorTo(client)(err);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const compact = (client, frame) => {
|
|
187
|
+
if (notWritable(client, 'compact the session'))
|
|
188
|
+
return;
|
|
189
|
+
void session()
|
|
190
|
+
.compact(frame.instructions)
|
|
191
|
+
.then(() => ackTo(client, 'compact'))
|
|
192
|
+
.catch(engineErrorTo(client));
|
|
193
|
+
};
|
|
194
|
+
const setSessionName = (client, frame) => {
|
|
195
|
+
if (notWritable(client, 'rename the session'))
|
|
196
|
+
return;
|
|
197
|
+
try {
|
|
198
|
+
session().setSessionName(frame.name);
|
|
199
|
+
ackTo(client, 'set_session_name');
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
engineErrorTo(client)(err);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
return (client, frame) => {
|
|
206
|
+
switch (frame.type) {
|
|
207
|
+
case 'set_model':
|
|
208
|
+
return setModel(client, frame);
|
|
209
|
+
case 'deliver_custom_message':
|
|
210
|
+
return deliverCustomMessage(client, frame);
|
|
211
|
+
case 'cycle_model':
|
|
212
|
+
return cycleModel(client, frame);
|
|
213
|
+
case 'cycle_ladder':
|
|
214
|
+
return cycleLadder(client, frame);
|
|
215
|
+
case 'cycle_thinking':
|
|
216
|
+
return cycleThinking(client);
|
|
217
|
+
case 'dequeue':
|
|
218
|
+
return dequeue(client, frame);
|
|
219
|
+
case 'set_thinking_level':
|
|
220
|
+
return setThinkingLevel(client, frame);
|
|
221
|
+
case 'set_auto_retry':
|
|
222
|
+
return setAutoRetry(client, frame);
|
|
223
|
+
case 'set_auto_compaction':
|
|
224
|
+
return setAutoCompaction(client, frame);
|
|
225
|
+
case 'compact':
|
|
226
|
+
return compact(client, frame);
|
|
227
|
+
case 'set_session_name':
|
|
228
|
+
return setSessionName(client, frame);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RefMeta } from '../broker-protocol.js';
|
|
2
|
+
import type { BrokerSession } from './read-ops.js';
|
|
3
|
+
/**
|
|
4
|
+
* Inline memory-reference inventory (design: inline memory references) — a
|
|
5
|
+
* single broker-level memo shared by BOTH the `list_memory_refs` read-op reply
|
|
6
|
+
* and per-submission guidance resolution, so a burst of reads + submissions
|
|
7
|
+
* between reloads never re-walks the corpus per call. Invalidated only when the
|
|
8
|
+
* corpus is KNOWN to have changed (a successful `session.reload()`); a failed
|
|
9
|
+
* reload leaves the memo intact.
|
|
10
|
+
*/
|
|
11
|
+
export type MemoryRefGuidance = {
|
|
12
|
+
/** The memoized inventory rows the `list_memory_refs` read-op replies with. */
|
|
13
|
+
refs(): RefMeta[];
|
|
14
|
+
/** The hidden guidance text for a human frame's verbatim text, or `null`. */
|
|
15
|
+
guidanceFor(text: string): string | null;
|
|
16
|
+
/** Drop the memo AND the shared substrate parse cache it reads through. */
|
|
17
|
+
invalidate(): void;
|
|
18
|
+
};
|
|
19
|
+
export declare function createMemoryRefGuidance(session: () => BrokerSession): MemoryRefGuidance;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { emitEvent } from '../../events/emit.js';
|
|
2
|
+
import { buildRefInventory } from '../../memory/inline-ref-inventory.js';
|
|
3
|
+
import { buildGuidance } from '../../memory/inline-ref-guidance.js';
|
|
4
|
+
import { clearSessionCache } from '../../substrate/session-cache.js';
|
|
5
|
+
import { isLeadingEngineCommand } from './engine-routing.js';
|
|
6
|
+
export function createMemoryRefGuidance(session) {
|
|
7
|
+
let memoized = null;
|
|
8
|
+
const inventory = () => {
|
|
9
|
+
if (memoized === null)
|
|
10
|
+
memoized = buildRefInventory();
|
|
11
|
+
return memoized;
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
refs: () => inventory().refs,
|
|
15
|
+
/** `null` when no guidance should be sent (a leading engine command/skill/
|
|
16
|
+
* template, no resolvable refs, or a degraded inventory/guidance build —
|
|
17
|
+
* work item 6: any failure here degrades to "no guidance", logged once as a
|
|
18
|
+
* structured warning, and NEVER blocks/alters the human delivery). */
|
|
19
|
+
guidanceFor(text) {
|
|
20
|
+
try {
|
|
21
|
+
if (isLeadingEngineCommand(text, session()))
|
|
22
|
+
return null;
|
|
23
|
+
return buildGuidance(text, inventory().names);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
emitEvent({
|
|
27
|
+
level: 'warn',
|
|
28
|
+
event: 'broker.memory_refs.guidance_resolve_failed',
|
|
29
|
+
error: err,
|
|
30
|
+
});
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
invalidate() {
|
|
35
|
+
memoized = null;
|
|
36
|
+
// Without this, a fresh buildRefInventory() would still read the stale
|
|
37
|
+
// cached docs out of the shared substrate parse cache.
|
|
38
|
+
clearSessionCache();
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ModelRegistry } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
/** A model resolved out of the registry, or undefined when nothing matches. */
|
|
3
|
+
export type ResolvedModel = ReturnType<ModelRegistry['find']>;
|
|
4
|
+
/** Resolve a `provider/id` model spec against the LIVE services registry (which
|
|
5
|
+
* carries any extension-registered providers). Pure: returns undefined on a
|
|
6
|
+
* malformed spec or an unknown model. */
|
|
7
|
+
export declare function findModelSpec(registry: ModelRegistry, spec: string): ResolvedModel;
|
|
8
|
+
/** Resolve a model from a free-text query — `/model opus`, `/model fable`. Tries
|
|
9
|
+
* the exact `provider/id` spec first; failing that, searches the registry the
|
|
10
|
+
* way the picker's search box does (case-insensitive substring on the model id
|
|
11
|
+
* and the `provider/id` key) and returns the BEST match, preferring authed/
|
|
12
|
+
* available models. Returns undefined only when nothing matches. */
|
|
13
|
+
export declare function resolveModelQuery(registry: ModelRegistry, query: string): ResolvedModel;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { parseModelSpec } from '../model-selection.js';
|
|
2
|
+
import { modelKey } from './read-ops.js';
|
|
3
|
+
/** Resolve a `provider/id` model spec against the LIVE services registry (which
|
|
4
|
+
* carries any extension-registered providers). Pure: returns undefined on a
|
|
5
|
+
* malformed spec or an unknown model. */
|
|
6
|
+
export function findModelSpec(registry, spec) {
|
|
7
|
+
const { modelSpec } = parseModelSpec(spec);
|
|
8
|
+
const slash = modelSpec.indexOf('/');
|
|
9
|
+
if (slash <= 0)
|
|
10
|
+
return undefined;
|
|
11
|
+
return registry.find(modelSpec.slice(0, slash), modelSpec.slice(slash + 1));
|
|
12
|
+
}
|
|
13
|
+
/** Resolve a model from a free-text query — `/model opus`, `/model fable`. Tries
|
|
14
|
+
* the exact `provider/id` spec first; failing that, searches the registry the
|
|
15
|
+
* way the picker's search box does (case-insensitive substring on the model id
|
|
16
|
+
* and the `provider/id` key) and returns the BEST match, preferring authed/
|
|
17
|
+
* available models. Returns undefined only when nothing matches. */
|
|
18
|
+
export function resolveModelQuery(registry, query) {
|
|
19
|
+
const exact = findModelSpec(registry, query);
|
|
20
|
+
if (exact)
|
|
21
|
+
return exact;
|
|
22
|
+
const q = query.trim().toLowerCase();
|
|
23
|
+
if (q === '')
|
|
24
|
+
return undefined;
|
|
25
|
+
let all;
|
|
26
|
+
try {
|
|
27
|
+
all = registry.getAll();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
let availableKeys;
|
|
33
|
+
try {
|
|
34
|
+
availableKeys = new Set(registry.getAvailable().map(modelKey));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
availableKeys = new Set();
|
|
38
|
+
}
|
|
39
|
+
// Rank: exact id (100) > exact provider/id (90) > id prefix (80) > id
|
|
40
|
+
// substring (60) > provider/id substring (40); +5 if the model is available.
|
|
41
|
+
const rank = (m) => {
|
|
42
|
+
const id = m.id.toLowerCase();
|
|
43
|
+
const key = modelKey(m).toLowerCase();
|
|
44
|
+
let base = -1;
|
|
45
|
+
if (id === q)
|
|
46
|
+
base = 100;
|
|
47
|
+
else if (key === q)
|
|
48
|
+
base = 90;
|
|
49
|
+
else if (id.startsWith(q))
|
|
50
|
+
base = 80;
|
|
51
|
+
else if (id.includes(q))
|
|
52
|
+
base = 60;
|
|
53
|
+
else if (key.includes(q))
|
|
54
|
+
base = 40;
|
|
55
|
+
else
|
|
56
|
+
return -1;
|
|
57
|
+
return base + (availableKeys.has(modelKey(m)) ? 5 : 0);
|
|
58
|
+
};
|
|
59
|
+
let best;
|
|
60
|
+
let bestRank = 0;
|
|
61
|
+
for (const m of all) {
|
|
62
|
+
const r = rank(m);
|
|
63
|
+
if (r > bestRank) {
|
|
64
|
+
bestRank = r;
|
|
65
|
+
best = m;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return best;
|
|
69
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ClientToBroker } from '../broker-protocol.js';
|
|
2
|
+
import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
|
|
3
|
+
import type { FrameReplies } from './frame-replies.js';
|
|
4
|
+
import type { MemoryRefGuidance } from './frame-memory-refs.js';
|
|
5
|
+
import { type BrokerSession } from './read-ops.js';
|
|
6
|
+
/** The read ops — none of them controller-gated: they are pure reads that
|
|
7
|
+
* drive nothing in the engine, so terminal observer connections populate their
|
|
8
|
+
* palettes and pickers through these too. */
|
|
9
|
+
export type ReadFrame = Extract<ClientToBroker, {
|
|
10
|
+
type: 'get_commands' | 'list_models' | 'list_scoped_models' | 'list_memory_refs' | 'get_tree' | 'get_settings' | 'list_sessions' | 'get_human_fork_coordinates';
|
|
11
|
+
}>;
|
|
12
|
+
export declare function createReadHandler(deps: {
|
|
13
|
+
registry: BrokerClientRegistry;
|
|
14
|
+
replies: FrameReplies;
|
|
15
|
+
session: () => BrokerSession;
|
|
16
|
+
boundaryReviewId: string | undefined;
|
|
17
|
+
memoryRefs: MemoryRefGuidance;
|
|
18
|
+
formatExactModelSpec: (model: BrokerSession['model'], thinkingLevel?: string) => string | null;
|
|
19
|
+
}): (client: BrokerClient, frame: ReadFrame) => void;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { SessionListCache } from '../session-list-cache.js';
|
|
2
|
+
import { sessionListCachePath } from '../../canvas/paths.js';
|
|
3
|
+
import { engineLeadingCommandTokens } from './engine-routing.js';
|
|
4
|
+
import { buildCommandList, buildGetTreeData, buildListMemoryRefsData, buildListModelsData, buildScopedModelsData, buildSessionsData, buildSettingsData, } from './read-ops.js';
|
|
5
|
+
export function createReadHandler(deps) {
|
|
6
|
+
const { registry, replies, session, boundaryReviewId, memoryRefs, formatExactModelSpec } = deps;
|
|
7
|
+
// Cache per-session wire info keyed by (path, mtime, size) so reopening the
|
|
8
|
+
// picker does not re-read the whole corpus (see session-list-cache.ts).
|
|
9
|
+
const sessionListCache = new SessionListCache(sessionListCachePath());
|
|
10
|
+
sessionListCache.prewarm(session().sessionManager.getSessionDir());
|
|
11
|
+
return (client, frame) => {
|
|
12
|
+
switch (frame.type) {
|
|
13
|
+
case 'get_commands': {
|
|
14
|
+
// OBSERVERS may call this: the merged command inventory is static
|
|
15
|
+
// (extensions + templates + skills + builtins) and drives nothing in the
|
|
16
|
+
// engine, so it is not a controller-gated op. Terminal observer
|
|
17
|
+
// connections use it to populate the command palette.
|
|
18
|
+
// The merged command list rides in ack.detail as JSON (the foundation's
|
|
19
|
+
// AckFrame.detail field) — the viewer JSON.parses it when for ===
|
|
20
|
+
// 'get_commands'. Keeps every command op a uniform ack reply.
|
|
21
|
+
try {
|
|
22
|
+
replies.ackTo(client, 'get_commands', true, JSON.stringify(buildCommandList(session())));
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
replies.engineErrorTo(client)(err);
|
|
26
|
+
}
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
// read-op picker data (§5 Unit A)
|
|
30
|
+
// NOT controller-gated: read-only, like get_commands. Terminal observer
|
|
31
|
+
// connections populate pickers through these too.
|
|
32
|
+
case 'list_models': {
|
|
33
|
+
try {
|
|
34
|
+
registry.sendFrame(client, buildListModelsData(frame.id, session()));
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
replies.engineErrorTo(client, frame.id)(err);
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case 'list_scoped_models': {
|
|
42
|
+
try {
|
|
43
|
+
registry.sendFrame(client, buildScopedModelsData(frame.id, session()));
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
replies.engineErrorTo(client, frame.id)(err);
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case 'list_memory_refs': {
|
|
51
|
+
try {
|
|
52
|
+
registry.sendFrame(client, buildListMemoryRefsData(frame.id, memoryRefs.refs(), engineLeadingCommandTokens(session())));
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
replies.engineErrorTo(client, frame.id)(err);
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case 'get_tree': {
|
|
60
|
+
try {
|
|
61
|
+
registry.sendFrame(client, buildGetTreeData(frame.id, session(), boundaryReviewId));
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
replies.engineErrorTo(client, frame.id)(err);
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case 'get_settings': {
|
|
69
|
+
try {
|
|
70
|
+
registry.sendFrame(client, buildSettingsData(frame.id, session()));
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
replies.engineErrorTo(client, frame.id)(err);
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case 'list_sessions': {
|
|
78
|
+
// Session listing reads the session dir off disk — async; reply when it
|
|
79
|
+
// resolves (or relay the error, correlated by id so the viewer doesn't hang).
|
|
80
|
+
void buildSessionsData(frame.id, frame.scope ?? 'cwd', session(), sessionListCache)
|
|
81
|
+
.then((d) => registry.sendFrame(client, d))
|
|
82
|
+
.catch(replies.engineErrorTo(client, frame.id));
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case 'get_human_fork_coordinates': {
|
|
86
|
+
// NOT controller-gated: this is a pure observer read. Branch creation is
|
|
87
|
+
// intentionally delegated to a detached SDK manager in the ask publisher;
|
|
88
|
+
// calling createBranchedSession here corrupts this live manager's identity.
|
|
89
|
+
try {
|
|
90
|
+
const sm = session().sessionManager;
|
|
91
|
+
const sourceFile = sm.getSessionFile();
|
|
92
|
+
const leafId = sm.getLeafId();
|
|
93
|
+
if (sourceFile === undefined ||
|
|
94
|
+
sourceFile === '' ||
|
|
95
|
+
leafId === null) {
|
|
96
|
+
replies.engineErrorTo(client, frame.id)(new Error('no persisted source session leaf'));
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
registry.sendFrame(client, {
|
|
100
|
+
type: 'data',
|
|
101
|
+
id: frame.id,
|
|
102
|
+
kind: 'human_fork_coordinates',
|
|
103
|
+
sourceFile,
|
|
104
|
+
sourceSessionId: sm.getSessionId(),
|
|
105
|
+
leafId,
|
|
106
|
+
modelSpec: formatExactModelSpec(session().model, session().thinkingLevel),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
replies.engineErrorTo(client, frame.id)(err);
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
|
|
2
|
+
import type { BrokerSession } from './read-ops.js';
|
|
3
|
+
export declare const REVIEW_BOUNDARY_NAVIGATION_CODE = "review_boundary_navigation";
|
|
4
|
+
export declare class ReviewBoundaryNavigationError extends Error {
|
|
5
|
+
readonly targetId: string;
|
|
6
|
+
readonly code = "review_boundary_navigation";
|
|
7
|
+
constructor(targetId: string);
|
|
8
|
+
}
|
|
9
|
+
/** The reply vocabulary every frame handler answers a client with, bound once
|
|
10
|
+
* to the client registry that owns the sockets. */
|
|
11
|
+
export type FrameReplies = {
|
|
12
|
+
/** Reject a read-only client for a mutating op. The decision reads this
|
|
13
|
+
* client's OWN fixed role — there is no shared writer slot, so any number of
|
|
14
|
+
* writable clients pass concurrently. Returns true when rejected (the caller
|
|
15
|
+
* should `break`). */
|
|
16
|
+
notWritable(client: BrokerClient, what: string, id?: string): boolean;
|
|
17
|
+
ackTo(client: BrokerClient, op: string, ok?: boolean, detail?: string, id?: string): void;
|
|
18
|
+
engineErrorTo(client: BrokerClient, id?: string): (err: unknown) => void;
|
|
19
|
+
reviewBoundaryRefusalTo(client: BrokerClient, targetId: string): void;
|
|
20
|
+
};
|
|
21
|
+
export declare function createFrameReplies(registry: BrokerClientRegistry): FrameReplies;
|
|
22
|
+
/** True iff `entryId` sits BEFORE this review companion's visible transcript
|
|
23
|
+
* boundary — the navigation an ordinary node has no boundary for. */
|
|
24
|
+
export declare function isPreBoundaryReviewEntry(session: BrokerSession, boundaryReviewId: string | undefined, entryId: string): boolean;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { isReviewBoundary } from '../session-visibility.js';
|
|
2
|
+
export const REVIEW_BOUNDARY_NAVIGATION_CODE = 'review_boundary_navigation';
|
|
3
|
+
export class ReviewBoundaryNavigationError extends Error {
|
|
4
|
+
targetId;
|
|
5
|
+
code = REVIEW_BOUNDARY_NAVIGATION_CODE;
|
|
6
|
+
constructor(targetId) {
|
|
7
|
+
super(`review companion cannot navigate before the visible transcript boundary (target: ${targetId})`);
|
|
8
|
+
this.targetId = targetId;
|
|
9
|
+
this.name = 'ReviewBoundaryNavigationError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function createFrameReplies(registry) {
|
|
13
|
+
return {
|
|
14
|
+
notWritable(client, what, id) {
|
|
15
|
+
if (client.role === 'controller')
|
|
16
|
+
return false;
|
|
17
|
+
registry.sendFrame(client, {
|
|
18
|
+
type: 'error',
|
|
19
|
+
code: 'read_only',
|
|
20
|
+
message: `a read-only client may not ${what}`,
|
|
21
|
+
// M1: echo a correlated request's id so its pending-by-id promise rejects
|
|
22
|
+
// rather than hanging (e.g. a read-only `dequeue`).
|
|
23
|
+
...(id !== undefined ? { id } : {}),
|
|
24
|
+
});
|
|
25
|
+
return true;
|
|
26
|
+
},
|
|
27
|
+
ackTo(client, op, ok = true, detail, id) {
|
|
28
|
+
registry.sendFrame(client, {
|
|
29
|
+
type: 'ack',
|
|
30
|
+
for: op,
|
|
31
|
+
ok,
|
|
32
|
+
...(detail !== undefined ? { detail } : {}),
|
|
33
|
+
...(id !== undefined ? { id } : {}),
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
engineErrorTo(client, id) {
|
|
37
|
+
return (err) => registry.sendFrame(client, {
|
|
38
|
+
type: 'error',
|
|
39
|
+
code: 'engine_error',
|
|
40
|
+
message: String(err),
|
|
41
|
+
// M1: a correlated read-op / dequeue failure echoes its id so the viewer's
|
|
42
|
+
// pending-by-id promise rejects instead of hanging; uncorrelated callers
|
|
43
|
+
// pass no id (unchanged behavior).
|
|
44
|
+
...(id !== undefined ? { id } : {}),
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
reviewBoundaryRefusalTo(client, targetId) {
|
|
48
|
+
const refusal = new ReviewBoundaryNavigationError(targetId);
|
|
49
|
+
registry.sendFrame(client, {
|
|
50
|
+
type: 'error',
|
|
51
|
+
code: refusal.code,
|
|
52
|
+
message: refusal.message,
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** True iff `entryId` sits BEFORE this review companion's visible transcript
|
|
58
|
+
* boundary — the navigation an ordinary node has no boundary for. */
|
|
59
|
+
export function isPreBoundaryReviewEntry(session, boundaryReviewId, entryId) {
|
|
60
|
+
return (boundaryReviewId !== undefined &&
|
|
61
|
+
!session.sessionManager
|
|
62
|
+
.getBranch(entryId)
|
|
63
|
+
.some((entry) => isReviewBoundary(entry, boundaryReviewId)));
|
|
64
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ClientToBroker } from '../broker-protocol.js';
|
|
2
|
+
import type { BrokerClient, BrokerClientRegistry } from './client-registry.js';
|
|
3
|
+
import type { FrameReplies } from './frame-replies.js';
|
|
4
|
+
import type { MemoryRefGuidance } from './frame-memory-refs.js';
|
|
5
|
+
import type { RebindDriver } from './rebind.js';
|
|
6
|
+
import type { BrokerSession } from './read-ops.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 declare function isHiddenJsonlBranchEntry(entry: unknown): boolean;
|
|
15
|
+
/** Rewrite an already-exported JSONL file in place, dropping every hidden
|
|
16
|
+
* (`display:false`) branch entry while re-chaining `parentId` so the
|
|
17
|
+
* remaining entries stay a single valid linear branch — applying the general
|
|
18
|
+
* display:false boundary to `session.exportToJsonl`'s own output, which
|
|
19
|
+
* serializes every entry verbatim regardless of display. The header line
|
|
20
|
+
* (line 1) is copied through untouched. */
|
|
21
|
+
export declare function stripHiddenEntriesFromJsonlExport(filePath: string): void;
|
|
22
|
+
/** The ops that navigate, replace, or publish this node's session. */
|
|
23
|
+
export type SessionFrame = Extract<ClientToBroker, {
|
|
24
|
+
type: 'navigate_tree' | 'reload' | 'export' | 'new_session' | 'switch_session' | 'fork' | 'clone' | 'share';
|
|
25
|
+
}>;
|
|
26
|
+
export declare function createSessionHandler(deps: {
|
|
27
|
+
registry: BrokerClientRegistry;
|
|
28
|
+
replies: FrameReplies;
|
|
29
|
+
rebind: RebindDriver;
|
|
30
|
+
session: () => BrokerSession;
|
|
31
|
+
boundaryReviewId: string | undefined;
|
|
32
|
+
memoryRefs: MemoryRefGuidance;
|
|
33
|
+
reWelcomeAll: () => void;
|
|
34
|
+
}): (client: BrokerClient, frame: SessionFrame) => void;
|