@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
|
@@ -72,9 +72,7 @@ function countLines(path) {
|
|
|
72
72
|
return 0;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
75
|
// A1 — /healthz ok + socket mode 0600
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
76
|
test('A1: /healthz is ok and the unix socket is bound mode 0600', async () => {
|
|
79
77
|
const health = await client.healthz();
|
|
80
78
|
assert.equal(health.ok, true, 'GET /healthz → { ok: true }');
|
|
@@ -86,10 +84,8 @@ test('attention counts reads only the requested node slice', async () => {
|
|
|
86
84
|
const result = await client.attentionCounts([root, 'unknown-node']);
|
|
87
85
|
assert.deepEqual(result.counts, { [root]: 0, 'unknown-node': 0 });
|
|
88
86
|
});
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
87
|
// A2 — POST /nodes → live node (row + broker + recorded pid), GET shows active,
|
|
91
88
|
// view.sock exists
|
|
92
|
-
// ---------------------------------------------------------------------------
|
|
93
89
|
test('A2: POST /nodes boots a live broker; GET /nodes/{id} shows active + live pid + view.sock', async () => {
|
|
94
90
|
const root = h.spawnRoot('a2 root'); // in-process resident, pi_pid = harness pid (alive)
|
|
95
91
|
const detail = await client.createNode({ kind: 'developer', parent: root, prompt: 'a2 worker' });
|
|
@@ -103,31 +99,27 @@ test('A2: POST /nodes boots a live broker; GET /nodes/{id} shows active + live p
|
|
|
103
99
|
assert.ok(meta?.pi_pid != null && isPidAlive(meta.pi_pid), 'row records a live broker pid');
|
|
104
100
|
assert.ok(existsSync(h.brokerSock(id)), 'view.sock exists for the live broker');
|
|
105
101
|
});
|
|
106
|
-
// ---------------------------------------------------------------------------
|
|
107
102
|
// A3 — POST .../messages appends inbox.jsonl; POST .../reports {tier:'final'}
|
|
108
103
|
// writes the report, notifies subscribers, transitions the row
|
|
109
|
-
// ---------------------------------------------------------------------------
|
|
110
104
|
test('A3: messages append inbox.jsonl; final report writes + notifies subscriber + transitions row', async () => {
|
|
111
105
|
const A = h.spawnRoot('a3 mgr');
|
|
112
106
|
const B = (await client.createNode({ kind: 'developer', parent: A, prompt: 'a3 worker' })).node_id;
|
|
113
107
|
await h.awaitBoot(B);
|
|
114
|
-
//
|
|
108
|
+
// messages append inbox.jsonl (raw line count is cursor-independent)
|
|
115
109
|
const inboxFile = inboxPath(B);
|
|
116
110
|
const before = countLines(inboxFile);
|
|
117
111
|
const msg = await client.sendMessage(B, { body: 'hello inbox' });
|
|
118
112
|
assert.equal(msg.delivered, true, 'message reports delivered');
|
|
119
113
|
await h.waitFor(() => countLines(inboxFile) > before, { label: 'inbox.jsonl appended', timeoutMs: 10_000 });
|
|
120
|
-
//
|
|
114
|
+
// final report: file written + subscriber notified + row transitioned
|
|
121
115
|
const res = await client.pushReport(B, { tier: 'final', body: 'a3 done result' });
|
|
122
116
|
assert.ok(existsSync(res.report_path), `report file written at ${res.report_path}`);
|
|
123
117
|
assert.ok(res.notified.includes(A), `final push notifies the spawn-subscribed manager A (notified=${JSON.stringify(res.notified)})`);
|
|
124
118
|
assert.deepEqual(res.transitioned, { from: 'active', to: 'done' }, 'row transitioned active→done');
|
|
125
119
|
assert.equal(h.node(B)?.status, 'done', 'row status is done after final');
|
|
126
120
|
});
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
121
|
// A4 — WS attach reconstructs the same settled broker snapshot as a direct
|
|
129
122
|
// ViewSocketClient; ?revive=0 on a dormant node → 409 without launching a broker
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
123
|
test('A4a: WS attach welcome.snapshot is byte-identical to a direct ViewSocketClient snapshot', async () => {
|
|
132
124
|
const root = h.spawnRoot('a4 root');
|
|
133
125
|
const id = (await client.createNode({ kind: 'developer', parent: root, prompt: 'a4 worker' })).node_id;
|
|
@@ -229,9 +221,7 @@ test('A4b: ?revive=0 on a dormant node refuses 409 without launching a broker',
|
|
|
229
221
|
assert.equal(h.bootCount(dorm), bootsBefore, 'no broker was launched (boot count unchanged)');
|
|
230
222
|
assert.equal(h.node(dorm)?.pi_pid ?? null, null, 'no pid recorded — the node stayed dormant');
|
|
231
223
|
});
|
|
232
|
-
// ---------------------------------------------------------------------------
|
|
233
224
|
// A5 — POST .../close tears down the broker + cascades per edges
|
|
234
|
-
// ---------------------------------------------------------------------------
|
|
235
225
|
test('A5: close tears down the target broker and cascades its subtree, leaving the ancestor untouched', async () => {
|
|
236
226
|
const A = h.spawnRoot('a5 root');
|
|
237
227
|
const B = (await client.createNode({ kind: 'developer', parent: A, prompt: 'a5 mid' })).node_id;
|
|
@@ -259,12 +249,10 @@ test('A5: close tears down the target broker and cascades its subtree, leaving t
|
|
|
259
249
|
assert.equal(h.node(C)?.status, 'canceled', 'cascaded child C is canceled');
|
|
260
250
|
assert.equal(h.node(A)?.status, 'active', 'ancestor A is untouched (still active)');
|
|
261
251
|
});
|
|
262
|
-
// ---------------------------------------------------------------------------
|
|
263
252
|
// A6 — PUT /profiles/{name} idempotent; PUT /model-auth/{provider} writes
|
|
264
253
|
// auth.json (sandboxed PI_CODING_AGENT_DIR).
|
|
265
254
|
// HONEST PROXY: reload fan is asserted as the file write only (the mtime
|
|
266
255
|
// watcher lives in runDaemon, not createApiServer).
|
|
267
|
-
// ---------------------------------------------------------------------------
|
|
268
256
|
test('A6a: PUT /profiles/{name} is idempotent (twice → same profile id)', async () => {
|
|
269
257
|
// Profiles write under `join(homedir(), '.crouter', 'profiles')` — homedir()
|
|
270
258
|
// honors HOME, which the harness only overrides for SUBPROCESS children. For
|
|
@@ -312,11 +300,9 @@ test('A6b: PUT /model-auth/{provider} writes auth.json (reload fan asserted as f
|
|
|
312
300
|
rmSync(piDir, { recursive: true, force: true });
|
|
313
301
|
}
|
|
314
302
|
});
|
|
315
|
-
// ---------------------------------------------------------------------------
|
|
316
303
|
// /v1/files/peek — host file read (capped → `truncated`), plus the
|
|
317
304
|
// usage/relative/404 error mapping. Covers the handler's non-trivial
|
|
318
305
|
// branching (usage / notFound / directory / truncation boundary).
|
|
319
|
-
// ---------------------------------------------------------------------------
|
|
320
306
|
const PEEK_CAP = 512 * 1024; // MUST mirror PEEK_MAX_BYTES in handlers/files.ts
|
|
321
307
|
test('files/peek: 200 returns full content, untruncated, for a small file', async () => {
|
|
322
308
|
const dir = mkdtempSync(join(tmpdir(), 'crtr-peek-'));
|
|
@@ -60,10 +60,8 @@ after(async () => {
|
|
|
60
60
|
await server?.close();
|
|
61
61
|
await h?.dispose();
|
|
62
62
|
});
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
63
|
// B2 (attach hybrid) — ensureAttach revives a DORMANT node server-side; the
|
|
65
64
|
// direct view.sock snapshot streams; DETACH LEAVES THE BROKER RUNNING.
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
65
|
test('B2-attach: ensureAttach revives a dormant node, snapshot streams, detach leaves the broker running', async () => {
|
|
68
66
|
const root = h.spawnRoot('b10 attach root'); // in-process resident (alive pid)
|
|
69
67
|
const id = (await client.createNode({ kind: 'developer', parent: root, prompt: 'b10 attach worker' })).node_id;
|
|
@@ -87,7 +85,7 @@ test('B2-attach: ensureAttach revives a dormant node, snapshot streams, detach l
|
|
|
87
85
|
// reviveNode's double-launch guard reads the fleet entry, so attaching in the
|
|
88
86
|
// window between the kill and the child-exit delivery is correctly a no-op.
|
|
89
87
|
await h.awaitFleetExit(id);
|
|
90
|
-
//
|
|
88
|
+
// ensureAttach (the exact call viewer.ts makes) revives it SERVER-SIDE
|
|
91
89
|
const res = await client.ensureAttach(id, { resume: true, revive: true });
|
|
92
90
|
assert.equal(res.node_id, id, 'ensureAttach echoes the node id');
|
|
93
91
|
assert.equal(res.revived, true, 'server launched a fresh broker (revived=true) for the dormant node');
|
|
@@ -101,7 +99,7 @@ test('B2-attach: ensureAttach revives a dormant node, snapshot streams, detach l
|
|
|
101
99
|
// hop viewer.ts keeps local). ---
|
|
102
100
|
const attached = await kit.attach(id, 'observer', 'b10-attach-observer');
|
|
103
101
|
assert.ok(attached.welcome.snapshot != null, 'welcome frame carries a reconstructed snapshot (snapshot streams)');
|
|
104
|
-
//
|
|
102
|
+
// DETACH LEAVES THE BROKER RUNNING (reap-on-detach removed this batch)
|
|
105
103
|
attached.close();
|
|
106
104
|
// Give any (now-absent) reap path a beat to have fired, then assert the broker
|
|
107
105
|
// is unmistakably still alive.
|
|
@@ -111,11 +109,9 @@ test('B2-attach: ensureAttach revives a dormant node, snapshot streams, detach l
|
|
|
111
109
|
assert.ok(isPidAlive(afterDetachPid), 'detach left the broker RUNNING (no reap-on-detach)');
|
|
112
110
|
assert.notEqual(h.node(id)?.status, 'done', 'detach did not finalize the node');
|
|
113
111
|
});
|
|
114
|
-
// ---------------------------------------------------------------------------
|
|
115
112
|
// B2 (model-auth) — installCredential writes auth.json (content asserted), the
|
|
116
113
|
// reload FAN acks against a live broker, and the API path is byte-identical to
|
|
117
114
|
// the old direct-write path.
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
115
|
test('B2-modelauth: installCredential writes auth.json and the reload fan acks a live broker', async () => {
|
|
120
116
|
// Sandbox pi's credential store so the write lands in a throwaway dir, never the
|
|
121
117
|
// developer's real ~/.pi/agent/auth.json. authPath() honors PI_CODING_AGENT_DIR.
|
|
@@ -128,7 +124,7 @@ test('B2-modelauth: installCredential writes auth.json and the reload fan acks a
|
|
|
128
124
|
const root = h.spawnRoot('b10 modelauth root');
|
|
129
125
|
liveId = (await client.createNode({ kind: 'developer', parent: root, prompt: 'b10 fan target' })).node_id;
|
|
130
126
|
await h.awaitBoot(liveId);
|
|
131
|
-
//
|
|
127
|
+
// installCredential (the exact call setup.ts makes) writes auth.json
|
|
132
128
|
const res = await client.installCredential('anthropic', { kind: 'api_key', api_key: 'sk-b10-test-key' });
|
|
133
129
|
assert.equal(res.installed, true, 'credential reports installed');
|
|
134
130
|
assert.equal(res.managed, false, 'api_key install is not a managed-pool account');
|
|
@@ -150,14 +146,12 @@ test('B2-modelauth: installCredential writes auth.json and the reload fan acks a
|
|
|
150
146
|
rmSync(apiDir, { recursive: true, force: true });
|
|
151
147
|
}
|
|
152
148
|
});
|
|
153
|
-
// ---------------------------------------------------------------------------
|
|
154
149
|
// B2 (model-auth, oauth) — non-managed OAuth install must PRESERVE
|
|
155
150
|
// provider-specific extra credential fields (e.g. github-copilot's
|
|
156
151
|
// `enterpriseUrl`, load-bearing for enterprise routing/refresh). The client
|
|
157
152
|
// forwards non-standard keys through `extra` and the handler re-spreads them.
|
|
158
153
|
// Regression guard for review M1 (a dropped enterpriseUrl breaks
|
|
159
154
|
// GitHub-Enterprise Copilot).
|
|
160
|
-
// ---------------------------------------------------------------------------
|
|
161
155
|
test('B2-modelauth-oauth: installCredential(oauth) preserves extra fields (enterpriseUrl)', async () => {
|
|
162
156
|
const savedPiDir = process.env['PI_CODING_AGENT_DIR'];
|
|
163
157
|
const apiDir = mkdtempSync(join(tmpdir(), 'crtr-b10-oauth-api-'));
|
|
@@ -192,10 +186,8 @@ test('B2-modelauth-oauth: installCredential(oauth) preserves extra fields (enter
|
|
|
192
186
|
rmSync(apiDir, { recursive: true, force: true });
|
|
193
187
|
}
|
|
194
188
|
});
|
|
195
|
-
// ---------------------------------------------------------------------------
|
|
196
189
|
// Credential truth/removal — GET exposes only connection state and DELETE
|
|
197
190
|
// clears exactly its provider's durable store.
|
|
198
|
-
// ---------------------------------------------------------------------------
|
|
199
191
|
test('B2-modelauth-status: list is sanitized and delete clears only the selected provider', async () => {
|
|
200
192
|
const savedPiDir = process.env['PI_CODING_AGENT_DIR'];
|
|
201
193
|
const apiDir = mkdtempSync(join(tmpdir(), 'crtr-b10-status-'));
|
|
@@ -127,12 +127,10 @@ after(async () => {
|
|
|
127
127
|
else
|
|
128
128
|
process.env['CRTR_NODE_ID'] = savedNodeId;
|
|
129
129
|
});
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
130
|
// push final — the report write, the subscriber fan, and the lifecycle
|
|
132
131
|
// transition all happen server-side, driven through the real push.ts leaf. A
|
|
133
132
|
// parent spawns a child (so the parent subscribes to the child); the child
|
|
134
133
|
// pushes.
|
|
135
|
-
// ---------------------------------------------------------------------------
|
|
136
134
|
test('`crtr push final` from a child writes a report, notifies the subscribed parent, AND transitions the child row active→done', async () => {
|
|
137
135
|
const parent = h.spawnRoot('leaf-api-parity final parent');
|
|
138
136
|
const child = (await client.createNode({ kind: 'developer', parent, prompt: 'final worker' })).node_id;
|
|
@@ -153,11 +151,9 @@ test('`crtr push final` from a child writes a report, notifies the subscribed pa
|
|
|
153
151
|
// — the re-homed guard only engages for a managed-worktree owner.
|
|
154
152
|
assert.equal('worktree_auto_dropped' in out, false, 'a normal node final is unaffected by the worktree guard');
|
|
155
153
|
});
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
154
|
// Re-homed worktree guard/auto-drop (ambiguity-2 follow-up): the pre-B-5 CLI
|
|
158
155
|
// `push final` worktree behavior now lives SERVER-side in handlers/reports.ts,
|
|
159
156
|
// exercised end-to-end through the real push.ts leaf.
|
|
160
|
-
// ---------------------------------------------------------------------------
|
|
161
157
|
test('re-homed guard: `crtr push final` on a node with a managed worktree holding REAL unlanded commits blocks with the parity open_managed_worktree InputError (round-tripped from the server), and does NOT finalize', async () => {
|
|
162
158
|
const repo = initRepo();
|
|
163
159
|
try {
|
|
@@ -255,13 +251,11 @@ test('`crtr push final` keeps an absent managed worktree open when its branch is
|
|
|
255
251
|
rmSync(repo, { recursive: true, force: true });
|
|
256
252
|
}
|
|
257
253
|
});
|
|
258
|
-
// ---------------------------------------------------------------------------
|
|
259
254
|
// `node worktree close` error parity — the SPECIFIC WorktreeError code + its
|
|
260
255
|
// `next` remediation round-trip verbatim through the server → ApiError → the
|
|
261
256
|
// bespoke CLI mapper in node-worktree.ts, instead of collapsing to a generic
|
|
262
257
|
// status bucket. That mapper exists for exactly this contract, so this is the
|
|
263
258
|
// load-bearing regression to lock.
|
|
264
|
-
// ---------------------------------------------------------------------------
|
|
265
259
|
test('`crtr node worktree close` on a node with NO managed worktree round-trips the specific `no_managed_worktree` code + its exact next-hint (not a generic status bucket)', async () => {
|
|
266
260
|
const id = 'wt-close-none';
|
|
267
261
|
createNode({ node_id: id, name: id, created: new Date().toISOString(), cwd: process.cwd(), kind: 'developer', mode: 'base', lifecycle: 'terminal', status: 'active' });
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
// is bridge.ts's (A-8) — NOT routed here.
|
|
6
6
|
//
|
|
7
7
|
// `{id}` is the target node being attached to.
|
|
8
|
-
import { getNode
|
|
8
|
+
import { getNode } from '../../../core/canvas/canvas.js';
|
|
9
|
+
import { reviveNode } from '../../../core/runtime/revive.js';
|
|
9
10
|
import { viewSocketPath } from '../../../core/canvas/paths.js';
|
|
10
11
|
import { waitForBrokerViewSocket } from '../../../core/runtime/placement-tmux.js';
|
|
11
12
|
import { notFound, usage } from '../../../core/errors.js';
|
|
@@ -6,7 +6,7 @@ import { editorLabel } from '../../../core/canvas/labels.js';
|
|
|
6
6
|
import { assembleNodeSubject } from '../../../core/substrate/subject.js';
|
|
7
7
|
import { withCanvasWrite } from '../../../core/canvas/db.js';
|
|
8
8
|
import { notFound, usage } from '../../../core/errors.js';
|
|
9
|
-
import { readCursor, readInboxSince, writeCursor } from '../../../core/feed/inbox.js';
|
|
9
|
+
import { hasUnseenWakeMail, readCursor, readInboxSince, writeCursor } from '../../../core/feed/inbox.js';
|
|
10
10
|
import { clearYieldMessage } from '../../../core/runtime/kickoff.js';
|
|
11
11
|
import { transition } from '../../../core/runtime/lifecycle.js';
|
|
12
12
|
import { focusOf, tearDownNode } from '../../../core/runtime/placement.js';
|
|
@@ -224,9 +224,6 @@ function handleSessionBound(ctx) {
|
|
|
224
224
|
});
|
|
225
225
|
return { status: 200, body: result };
|
|
226
226
|
}
|
|
227
|
-
function hasUnseenNonDeferredInbox(nodeId) {
|
|
228
|
-
return readInboxSince(nodeId, readCursor(nodeId)).some((entry) => entry.tier !== 'deferred');
|
|
229
|
-
}
|
|
230
227
|
/** Commit the complete broker-settlement policy while holding one canvas write
|
|
231
228
|
* boundary. This handler intentionally never invokes the broker that called it:
|
|
232
229
|
* the returned directive is the only Pi-side consequence. */
|
|
@@ -300,14 +297,14 @@ function settleBroker(nodeId, request) {
|
|
|
300
297
|
if (decision.action === 'reprompt')
|
|
301
298
|
return { action: 'reprompt', prompt: decision.message };
|
|
302
299
|
if (decision.reason === 'finished') {
|
|
303
|
-
return
|
|
300
|
+
return hasUnseenWakeMail(nodeId) ? { action: 'stay_dormant' } : { action: 'shutdown' };
|
|
304
301
|
}
|
|
305
302
|
if (decision.reason !== 'awaiting' && decision.reason !== 'scheduled') {
|
|
306
303
|
return { action: 'stay_dormant' };
|
|
307
304
|
}
|
|
308
305
|
if (focusOf(nodeId) !== null
|
|
309
306
|
|| hasAttachedBrokerViewers(nodeId)
|
|
310
|
-
||
|
|
307
|
+
|| hasUnseenWakeMail(nodeId))
|
|
311
308
|
return { action: 'stay_dormant' };
|
|
312
309
|
transition(nodeId, 'release');
|
|
313
310
|
return { action: 'shutdown' };
|
|
@@ -11,13 +11,15 @@
|
|
|
11
11
|
// the sanctioned place for it.
|
|
12
12
|
import { existsSync, readdirSync } from 'node:fs';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
-
import { listNodes,
|
|
15
|
-
import { getNode, pruneNodes, pruneToLimit, rebuildIndex, rosterEdges, rosterNodes, subscriptionsOf } from '../../../core/canvas/canvas.js';
|
|
14
|
+
import { getNode, listNodes, pruneNodes, pruneToLimit, rebuildIndex, rosterEdges, rosterNodes, subscriptionsOf } from '../../../core/canvas/canvas.js';
|
|
16
15
|
import { activeBackgroundBashJobs } from '../../../core/bash-jobs.js';
|
|
17
16
|
import { contextDir } from '../../../core/canvas/paths.js';
|
|
17
|
+
import { nowIso } from '../../../core/fs-utils.js';
|
|
18
18
|
import { isBrokerLive } from '../../../core/runtime/model-swap.js';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
import { dashboardRowsAllFromSource, enrichRowsFromSource } from '../../../core/canvas/render-source.js';
|
|
20
|
+
import { localCanvasSource } from '../../../core/canvas/source.js';
|
|
21
|
+
import { listFocuses } from '../../../core/canvas/focuses.js';
|
|
22
|
+
import { ticketCountsForNodes, ticketsAcrossCanvas } from '../../../core/canvas/attention.js';
|
|
21
23
|
import { buildCorpus, resolveRef, } from '../../../core/canvas/history.js';
|
|
22
24
|
import { nodeDir, nodesRoot } from '../../../core/canvas/paths.js';
|
|
23
25
|
import { reapEmptyNodes } from '../../../core/runtime/placement.js';
|
|
@@ -49,7 +51,6 @@ function handleAttentionCounts(ctx) {
|
|
|
49
51
|
const body = { counts: ticketCountsForNodes(rawIds) };
|
|
50
52
|
return { status: 200, body };
|
|
51
53
|
}
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
54
|
// history search / grep / read \u2014 the ranked corpus search, the required-
|
|
54
55
|
// pattern line-hit search, and ref resolution. Lifted verbatim from
|
|
55
56
|
// `commands/canvas-history/{search,grep,read}.ts`' run() bodies: the pure
|
|
@@ -57,7 +58,6 @@ function handleAttentionCounts(ctx) {
|
|
|
57
58
|
// now parses+validates flags and POSTs the normalized query). search and grep
|
|
58
59
|
// share the corpus builder and cursor helpers but are distinct wire contracts
|
|
59
60
|
// \u2014 no `--grep` flag survives on the search query.
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
61
|
function encodeCursor(offset) {
|
|
62
62
|
return Buffer.from(String(offset), 'utf8').toString('base64');
|
|
63
63
|
}
|
|
@@ -168,7 +168,7 @@ function handleHistorySearch(ctx) {
|
|
|
168
168
|
const limit = Math.min(100, Math.max(1, q.limit ?? 20));
|
|
169
169
|
const arts = buildCorpusFromQuery(q);
|
|
170
170
|
const offset = q.cursor !== undefined ? decodeCursor(q.cursor) : 0;
|
|
171
|
-
//
|
|
171
|
+
// ranked / browse mode
|
|
172
172
|
const terms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
173
173
|
let pool = arts;
|
|
174
174
|
let scored;
|
|
@@ -264,13 +264,13 @@ function handleHistoryRead(ctx) {
|
|
|
264
264
|
};
|
|
265
265
|
return { status: 200, body };
|
|
266
266
|
}
|
|
267
|
-
// ---------------------------------------------------------------------------
|
|
268
267
|
// snapshot \u2014 the enriched browser canvas roster + subscribes_to adjacency.
|
|
269
268
|
// Lifted from `commands/canvas-snapshot.ts` run() (dashboardRowsAll + enrichRows).
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const rows =
|
|
273
|
-
|
|
269
|
+
async function handleSnapshot() {
|
|
270
|
+
const focusedNodeIds = new Set(listFocuses().map((f) => f.node_id));
|
|
271
|
+
const rows = await dashboardRowsAllFromSource(localCanvasSource, focusedNodeIds);
|
|
272
|
+
const asks = ticketCountsForNodes(rows.map((r) => r.node_id));
|
|
273
|
+
await enrichRowsFromSource(localCanvasSource, rows, asks);
|
|
274
274
|
const nodes = rows.map((row) => {
|
|
275
275
|
const meta = getNode(row.node_id);
|
|
276
276
|
const hostKind = meta?.host_kind ?? 'tmux';
|
|
@@ -308,13 +308,11 @@ function handleSnapshot() {
|
|
|
308
308
|
const body = { generated_at: nowIso(), nodes, subscriptions };
|
|
309
309
|
return { status: 200, body };
|
|
310
310
|
}
|
|
311
|
-
// ---------------------------------------------------------------------------
|
|
312
311
|
// roster - the lean, set-based topology read (daemon-starvation fix step 2).
|
|
313
312
|
// Exactly two indexed queries (rosterNodes() + rosterEdges()), no enrichRows,
|
|
314
313
|
// no per-row getNode/subscriptionsOf loop, no isStreaming/isPidAlive process
|
|
315
314
|
// probe. Single-flighted below so a burst of concurrent pollers (attach +
|
|
316
315
|
// browser, or several attach viewers) shares one DB pass instead of stacking N.
|
|
317
|
-
// ---------------------------------------------------------------------------
|
|
318
316
|
function buildRoster() {
|
|
319
317
|
const nodes = rosterNodes().map((row) => {
|
|
320
318
|
const hostKind = row.host_kind ?? 'tmux';
|
|
@@ -370,10 +368,8 @@ async function handleRoster() {
|
|
|
370
368
|
const body = await rosterCoalesced();
|
|
371
369
|
return { status: 200, body };
|
|
372
370
|
}
|
|
373
|
-
// ---------------------------------------------------------------------------
|
|
374
371
|
// prune \u2014 the three exclusive modes (COUNT > EMPTY > TTL sweep) + target-gone
|
|
375
372
|
// notification. Lifted from `commands/canvas-prune.ts` run().
|
|
376
|
-
// ---------------------------------------------------------------------------
|
|
377
373
|
/** Validate + narrow the prune body (all fields optional). */
|
|
378
374
|
function parsePruneBody(body) {
|
|
379
375
|
if (body === undefined || body === null)
|
|
@@ -436,10 +432,8 @@ function handlePrune(ctx) {
|
|
|
436
432
|
const body = { pruned, dry_run: dryRun, ttl_days: ttlDays };
|
|
437
433
|
return { status: 200, body };
|
|
438
434
|
}
|
|
439
|
-
// ---------------------------------------------------------------------------
|
|
440
435
|
// rebuild-index \u2014 re-derive rows from on-disk metas + the fail-loud guard.
|
|
441
436
|
// Lifted from `commands/canvas-rebuild-index.ts` (countOnDiskMetas + throw).
|
|
442
|
-
// ---------------------------------------------------------------------------
|
|
443
437
|
function countOnDiskMetas() {
|
|
444
438
|
const root = nodesRoot();
|
|
445
439
|
if (!existsSync(root))
|
|
@@ -18,9 +18,7 @@ import { requireTicketId, resolveTicket } from './inbox.js';
|
|
|
18
18
|
import { invalidRequest, objectBody, optionalString, requiredString } from './validate.js';
|
|
19
19
|
import { toPageFeedbackDTO, toPageFeedbackCommentDTO } from '../map.js';
|
|
20
20
|
const COMMENT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
21
|
// Shared guards
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
22
|
/** Resolve a mutable page ticket: it must exist, be a page, and not have a
|
|
25
23
|
* terminal outcome. Passive pages never settle, so they stay commentable.
|
|
26
24
|
* A terminal ticket refuses every feedback action — settlement won. */
|
|
@@ -45,9 +43,7 @@ function requireCommentId(params) {
|
|
|
45
43
|
}
|
|
46
44
|
return commentId;
|
|
47
45
|
}
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
46
|
// Delivery — the person's turn in the companion conversation
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
47
|
/** The comment as the person's own words: the quoted page content and the
|
|
52
48
|
* note, nothing else. The companion's briefing rides its bearings and
|
|
53
49
|
* persona, never the human turn. */
|
|
@@ -79,9 +75,7 @@ async function deliverCommentTurn(args) {
|
|
|
79
75
|
return { target_node_id: target, status: 'failed', woke: false, error: error instanceof Error ? error.message : String(error) };
|
|
80
76
|
}
|
|
81
77
|
}
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
78
|
// POST /v1/human/inbox/:ticket_id/feedback-comments — create
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
79
|
function parseCreate(ctx) {
|
|
86
80
|
const violations = [];
|
|
87
81
|
const body = objectBody(ctx.body, ['quote', 'context', 'note'], violations);
|
|
@@ -154,9 +148,7 @@ async function handleCreate(ctx) {
|
|
|
154
148
|
};
|
|
155
149
|
return { status: 200, body };
|
|
156
150
|
}
|
|
157
|
-
// ---------------------------------------------------------------------------
|
|
158
151
|
// POST .../feedback-comments/:comment_id/resolve — the companion's report
|
|
159
|
-
// ---------------------------------------------------------------------------
|
|
160
152
|
function parseResolve(ctx) {
|
|
161
153
|
const violations = [];
|
|
162
154
|
const body = objectBody(ctx.body, ['node_id'], violations);
|
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
// Both run INSIDE crtrd, so `daemon_up` is trivially true and `daemon_pid` is
|
|
7
7
|
// this process. Store access is probed by a cheap `listNodes()` — a throw means
|
|
8
8
|
// the store is inaccessible.
|
|
9
|
-
import { listNodes } from '../../../
|
|
9
|
+
import { listNodes } from '../../../core/canvas/canvas.js';
|
|
10
10
|
import { apiSocketPath } from '../../../core/canvas/paths.js';
|
|
11
11
|
import { currentVersion } from '../../../core/self-update.js';
|
|
12
12
|
import { API_VERSION } from '../../../api/index.js';
|
|
13
13
|
import { DEFAULT_INTERVAL_MS } from '../../supervise-cadence.js';
|
|
14
|
+
import { envCrtrdTcp } from '../../../shared/env.js';
|
|
14
15
|
// The daemon's supervise-loop cadence, echoed as an informational field; the
|
|
15
16
|
// loop itself is unchanged (hard constraint). Derived from the single shared
|
|
16
17
|
// constant so it can never drift from crtrd's actual interval.
|
|
@@ -45,7 +46,7 @@ function handleStatus() {
|
|
|
45
46
|
// The store is unreachable — spec §8 store_unavailable.
|
|
46
47
|
throw { status: 503, code: 'store_unavailable', message: 'canvas store is inaccessible' };
|
|
47
48
|
}
|
|
48
|
-
const tcp =
|
|
49
|
+
const tcp = envCrtrdTcp();
|
|
49
50
|
const body = {
|
|
50
51
|
daemon_pid: process.pid,
|
|
51
52
|
tick_interval_ms: TICK_INTERVAL_MS,
|
|
@@ -21,9 +21,7 @@ const REASON_MAX = 1000;
|
|
|
21
21
|
const ACTOR_MAX = 200;
|
|
22
22
|
const DEFAULT_DELIVERY = { placement: 'inline', inbox: true, reply: false };
|
|
23
23
|
const SOURCE_FIELDS = ['sessionName', 'askedBy', 'emittedAt', 'profileName', 'nodeId'];
|
|
24
|
-
// ===========================================================================
|
|
25
24
|
// Body validation
|
|
26
|
-
// ===========================================================================
|
|
27
25
|
function isPlainObject(value) {
|
|
28
26
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
29
27
|
}
|
|
@@ -259,9 +257,7 @@ function requestToDTO(request) {
|
|
|
259
257
|
}
|
|
260
258
|
return dto;
|
|
261
259
|
}
|
|
262
|
-
// ===========================================================================
|
|
263
260
|
// POST /v1/human/requests — create
|
|
264
|
-
// ===========================================================================
|
|
265
261
|
/** Authoring failure is the caller's malformed page, never a server fault. */
|
|
266
262
|
function prepareOrReject(dialect, document, source, delivery) {
|
|
267
263
|
try {
|
|
@@ -312,15 +308,11 @@ function handleCreate(ctx) {
|
|
|
312
308
|
},
|
|
313
309
|
};
|
|
314
310
|
}
|
|
315
|
-
// ===========================================================================
|
|
316
311
|
// GET /v1/human/requests/:request_id — read
|
|
317
|
-
// ===========================================================================
|
|
318
312
|
function handleGet(ctx) {
|
|
319
313
|
return { status: 200, body: requestToDTO(resolveRequest(ctx)) };
|
|
320
314
|
}
|
|
321
|
-
// ===========================================================================
|
|
322
315
|
// POST /v1/human/requests/:request_id/replace — revise the pending page
|
|
323
|
-
// ===========================================================================
|
|
324
316
|
function handleReplace(ctx) {
|
|
325
317
|
const request = resolveRequest(ctx);
|
|
326
318
|
if (request.result !== null)
|
|
@@ -332,9 +324,7 @@ function handleReplace(ctx) {
|
|
|
332
324
|
publishPage(request.dir, prepared);
|
|
333
325
|
return { status: 200, body: requestToDTO({ ...request, manifest: prepared.manifest }) };
|
|
334
326
|
}
|
|
335
|
-
// ===========================================================================
|
|
336
327
|
// Settlement — respond, dismiss, cancel
|
|
337
|
-
// ===========================================================================
|
|
338
328
|
/** The settlement funnels speak the inbox's error vocabulary; requests speak
|
|
339
329
|
* the wire contract's. This is the one translation. */
|
|
340
330
|
function settlementError(error) {
|
|
@@ -19,9 +19,7 @@ function requireInteractionTicketId(ctx) {
|
|
|
19
19
|
}
|
|
20
20
|
return ticketId;
|
|
21
21
|
}
|
|
22
|
-
// ===========================================================================
|
|
23
22
|
// POST /v1/human/bridge — create a terminal bridge without a broker
|
|
24
|
-
// ===========================================================================
|
|
25
23
|
function handleCreateBridge(ctx) {
|
|
26
24
|
const req = ctx.body;
|
|
27
25
|
if (req === undefined || typeof req !== 'object') {
|
|
@@ -46,9 +44,7 @@ function handleCreateBridge(ctx) {
|
|
|
46
44
|
throw new ApiError(400, 'invalid_request', String(error));
|
|
47
45
|
}
|
|
48
46
|
}
|
|
49
|
-
// ===========================================================================
|
|
50
47
|
// POST /v1/human/tickets/:ticket_id/resolve and /cancel
|
|
51
|
-
// ===========================================================================
|
|
52
48
|
function reviewSubmitGuidance() {
|
|
53
49
|
return new ApiError(400, 'invalid_request', 'review approvals use POST /v1/human/reviews/:review_id/submit');
|
|
54
50
|
}
|
|
@@ -100,7 +96,7 @@ async function handleResolve(ctx) {
|
|
|
100
96
|
return { status: 200, body: { completed: true, reply_delivered: replyExpected, kind: 'page' } };
|
|
101
97
|
}
|
|
102
98
|
function hasPublishedTicket(dir) {
|
|
103
|
-
return existsSync(pageManifestPath(dir)) ||
|
|
99
|
+
return existsSync(pageManifestPath(dir)) || readTicketResult(dir) !== null;
|
|
104
100
|
}
|
|
105
101
|
async function handleCancel(ctx) {
|
|
106
102
|
const ticketId = requireInteractionTicketId(ctx);
|
|
@@ -18,9 +18,7 @@ import { filterTerminalReviewTickets } from '../../../core/review/ticket-filter.
|
|
|
18
18
|
const DEFAULT_CANCEL_REASON = 'Canceled from the inbox.';
|
|
19
19
|
const CANCEL_REASON_MAX = 1000;
|
|
20
20
|
const TICKET_ID_PATTERN = /^[a-f0-9]{64}$/;
|
|
21
|
-
// ===========================================================================
|
|
22
21
|
// Opaque ticket id resolution
|
|
23
|
-
// ===========================================================================
|
|
24
22
|
function ticketHash(canonicalRoot, ticketBasename) {
|
|
25
23
|
return createHash('sha256').update(`${canonicalRoot}\0${ticketBasename}`, 'utf8').digest('hex');
|
|
26
24
|
}
|
|
@@ -70,9 +68,7 @@ export function requireTicketId(params) {
|
|
|
70
68
|
}
|
|
71
69
|
return ticketId;
|
|
72
70
|
}
|
|
73
|
-
// ===========================================================================
|
|
74
71
|
// Projections — humanloop canonical shapes → wire DTOs
|
|
75
|
-
// ===========================================================================
|
|
76
72
|
function pageSlotToDTO(slot) {
|
|
77
73
|
const dto = {
|
|
78
74
|
kind: slot.kind,
|
|
@@ -166,9 +162,7 @@ const SLOT_VALIDATION_PATTERNS = [
|
|
|
166
162
|
function isSlotValidationError(err) {
|
|
167
163
|
return err instanceof Error && SLOT_VALIDATION_PATTERNS.some((pattern) => pattern.test(err.message));
|
|
168
164
|
}
|
|
169
|
-
// ===========================================================================
|
|
170
165
|
// GET /v1/human/inbox — enumerate
|
|
171
|
-
// ===========================================================================
|
|
172
166
|
/** The one page-summary projection: the pending list and the per-node history differ only in which tickets they walk. */
|
|
173
167
|
function pageSummaryToDTO(item, canonicalRoot) {
|
|
174
168
|
const dto = {
|
|
@@ -222,9 +216,7 @@ function handleList() {
|
|
|
222
216
|
});
|
|
223
217
|
return { status: 200, body: { tickets } };
|
|
224
218
|
}
|
|
225
|
-
// ===========================================================================
|
|
226
219
|
// GET /v1/human/inbox/history?node_id=<id> — page ticket history
|
|
227
|
-
// ===========================================================================
|
|
228
220
|
function handleHistory(ctx) {
|
|
229
221
|
// Home-wide when `node_id` is absent; an explicitly empty param is a caller bug.
|
|
230
222
|
const nodeId = ctx.query.get('node_id');
|
|
@@ -236,9 +228,7 @@ function handleHistory(ctx) {
|
|
|
236
228
|
const tickets = history.map((item) => pageSummaryToDTO(item, canonicalRoot));
|
|
237
229
|
return { status: 200, body: { tickets } };
|
|
238
230
|
}
|
|
239
|
-
// ===========================================================================
|
|
240
231
|
// GET /v1/human/inbox/:ticket_id — read a page ticket
|
|
241
|
-
// ===========================================================================
|
|
242
232
|
function handleGetPage(ctx) {
|
|
243
233
|
const ticketId = requireTicketId(ctx.params);
|
|
244
234
|
const resolved = resolveTicket(ticketId);
|
|
@@ -270,9 +260,7 @@ function handleGetPage(ctx) {
|
|
|
270
260
|
},
|
|
271
261
|
};
|
|
272
262
|
}
|
|
273
|
-
// ===========================================================================
|
|
274
263
|
// POST /v1/human/inbox/:ticket_id/respond — submit
|
|
275
|
-
// ===========================================================================
|
|
276
264
|
async function handleRespond(ctx) {
|
|
277
265
|
const ticketId = requireTicketId(ctx.params);
|
|
278
266
|
const request = validateRespondRequest(ctx.body);
|
|
@@ -317,9 +305,7 @@ async function handleRespond(ctx) {
|
|
|
317
305
|
throw new ApiError(500, 'internal', 'the ticket could not be finalized');
|
|
318
306
|
}
|
|
319
307
|
}
|
|
320
|
-
// ===========================================================================
|
|
321
308
|
// POST /v1/human/inbox/:ticket_id/progress — autosave
|
|
322
|
-
// ===========================================================================
|
|
323
309
|
function handleProgress(ctx) {
|
|
324
310
|
const ticketId = requireTicketId(ctx.params);
|
|
325
311
|
const request = validateProgressRequest(ctx.body);
|
|
@@ -346,9 +332,7 @@ function handleProgress(ctx) {
|
|
|
346
332
|
throw new ApiError(500, 'internal', 'progress could not be saved');
|
|
347
333
|
}
|
|
348
334
|
}
|
|
349
|
-
// ===========================================================================
|
|
350
335
|
// POST /v1/human/inbox/:ticket_id/cancel — cancel
|
|
351
|
-
// ===========================================================================
|
|
352
336
|
async function handleCancel(ctx) {
|
|
353
337
|
const ticketId = requireTicketId(ctx.params);
|
|
354
338
|
const request = validateCancelRequest(ctx.body);
|
|
@@ -378,9 +362,7 @@ async function handleCancel(ctx) {
|
|
|
378
362
|
throw new ApiError(500, 'internal', 'the ticket could not be canceled');
|
|
379
363
|
}
|
|
380
364
|
}
|
|
381
|
-
// ===========================================================================
|
|
382
365
|
// GET /v1/human/inbox/:ticket_id/response — published response
|
|
383
|
-
// ===========================================================================
|
|
384
366
|
function handleGetResponse(ctx) {
|
|
385
367
|
const ticketId = requireTicketId(ctx.params);
|
|
386
368
|
const resolved = resolveTicket(ticketId);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// to different documents for two nodes (each has its own context store, project
|
|
9
9
|
// stack, and profile). Resolution therefore runs target-addressed —
|
|
10
10
|
// `resolveMemoryDocForTarget` with the node's cwd/profile/id off canvas.
|
|
11
|
-
import { getNode } from '../../../
|
|
11
|
+
import { getNode } from '../../../core/canvas/canvas.js';
|
|
12
12
|
import { notFound, usage } from '../../../core/errors.js';
|
|
13
13
|
import { resolveMemoryDocForTarget } from '../../../core/memory-resolver.js';
|
|
14
14
|
import { isDocLinkName } from '../../../core/memory/doc-link-grammar.js';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// Message handler (spec §6.2). `POST /v1/nodes/{id}/messages` delivers an
|
|
2
2
|
// inbox message now (201) or fresh-revives with no inbox entry. `{id}` is the
|
|
3
3
|
// target node.
|
|
4
|
-
import { appendInbox,
|
|
4
|
+
import { appendInbox, cancelPendingHumanMessages } from '../../../core/feed/inbox.js';
|
|
5
|
+
import { reviveNode } from '../../../core/runtime/revive.js';
|
|
5
6
|
import { BrokerUnreachableError } from '../../../core/runtime/broker-request.js';
|
|
6
7
|
import { isBrokerLive } from '../../../core/runtime/model-swap.js';
|
|
7
8
|
import { deliverLive, interruptLive } from '../../../core/runtime/interactive-deliver.js';
|
|
8
|
-
import { cancelPendingHumanMessages } from '../../../core/feed/inbox.js';
|
|
9
9
|
import { isProfilePaused } from '../../../core/profiles/manifest.js';
|
|
10
|
-
import { withFreshTerminalGuard } from '../../../core/canvas/canvas.js';
|
|
10
|
+
import { getNode, withFreshTerminalGuard } from '../../../core/canvas/canvas.js';
|
|
11
11
|
import { assertNotFinalized, assertFinalizedForReopen, commitReopen, commitReopenResident, } from '../../../core/runtime/reopen.js';
|
|
12
12
|
import { writeOutputSchema } from '../../../core/runtime/structured-output.js';
|
|
13
13
|
import { appendSituationalContext, formatSituationalProse } from '../../../core/runtime/situational-context.js';
|
|
@@ -17,9 +17,7 @@ import { hasFreshGroundState } from '../../../core/runtime/revive.js';
|
|
|
17
17
|
import { notFound, usage } from '../../../core/errors.js';
|
|
18
18
|
import { InputError } from '../../../core/io.js';
|
|
19
19
|
import { ApiError } from '../../../api/index.js';
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
20
|
// POST /v1/nodes/{id}/messages
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
21
|
const TIERS = ['critical', 'urgent', 'normal', 'deferred'];
|
|
24
22
|
/** Validate + narrow one caller-supplied runtime card. A bare kind is crouter's
|
|
25
23
|
* own closed vocabulary, so a caller must namespace: no product can shadow a
|
|
@@ -209,7 +207,7 @@ async function handleMessage(ctx) {
|
|
|
209
207
|
const schema = req.output_schema !== undefined && req.output_schema.trim() !== ''
|
|
210
208
|
? parseSchema(req.output_schema)
|
|
211
209
|
: null;
|
|
212
|
-
//
|
|
210
|
+
// Fresh revive: no inbox entry, resume:false (spec §7.7).
|
|
213
211
|
// Mirrors the CLI's immediate --fresh: validate/clear the finalization latch
|
|
214
212
|
// (--reopen) and upsert the situational sidecar BEFORE the revive, so the
|
|
215
213
|
// fresh kickoff picks up the update. The goal/roadmap recoverable-state guard
|
|
@@ -344,9 +342,7 @@ async function handleMessage(ctx) {
|
|
|
344
342
|
};
|
|
345
343
|
return { status: 201, body };
|
|
346
344
|
}
|
|
347
|
-
// ---------------------------------------------------------------------------
|
|
348
345
|
// POST /v1/nodes/{id}/interrupt
|
|
349
|
-
// ---------------------------------------------------------------------------
|
|
350
346
|
/** The human Esc, first-class. Cancel pending undelivered human sends FIRST,
|
|
351
347
|
* then abort the live turn — in that order, so a watcher injection racing
|
|
352
348
|
* between the two is caught by the abort right behind it. A dormant target is
|