@crouton-kit/crouter 0.3.32 → 0.3.33
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/README.md +8 -0
- package/dist/build-root.js +1 -0
- package/dist/builtin-memory/crouter-development/marketplaces.md +2 -2
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +2 -2
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +5 -5
- package/dist/builtin-memory/crouter-development/personas.md +3 -4
- package/dist/builtin-memory/crouter-development/plugins.md +18 -18
- package/dist/builtin-memory/design.md +4 -1
- package/dist/builtin-memory/development.md +4 -1
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/nodes-and-canvas.md +6 -6
- package/dist/builtin-memory/internal/storage-tiers.md +5 -5
- package/dist/builtin-memory/planning.md +4 -1
- package/dist/builtin-memory/product.md +80 -0
- package/dist/builtin-memory/spec.md +4 -1
- package/dist/builtin-personas/advisor/PERSONA.md +10 -0
- package/dist/builtin-personas/design/PERSONA.md +2 -2
- package/dist/builtin-personas/design/orchestrator.md +3 -3
- package/dist/builtin-personas/developer/PERSONA.md +1 -1
- package/dist/builtin-personas/developer/orchestrator.md +2 -2
- package/dist/builtin-personas/explore/PERSONA.md +3 -3
- package/dist/builtin-personas/explore/orchestrator.md +4 -2
- package/dist/builtin-personas/general/PERSONA.md +1 -1
- package/dist/builtin-personas/general/orchestrator.md +1 -1
- package/dist/builtin-personas/orchestration-kernel.md +7 -14
- package/dist/builtin-personas/plan/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/orchestrator.md +2 -2
- package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -1
- package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -1
- package/dist/builtin-personas/product/PERSONA.md +18 -0
- package/dist/builtin-personas/product/orchestrator.md +14 -0
- package/dist/builtin-personas/product/teardown/PERSONA.md +13 -0
- package/dist/builtin-personas/review/PERSONA.md +1 -1
- package/dist/builtin-personas/review/orchestrator.md +1 -1
- package/dist/builtin-personas/runtime-base.md +5 -0
- package/dist/builtin-personas/spec/PERSONA.md +1 -1
- package/dist/builtin-personas/spec/orchestrator.md +2 -2
- package/dist/builtin-personas/spec/requirements/PERSONA.md +1 -1
- package/dist/builtin-views/canvas/core.mjs +82 -1
- package/dist/builtin-views/canvas/tui.mjs +9 -5
- package/dist/builtin-views/canvas/web.jsx +3 -2
- package/dist/builtin-views/chat/core.mjs +725 -0
- package/dist/builtin-views/chat/text.mjs +101 -0
- package/dist/builtin-views/chat/tui.mjs +368 -0
- package/dist/builtin-views/chat/web.jsx +367 -0
- package/dist/builtin-views/prompt-review/core.mjs +863 -0
- package/dist/builtin-views/prompt-review/text.mjs +15 -0
- package/dist/builtin-views/prompt-review/tui.mjs +196 -0
- package/dist/builtin-views/prompt-review/web.jsx +484 -0
- package/dist/builtin-views/settings/core.mjs +397 -0
- package/dist/builtin-views/settings/text.mjs +40 -0
- package/dist/builtin-views/settings/tui.mjs +95 -0
- package/dist/builtin-views/settings/web.jsx +167 -0
- package/dist/cli.js +16 -3
- package/dist/clients/attach/__tests__/autocomplete-and-bash-mode.test.js +96 -0
- package/dist/clients/attach/__tests__/bash-bang-routing.test.js +58 -0
- package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.js +56 -0
- package/dist/clients/attach/__tests__/titled-editor.test.js +22 -1
- package/dist/clients/attach/attach-cmd.d.ts +18 -0
- package/dist/clients/attach/attach-cmd.js +1723 -795
- package/dist/clients/attach/canvas-panels.js +2 -2
- package/dist/clients/attach/chat-view.d.ts +10 -0
- package/dist/clients/attach/chat-view.js +23 -0
- package/dist/clients/attach/config-load.d.ts +8 -4
- package/dist/clients/attach/config-load.js +2 -0
- package/dist/clients/attach/extension-dialogs.d.ts +2 -3
- package/dist/clients/attach/extension-dialogs.js +7 -9
- package/dist/clients/attach/graph-overlay.js +3 -2
- package/dist/clients/attach/input-controller.d.ts +4 -3
- package/dist/clients/attach/input-controller.js +24 -3
- package/dist/clients/attach/slash-commands.d.ts +7 -7
- package/dist/clients/attach/slash-commands.js +38 -14
- package/dist/clients/attach/titled-editor.js +18 -14
- package/dist/clients/attach/view-socket.d.ts +2 -1
- package/dist/clients/attach/view-socket.js +27 -8
- package/dist/clients/web/__tests__/source-cache.test.d.ts +1 -0
- package/dist/clients/web/__tests__/source-cache.test.js +32 -0
- package/dist/clients/web/dev-server.js +1 -0
- package/dist/clients/web/events.js +9 -11
- package/dist/clients/web/server.d.ts +4 -3
- package/dist/clients/web/server.js +138 -31
- package/dist/clients/web/source-cache.d.ts +10 -0
- package/dist/clients/web/source-cache.js +57 -0
- package/dist/clients/web/web-cmd.js +28 -9
- package/dist/commands/__tests__/human.test.js +30 -28
- package/dist/commands/canvas-history/search.js +1 -1
- package/dist/commands/canvas-history.js +5 -8
- package/dist/commands/canvas-issue.d.ts +2 -0
- package/dist/commands/canvas-issue.js +148 -0
- package/dist/commands/canvas-prune.js +19 -0
- package/dist/commands/canvas-rebuild-index.d.ts +2 -0
- package/dist/commands/canvas-rebuild-index.js +57 -0
- package/dist/commands/canvas-snapshot.d.ts +2 -0
- package/dist/commands/canvas-snapshot.js +48 -0
- package/dist/commands/canvas-tmux-spread.d.ts +2 -0
- package/dist/commands/canvas-tmux-spread.js +188 -0
- package/dist/commands/canvas.d.ts +1 -0
- package/dist/commands/canvas.js +16 -1
- package/dist/commands/chord.js +143 -48
- package/dist/commands/daemon.js +3 -3
- package/dist/commands/human/prompts.d.ts +0 -1
- package/dist/commands/human/prompts.js +39 -54
- package/dist/commands/human/queue.d.ts +12 -1
- package/dist/commands/human/queue.js +468 -73
- package/dist/commands/human/shared.d.ts +3 -4
- package/dist/commands/human/shared.js +3 -3
- package/dist/commands/human.js +11 -10
- package/dist/commands/memory/find.js +3 -2
- package/dist/commands/memory/list.js +4 -3
- package/dist/commands/memory/read.js +3 -3
- package/dist/commands/memory/shared.d.ts +1 -4
- package/dist/commands/memory/shared.js +6 -9
- package/dist/commands/memory/write.js +3 -3
- package/dist/commands/memory.js +4 -7
- package/dist/commands/node-context.d.ts +2 -0
- package/dist/commands/node-context.js +172 -0
- package/dist/commands/node-snapshot.d.ts +2 -0
- package/dist/commands/node-snapshot.js +123 -0
- package/dist/commands/node.js +228 -52
- package/dist/commands/pkg/plugin-inspect.js +6 -6
- package/dist/commands/pkg/plugin-manage.js +1 -1
- package/dist/commands/pkg/plugin.js +2 -2
- package/dist/commands/pkg.js +2 -2
- package/dist/commands/push.d.ts +2 -0
- package/dist/commands/push.js +79 -11
- package/dist/commands/revive.js +74 -1
- package/dist/commands/search/answer.d.ts +1 -0
- package/dist/commands/search/answer.js +50 -0
- package/dist/commands/search/contents.d.ts +1 -0
- package/dist/commands/search/contents.js +96 -0
- package/dist/commands/search/exa.d.ts +53 -0
- package/dist/commands/search/exa.js +155 -0
- package/dist/commands/search/puremd.d.ts +11 -0
- package/dist/commands/search/puremd.js +53 -0
- package/dist/commands/search/web.d.ts +1 -0
- package/dist/commands/search/web.js +65 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +24 -0
- package/dist/commands/sys/__tests__/sync-import.test.d.ts +1 -0
- package/dist/commands/sys/__tests__/sync-import.test.js +72 -0
- package/dist/commands/sys/config.js +59 -10
- package/dist/commands/sys/doctor.js +56 -4
- package/dist/commands/sys/prompt-review.d.ts +1 -0
- package/dist/commands/sys/prompt-review.js +178 -0
- package/dist/commands/sys/promptstudio.d.ts +2 -0
- package/dist/commands/sys/promptstudio.js +65 -0
- package/dist/commands/sys/settings.d.ts +2 -0
- package/dist/commands/sys/settings.js +16 -0
- package/dist/commands/sys/sync.js +311 -159
- package/dist/commands/sys/sysprompt.d.ts +1 -0
- package/dist/commands/sys/sysprompt.js +86 -0
- package/dist/commands/sys.js +9 -5
- package/dist/commands/view-new.js +2 -2
- package/dist/core/__tests__/artifact-paths.test.d.ts +1 -0
- package/dist/core/__tests__/artifact-paths.test.js +44 -0
- package/dist/core/__tests__/broker-preflight.test.d.ts +1 -0
- package/dist/core/__tests__/broker-preflight.test.js +85 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.js +15 -0
- package/dist/core/__tests__/canvas.test.js +37 -1
- package/dist/core/__tests__/chat-view-reconnect.test.d.ts +1 -0
- package/dist/core/__tests__/chat-view-reconnect.test.js +70 -0
- package/dist/core/__tests__/child-death-wake.test.js +11 -2
- package/dist/core/__tests__/close.test.js +39 -1
- package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +1 -0
- package/dist/core/__tests__/connection-reconnect-resume.test.js +37 -0
- package/dist/core/__tests__/context-intro.test.js +48 -14
- package/dist/core/__tests__/daemon-boot.test.js +182 -9
- package/dist/core/__tests__/editor-label.test.d.ts +1 -0
- package/dist/core/__tests__/editor-label.test.js +26 -0
- package/dist/core/__tests__/fault-marker.test.d.ts +1 -0
- package/dist/core/__tests__/fault-marker.test.js +112 -0
- package/dist/core/__tests__/fault-retry-rewind.test.d.ts +1 -0
- package/dist/core/__tests__/fault-retry-rewind.test.js +123 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +30 -4
- package/dist/core/__tests__/fixtures/fake-engine.js +156 -17
- package/dist/core/__tests__/fixtures/fake-pi-host.js +10 -1
- package/dist/core/__tests__/full/human-new-window-regression.test.js +1 -1
- package/dist/core/__tests__/helpers/harness.d.ts +1 -1
- package/dist/core/__tests__/helpers/harness.js +2 -2
- package/dist/core/__tests__/human-stranded-deliver.test.js +38 -1
- package/dist/core/__tests__/human-surface-target.test.js +1 -1
- package/dist/core/__tests__/kickoff.test.js +18 -19
- package/dist/core/__tests__/memory-resolver.test.js +12 -1
- package/dist/core/__tests__/migration.test.js +49 -14
- package/dist/core/__tests__/model-ladders.test.d.ts +1 -0
- package/dist/core/__tests__/model-ladders.test.js +160 -0
- package/dist/core/__tests__/node-env.test.d.ts +1 -0
- package/dist/core/__tests__/node-env.test.js +26 -0
- package/dist/core/__tests__/push-final-guard.test.js +7 -1
- package/dist/core/__tests__/relaunch-root.test.js +9 -9
- package/dist/core/__tests__/revive.test.js +36 -6
- package/dist/core/__tests__/spawn-root.test.js +24 -1
- package/dist/core/__tests__/stop-guard.test.js +10 -0
- package/dist/core/__tests__/stranded-relaunch.test.d.ts +1 -0
- package/dist/core/__tests__/stranded-relaunch.test.js +72 -0
- package/dist/core/__tests__/tmux-surface.test.js +5 -1
- package/dist/core/__tests__/unknown-path.test.js +19 -19
- package/dist/core/__tests__/yield-ensures-daemon.test.d.ts +1 -0
- package/dist/core/__tests__/yield-ensures-daemon.test.js +54 -0
- package/dist/core/artifact.d.ts +2 -33
- package/dist/core/artifact.js +27 -87
- package/dist/core/bootstrap.js +2 -0
- package/dist/core/canvas/browse/__tests__/model.test.js +35 -1
- package/dist/core/canvas/browse/app.js +87 -17
- package/dist/core/canvas/browse/model.d.ts +10 -1
- package/dist/core/canvas/browse/model.js +37 -1
- package/dist/core/canvas/browse/render.js +35 -33
- package/dist/core/canvas/canvas.d.ts +7 -0
- package/dist/core/canvas/canvas.js +54 -5
- package/dist/core/canvas/db.js +35 -8
- package/dist/core/canvas/labels.d.ts +6 -8
- package/dist/core/canvas/labels.js +8 -11
- package/dist/core/canvas/nav-model.d.ts +5 -4
- package/dist/core/canvas/nav-model.js +31 -17
- package/dist/core/canvas/paths.d.ts +18 -1
- package/dist/core/canvas/paths.js +31 -2
- package/dist/core/canvas/render.d.ts +25 -0
- package/dist/core/canvas/render.js +96 -13
- package/dist/core/canvas/status-glyph.d.ts +35 -0
- package/dist/core/canvas/status-glyph.js +104 -0
- package/dist/core/canvas/types.d.ts +2 -2
- package/dist/core/config.js +114 -16
- package/dist/core/fault-classifier.d.ts +41 -0
- package/dist/core/fault-classifier.js +110 -0
- package/dist/core/feed/inbox.d.ts +15 -0
- package/dist/core/feed/inbox.js +40 -14
- package/dist/core/frontmatter.d.ts +1 -11
- package/dist/core/frontmatter.js +1 -55
- package/dist/core/hearth/config.d.ts +2 -0
- package/dist/core/hearth/config.js +75 -0
- package/dist/core/hearth/guest-env.d.ts +2 -0
- package/dist/core/hearth/guest-env.js +11 -0
- package/dist/core/hearth/index.d.ts +5 -0
- package/dist/core/hearth/index.js +5 -0
- package/dist/core/hearth/provider.d.ts +21 -0
- package/dist/core/hearth/provider.js +10 -0
- package/dist/core/hearth/providers/blaxel-bootstrap.d.ts +10 -0
- package/dist/core/hearth/providers/blaxel-bootstrap.js +39 -0
- package/dist/core/hearth/providers/blaxel-home.d.ts +11 -0
- package/dist/core/hearth/providers/blaxel-home.js +126 -0
- package/dist/core/hearth/providers/blaxel.d.ts +26 -0
- package/dist/core/hearth/providers/blaxel.js +208 -0
- package/dist/core/hearth/providers/types.d.ts +72 -0
- package/dist/core/hearth/providers/types.js +1 -0
- package/dist/core/hearth/registry.d.ts +15 -0
- package/dist/core/hearth/registry.js +179 -0
- package/dist/core/hearth/types.d.ts +121 -0
- package/dist/core/hearth/types.js +1 -0
- package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +1 -0
- package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +79 -0
- package/dist/core/{skill-sync → host-exports}/builtins.d.ts +21 -13
- package/dist/core/host-exports/builtins.js +68 -0
- package/dist/core/{skill-sync → host-exports}/export.d.ts +9 -6
- package/dist/core/{skill-sync → host-exports}/export.js +37 -14
- package/dist/core/io.js +1 -1
- package/dist/core/log.d.ts +9 -0
- package/dist/core/log.js +113 -0
- package/dist/core/memory-resolver.d.ts +8 -8
- package/dist/core/memory-resolver.js +15 -28
- package/dist/core/personas/index.d.ts +4 -4
- package/dist/core/personas/index.js +2 -2
- package/dist/core/personas/loader.d.ts +51 -0
- package/dist/core/personas/loader.js +54 -50
- package/dist/core/personas/resolve.d.ts +43 -27
- package/dist/core/personas/resolve.js +336 -94
- package/dist/core/runtime/auth-reload.d.ts +7 -0
- package/dist/core/runtime/auth-reload.js +69 -0
- package/dist/core/runtime/bearings.d.ts +35 -15
- package/dist/core/runtime/bearings.js +305 -44
- package/dist/core/runtime/branded-host.d.ts +4 -2
- package/dist/core/runtime/branded-host.js +66 -4
- package/dist/core/runtime/broker-protocol.d.ts +38 -3
- package/dist/core/runtime/broker.js +343 -18
- package/dist/core/runtime/close.js +37 -29
- package/dist/core/runtime/connectivity.d.ts +12 -0
- package/dist/core/runtime/connectivity.js +73 -0
- package/dist/core/runtime/fault-recovery-nudge.d.ts +2 -0
- package/dist/core/runtime/fault-recovery-nudge.js +2 -0
- package/dist/core/runtime/fault-recovery.d.ts +23 -0
- package/dist/core/runtime/fault-recovery.js +74 -0
- package/dist/core/runtime/fault.d.ts +25 -0
- package/dist/core/runtime/fault.js +176 -0
- package/dist/core/runtime/front-door.d.ts +1 -1
- package/dist/core/runtime/front-door.js +2 -2
- package/dist/core/runtime/host.d.ts +10 -0
- package/dist/core/runtime/host.js +71 -9
- package/dist/core/runtime/kickoff.js +31 -45
- package/dist/core/runtime/launch.d.ts +32 -18
- package/dist/core/runtime/launch.js +148 -43
- package/dist/core/runtime/model-swap.d.ts +18 -0
- package/dist/core/runtime/model-swap.js +95 -0
- package/dist/core/runtime/naming.d.ts +7 -0
- package/dist/core/runtime/naming.js +61 -9
- package/dist/core/runtime/nodes.js +6 -1
- package/dist/core/runtime/persona.js +16 -17
- package/dist/core/runtime/placement.d.ts +36 -31
- package/dist/core/runtime/placement.js +149 -64
- package/dist/core/runtime/promote.d.ts +2 -0
- package/dist/core/runtime/promote.js +23 -3
- package/dist/core/runtime/recycle.js +6 -4
- package/dist/core/runtime/reset.d.ts +5 -2
- package/dist/core/runtime/reset.js +2 -2
- package/dist/core/runtime/revive.d.ts +4 -0
- package/dist/core/runtime/revive.js +27 -9
- package/dist/core/runtime/roadmap.d.ts +1 -1
- package/dist/core/runtime/roadmap.js +1 -1
- package/dist/core/runtime/spawn.d.ts +2 -2
- package/dist/core/runtime/spawn.js +30 -6
- package/dist/core/runtime/stop-guard.js +6 -1
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +1 -1
- package/dist/core/runtime/tmux.d.ts +29 -6
- package/dist/core/runtime/tmux.js +134 -80
- package/dist/core/runtime/view-socket-client.d.ts +46 -0
- package/dist/core/runtime/view-socket-client.js +203 -0
- package/dist/core/substrate/index.d.ts +1 -1
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/on-read.js +16 -4
- package/dist/core/substrate/render.d.ts +10 -13
- package/dist/core/substrate/render.js +56 -51
- package/dist/core/tui/host.js +83 -18
- package/dist/core/view/__tests__/transport-cache.test.d.ts +1 -0
- package/dist/core/view/__tests__/transport-cache.test.js +62 -0
- package/dist/core/view/contract.d.ts +20 -0
- package/dist/core/view/stream-local.d.ts +3 -0
- package/dist/core/view/stream-local.js +184 -0
- package/dist/core/view/transport-cache.d.ts +8 -0
- package/dist/core/view/transport-cache.js +38 -0
- package/dist/core/view/transport-local.js +4 -0
- package/dist/core/view/transport.d.ts +7 -1
- package/dist/daemon/crtrd.d.ts +3 -19
- package/dist/daemon/crtrd.js +139 -178
- package/dist/daemon/manage.d.ts +18 -1
- package/dist/daemon/manage.js +54 -9
- package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.js +289 -0
- package/dist/hearth/wake-proxy/__tests__/config-timeout.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/config-timeout.test.js +34 -0
- package/dist/hearth/wake-proxy/__tests__/guest-source.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/guest-source.test.js +203 -0
- package/dist/hearth/wake-proxy/__tests__/hardening.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/hardening.test.js +59 -0
- package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.js +372 -0
- package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.js +258 -0
- package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.js +437 -0
- package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.js +455 -0
- package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.js +15 -0
- package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.js +141 -0
- package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.d.ts +1 -0
- package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.js +143 -0
- package/dist/hearth/wake-proxy/anthropic-oauth.d.ts +58 -0
- package/dist/hearth/wake-proxy/anthropic-oauth.js +189 -0
- package/dist/hearth/wake-proxy/auth.d.ts +22 -0
- package/dist/hearth/wake-proxy/auth.js +128 -0
- package/dist/hearth/wake-proxy/config.d.ts +2 -0
- package/dist/hearth/wake-proxy/config.js +151 -0
- package/dist/hearth/wake-proxy/guest-source.d.ts +14 -0
- package/dist/hearth/wake-proxy/guest-source.js +130 -0
- package/dist/hearth/wake-proxy/hearth-status.d.ts +44 -0
- package/dist/hearth/wake-proxy/hearth-status.js +267 -0
- package/dist/hearth/wake-proxy/home.d.ts +67 -0
- package/dist/hearth/wake-proxy/home.js +297 -0
- package/dist/hearth/wake-proxy/main.d.ts +1 -0
- package/dist/hearth/wake-proxy/main.js +134 -0
- package/dist/hearth/wake-proxy/model-auth.d.ts +13 -0
- package/dist/hearth/wake-proxy/model-auth.js +345 -0
- package/dist/hearth/wake-proxy/proxy.d.ts +35 -0
- package/dist/hearth/wake-proxy/proxy.js +716 -0
- package/dist/hearth/wake-proxy/public-source-gate.d.ts +9 -0
- package/dist/hearth/wake-proxy/public-source-gate.js +409 -0
- package/dist/hearth/wake-proxy/redact.d.ts +1 -0
- package/dist/hearth/wake-proxy/redact.js +17 -0
- package/dist/hearth/wake-proxy/server.d.ts +11 -0
- package/dist/hearth/wake-proxy/server.js +142 -0
- package/dist/hearth/wake-proxy/state.d.ts +18 -0
- package/dist/hearth/wake-proxy/state.js +342 -0
- package/dist/hearth/wake-proxy/types.d.ts +76 -0
- package/dist/hearth/wake-proxy/types.js +1 -0
- package/dist/index.js +3 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +6 -5
- package/dist/pi-extensions/canvas-context-intro.js +7 -7
- package/dist/pi-extensions/canvas-doc-substrate.js +19 -22
- package/dist/pi-extensions/canvas-nav.js +15 -8
- package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
- package/dist/pi-extensions/canvas-stophook.js +78 -11
- package/dist/types.d.ts +39 -20
- package/dist/types.js +38 -13
- package/dist/web/runtime.js +141 -42
- package/dist/web/transport-http.js +7 -5
- package/dist/web/transport-stream.d.ts +3 -0
- package/dist/web/transport-stream.js +204 -0
- package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
- package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
- package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
- package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
- package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
- package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
- package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
- package/dist/web-client/assets/index-BZUxTkv5.css +2 -0
- package/dist/web-client/assets/index-D36PNBj4.js +80 -0
- package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
- package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
- package/dist/web-client/index.html +3 -2
- package/package.json +32 -10
- package/dist/core/__tests__/error-stall-recycle.test.js +0 -141
- package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +0 -57
- package/dist/core/skill-sync/builtins.js +0 -112
- package/dist/core/skill-sync/claude-plugins.d.ts +0 -23
- package/dist/core/skill-sync/claude-plugins.js +0 -71
- package/dist/core/skill-sync/engine.d.ts +0 -42
- package/dist/core/skill-sync/engine.js +0 -633
- package/dist/core/skill-sync/manifest.d.ts +0 -64
- package/dist/core/skill-sync/manifest.js +0 -181
- package/dist/core/skill-sync/profile.d.ts +0 -76
- package/dist/core/skill-sync/profile.js +0 -173
- package/dist/core/skill-sync/snapshot.d.ts +0 -57
- package/dist/core/skill-sync/snapshot.js +0 -120
- package/dist/pi-extensions/canvas-commands.d.ts +0 -37
- package/dist/pi-extensions/canvas-commands.js +0 -113
- package/dist/pi-extensions/canvas-resume.d.ts +0 -21
- package/dist/pi-extensions/canvas-resume.js +0 -83
- package/dist/pi-extensions/canvas-view.d.ts +0 -21
- package/dist/pi-extensions/canvas-view.js +0 -76
- package/dist/web-client/assets/index-BUvQb4hR.css +0 -2
- package/dist/web-client/assets/index-ClLQXYAE.js +0 -10
- /package/dist/{core/__tests__/error-stall-recycle.test.d.ts → clients/attach/__tests__/autocomplete-and-bash-mode.test.d.ts} +0 -0
- /package/dist/{core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts → clients/attach/__tests__/bash-bang-routing.test.d.ts} +0 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// Layout (the framing a revived node sees):
|
|
9
9
|
// <roadmap file=…>…</roadmap> its evolving plan — the source of truth
|
|
10
10
|
// <context-dir path=…>…</context-dir> what artifacts exist on disk
|
|
11
|
-
// <feed>Awaiting N nodes … digest</feed>
|
|
11
|
+
// <feed>Awaiting N nodes … digest</feed> live wait state + unread/catch-up feed
|
|
12
12
|
// <yield-message>…</yield-message> the note its prior self left on yield
|
|
13
13
|
//
|
|
14
14
|
// The roadmap (NOT the original spawn prompt) carries the goal on a refresh: its
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
// yield-message is one-shot (consumed on the next revive).
|
|
20
20
|
import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync, readdirSync, } from 'node:fs';
|
|
21
21
|
import { join } from 'node:path';
|
|
22
|
-
import { homedir } from 'node:os';
|
|
23
22
|
import { contextDir, reportsDir, getNode, subscriptionsOf, subscribersOf, } from '../canvas/index.js';
|
|
24
23
|
import { readRoadmap, roadmapPath } from './roadmap.js';
|
|
25
24
|
import { buildWakeBearings } from './bearings.js';
|
|
@@ -132,56 +131,41 @@ export function drainBearings(meta) {
|
|
|
132
131
|
// conversation is gone AND the monotonic inbox cursor has already advanced past
|
|
133
132
|
// everything drained pre-yield, so the revived node loses sight of reports its
|
|
134
133
|
// subscriptions pushed BEFORE the yield. The bodies are never lost — they persist
|
|
135
|
-
// at reports/<ts>-<kind>.md forever — but
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
/** Collapse the home-dir prefix of an absolute path to a leading `~`, so report
|
|
141
|
-
* paths render as `~/.crouter/canvas/nodes/<id>/reports/<ts>.md` instead of
|
|
142
|
-
* repeating the long absolute home prefix on every catch-up line — cheaper
|
|
143
|
-
* context, still dereferenceable by the revived node. A path NOT under the home
|
|
144
|
-
* dir (e.g. a CRTR_HOME / project-scope home elsewhere) is returned unchanged,
|
|
145
|
-
* so we never mangle a non-home path. */
|
|
146
|
-
function tildify(p) {
|
|
147
|
-
const home = homedir();
|
|
148
|
-
return p.startsWith(home) ? '~' + p.slice(home.length) : p;
|
|
134
|
+
// at reports/<ts>-<kind>.md forever — but a fresh revive needs a bounded index
|
|
135
|
+
// that says history exists and how to expand it, not every path preloaded.
|
|
136
|
+
const REPORT_HISTORY_SOURCE_CAP = 8;
|
|
137
|
+
function reportKind(filename) {
|
|
138
|
+
return filename.match(/^[0-9]{8}T[0-9]{6}-(.+)\.md$/)?.[1] ?? 'report';
|
|
149
139
|
}
|
|
150
|
-
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const lines = [];
|
|
157
|
-
let total = 0;
|
|
140
|
+
/** Condensed report-history index for each ACTIVE subscription of `nodeId` —
|
|
141
|
+
* independent of publisher liveness, so a finished worker's history still
|
|
142
|
+
* surfaces for catch-up. Each source carries only its count and newest report
|
|
143
|
+
* ref; older refs stay one command away via `canvas history show`. */
|
|
144
|
+
function reportHistorySources(nodeId) {
|
|
145
|
+
const out = [];
|
|
158
146
|
for (const sub of subscriptionsOf(nodeId).filter((s) => s.active)) {
|
|
159
|
-
if (total >= REPORT_HISTORY_TOTAL_CAP)
|
|
160
|
-
break;
|
|
161
147
|
const dir = reportsDir(sub.node_id);
|
|
162
148
|
if (!existsSync(dir))
|
|
163
149
|
continue;
|
|
164
150
|
const files = readdirSync(dir)
|
|
165
151
|
.filter((f) => f.endsWith('.md'))
|
|
166
152
|
.sort() // YYYYMMDDTHHmmss-<kind>.md — lexical sort = chronological
|
|
167
|
-
.reverse() // most recent first
|
|
168
|
-
.slice(0, REPORT_HISTORY_PER_SOURCE);
|
|
153
|
+
.reverse(); // most recent first
|
|
169
154
|
if (files.length === 0)
|
|
170
155
|
continue;
|
|
171
156
|
const pub = getNode(sub.node_id);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
157
|
+
out.push({
|
|
158
|
+
nodeId: sub.node_id,
|
|
159
|
+
name: pub?.name ?? sub.node_id,
|
|
160
|
+
count: files.length,
|
|
161
|
+
latest: files[0],
|
|
162
|
+
});
|
|
179
163
|
}
|
|
180
|
-
return
|
|
164
|
+
return out.sort((a, b) => b.latest.localeCompare(a.latest));
|
|
181
165
|
}
|
|
182
166
|
/** Render the <feed> block PURELY: the live "awaiting" roster (a read), the
|
|
183
|
-
* already-drained unread digest (from drainBearings), and
|
|
184
|
-
*
|
|
167
|
+
* already-drained unread digest (from drainBearings), and a progressive index
|
|
168
|
+
* to on-disk report history. No cursor write. */
|
|
185
169
|
function feedBlock(nodeId, unreadDigest) {
|
|
186
170
|
// Awaiting = active subscriptions whose publisher is still live (active|idle).
|
|
187
171
|
const awaiting = subscriptionsOf(nodeId)
|
|
@@ -210,14 +194,16 @@ function feedBlock(nodeId, unreadDigest) {
|
|
|
210
194
|
}
|
|
211
195
|
// Catch-up history. The unread digest above only covers what arrived since the
|
|
212
196
|
// cursor; on a refresh-revive the cursor has already passed everything drained
|
|
213
|
-
// pre-yield, so
|
|
214
|
-
|
|
215
|
-
const history = reportHistoryLines(nodeId);
|
|
197
|
+
// pre-yield, so preserve the breadcrumb without preloading every old report.
|
|
198
|
+
const history = reportHistorySources(nodeId);
|
|
216
199
|
if (history.length > 0) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
200
|
+
const visible = history.slice(0, REPORT_HISTORY_SOURCE_CAP);
|
|
201
|
+
const hidden = history.length - visible.length;
|
|
202
|
+
const total = history.reduce((sum, h) => sum + h.count, 0);
|
|
203
|
+
lines.push('', `Report history on disk (collapsed): ${total} report${total === 1 ? '' : 's'} across ${history.length} subscribed node${history.length === 1 ? '' : 's'}. This is a catch-up index for reports your cursor already passed; read only what matters.`, ...visible.map((h) => {
|
|
204
|
+
const ref = `${h.nodeId}:reports/${h.latest}`;
|
|
205
|
+
return ` - ${h.name} (${h.nodeId}): ${h.count} report${h.count === 1 ? '' : 's'}; latest ${reportKind(h.latest)}: ${ref}`;
|
|
206
|
+
}), ...(hidden > 0 ? [` - … ${hidden} more subscribed node${hidden === 1 ? '' : 's'} with reports hidden`] : []), 'Expand: `crtr canvas history read <ref>` for one listed report; `crtr canvas history show <node-id> --type report` for that node\'s full report list; `crtr feed read --all` for cursor-independent inbox history.');
|
|
221
207
|
}
|
|
222
208
|
return `<feed>\n${lines.join('\n')}\n</feed>`;
|
|
223
209
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { NodeMeta, LaunchSpec, Mode, Lifecycle } from '../canvas/index.js';
|
|
2
|
+
import type { ModelProvider, ModelStrength } from '../../types.js';
|
|
2
3
|
export declare const CANVAS_STOPHOOK_PATH: string;
|
|
3
4
|
export declare const CANVAS_INBOX_WATCHER_PATH: string;
|
|
4
5
|
export declare const CANVAS_NAV_PATH: string;
|
|
@@ -7,32 +8,46 @@ export declare const CANVAS_GOAL_CAPTURE_PATH: string;
|
|
|
7
8
|
export declare const CANVAS_PASSIVE_CONTEXT_PATH: string;
|
|
8
9
|
export declare const CANVAS_CONTEXT_INTRO_PATH: string;
|
|
9
10
|
export declare const CANVAS_DOC_SUBSTRATE_PATH: string;
|
|
10
|
-
export declare const CANVAS_COMMANDS_PATH: string;
|
|
11
|
-
export declare const CANVAS_RESUME_PATH: string;
|
|
12
|
-
export declare const CANVAS_VIEW_PATH: string;
|
|
13
11
|
/** The canvas extensions every node loads, in order: stophook (routing +
|
|
14
12
|
* telemetry + session-id capture), inbox-watcher (wake), nav (in-editor
|
|
15
13
|
* graph chrome), recap (the per-node inactivity recap card — Haiku over the
|
|
16
14
|
* conversation, shown above the manager line after 300s of no message),
|
|
17
15
|
* goal-capture (persist the first user message as the goal),
|
|
18
16
|
* passive-context (drain passive backlog as pre-text on the next message),
|
|
19
|
-
* context-intro (inject the <crtr-
|
|
20
|
-
*
|
|
21
|
-
* doc-substrate (the unified document substrate's two hooks:
|
|
22
|
-
* <memory
|
|
23
|
-
* whole-canvas picker → `crtr node focus`), view (the /view popup → `crtr
|
|
24
|
-
* view pick` / `crtr view run <name>`).
|
|
17
|
+
* context-intro (inject the <crtr-bearings> block + the <memory kind="knowledge">
|
|
18
|
+
* catalog as its own session message, once per brand-new chat),
|
|
19
|
+
* doc-substrate (the unified document substrate's two hooks: the
|
|
20
|
+
* <memory kind="preference"> block at boot, on-read context injection).
|
|
25
21
|
* All self-gate on CRTR_NODE_ID. goal-capture precedes passive-context so it
|
|
26
22
|
* reads the raw user text. */
|
|
27
23
|
export declare const CANVAS_EXTENSIONS: string[];
|
|
28
|
-
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
/** Env var naming the DEFAULT provider for an UNqualified bare strength only.
|
|
25
|
+
* `CRTR_MODEL_PROVIDER=openai` (case-insensitive) makes a bare `strong` resolve
|
|
26
|
+
* on the OpenAI ladder; anything else defaults to Anthropic. A configured
|
|
27
|
+
* defaultProvider in config.json wins over this env. */
|
|
28
|
+
export declare const OPENAI_PROVIDER_ENV = "CRTR_MODEL_PROVIDER";
|
|
29
|
+
export interface EquivalentProviderModel {
|
|
30
|
+
fromProvider: ModelProvider;
|
|
31
|
+
toProvider: ModelProvider;
|
|
32
|
+
strength: ModelStrength;
|
|
33
|
+
model: string;
|
|
34
|
+
}
|
|
35
|
+
/** Map a concrete ladder model back to its provider×strength cell, then return
|
|
36
|
+
* the other configured provider's model at the same strength. `failedProviders`
|
|
37
|
+
* is per-turn state: once a provider has produced a retryable outage error, do
|
|
38
|
+
* not bounce back to it or keep reattempting the same failed provider. */
|
|
39
|
+
export declare function equivalentOtherProviderModel(currentModel: string, failedProviders?: ReadonlySet<ModelProvider>): EquivalentProviderModel | null;
|
|
40
|
+
/** Resolve a persona/caller `model:` token to the concrete `model:thinking`
|
|
41
|
+
* spec pi gets via `--model`, in order:
|
|
42
|
+
* 1. `provider/rest` — apply strength aliases to `rest`; if `provider` is
|
|
43
|
+
* `anthropic`/`openai` AND `rest` is a strength, return that ladder cell
|
|
44
|
+
* (the word `openai` maps to the openai-codex ladder). Otherwise it is a
|
|
45
|
+
* concrete `provider/id` (incl. any `:thinking` suffix) → pass through.
|
|
46
|
+
* 2. Bare strength (incl. aliases normal/weak) → the default-provider ladder
|
|
47
|
+
* cell (config default provider, then CRTR_MODEL_PROVIDER, else anthropic).
|
|
48
|
+
* 3. Bare family alias (opus/sonnet/haiku) → the Anthropic ladder's
|
|
49
|
+
* strong/medium/light, ALWAYS anthropic (ignores the env/config default).
|
|
50
|
+
* 4. Anything else → pass through unchanged. */
|
|
36
51
|
export declare function normalizeModel(model: string): string;
|
|
37
52
|
/** Compose a node's full pi launch recipe from its persona. The system prompt
|
|
38
53
|
* is composed from FOUR inputs: kind×mode (the persona body) plus lifecycle
|
|
@@ -49,7 +64,6 @@ export declare function buildLaunchSpec(kind: string, mode: Mode, opts: {
|
|
|
49
64
|
}): {
|
|
50
65
|
launch: LaunchSpec;
|
|
51
66
|
lifecycle: 'terminal' | 'resident';
|
|
52
|
-
skills: string[];
|
|
53
67
|
};
|
|
54
68
|
export interface PiInvocation {
|
|
55
69
|
/** argv after the `pi` binary. */
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
// (keeps its conversation), or fresh (against the context dir) for a refresh-yield.
|
|
7
7
|
// The spec is rewritten on every polymorph (base→orchestrator) so a node
|
|
8
8
|
// always comes back as its *current* self.
|
|
9
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import { dirname, join } from 'node:path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { readConfig, configPath as userConfigPath } from '../config.js';
|
|
12
13
|
import { resolve as resolvePersona } from '../personas/index.js';
|
|
13
14
|
import { nodeEnv } from './nodes.js';
|
|
14
15
|
import { editorLabel } from '../canvas/index.js';
|
|
15
16
|
import { nodeDir } from '../canvas/paths.js';
|
|
17
|
+
import { defaultScopeConfig } from '../../types.js';
|
|
16
18
|
// ---------------------------------------------------------------------------
|
|
17
19
|
// The two canvas pi-extensions every node loads. They self-gate on the live
|
|
18
20
|
// {kind,mode} env, so the worker→orchestrator polymorph flips hook behavior
|
|
@@ -34,21 +36,16 @@ export const CANVAS_GOAL_CAPTURE_PATH = resolveExtension('canvas-goal-capture');
|
|
|
34
36
|
export const CANVAS_PASSIVE_CONTEXT_PATH = resolveExtension('canvas-passive-context');
|
|
35
37
|
export const CANVAS_CONTEXT_INTRO_PATH = resolveExtension('canvas-context-intro');
|
|
36
38
|
export const CANVAS_DOC_SUBSTRATE_PATH = resolveExtension('canvas-doc-substrate');
|
|
37
|
-
export const CANVAS_COMMANDS_PATH = resolveExtension('canvas-commands');
|
|
38
|
-
export const CANVAS_RESUME_PATH = resolveExtension('canvas-resume');
|
|
39
|
-
export const CANVAS_VIEW_PATH = resolveExtension('canvas-view');
|
|
40
39
|
/** The canvas extensions every node loads, in order: stophook (routing +
|
|
41
40
|
* telemetry + session-id capture), inbox-watcher (wake), nav (in-editor
|
|
42
41
|
* graph chrome), recap (the per-node inactivity recap card — Haiku over the
|
|
43
42
|
* conversation, shown above the manager line after 300s of no message),
|
|
44
43
|
* goal-capture (persist the first user message as the goal),
|
|
45
44
|
* passive-context (drain passive backlog as pre-text on the next message),
|
|
46
|
-
* context-intro (inject the <crtr-
|
|
47
|
-
*
|
|
48
|
-
* doc-substrate (the unified document substrate's two hooks:
|
|
49
|
-
* <memory
|
|
50
|
-
* whole-canvas picker → `crtr node focus`), view (the /view popup → `crtr
|
|
51
|
-
* view pick` / `crtr view run <name>`).
|
|
45
|
+
* context-intro (inject the <crtr-bearings> block + the <memory kind="knowledge">
|
|
46
|
+
* catalog as its own session message, once per brand-new chat),
|
|
47
|
+
* doc-substrate (the unified document substrate's two hooks: the
|
|
48
|
+
* <memory kind="preference"> block at boot, on-read context injection).
|
|
52
49
|
* All self-gate on CRTR_NODE_ID. goal-capture precedes passive-context so it
|
|
53
50
|
* reads the raw user text. */
|
|
54
51
|
export const CANVAS_EXTENSIONS = [
|
|
@@ -60,37 +57,141 @@ export const CANVAS_EXTENSIONS = [
|
|
|
60
57
|
CANVAS_PASSIVE_CONTEXT_PATH,
|
|
61
58
|
CANVAS_CONTEXT_INTRO_PATH,
|
|
62
59
|
CANVAS_DOC_SUBSTRATE_PATH,
|
|
63
|
-
CANVAS_COMMANDS_PATH,
|
|
64
|
-
CANVAS_RESUME_PATH,
|
|
65
|
-
CANVAS_VIEW_PATH,
|
|
66
60
|
];
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Two-axis model resolution: (provider × strength) → concrete `model:thinking`.
|
|
63
|
+
//
|
|
64
|
+
// A persona declares ONE `model:` field valued `provider/strength` (e.g.
|
|
65
|
+
// `openai/strong`, `anthropic/light`). The provider is a fixed per-persona
|
|
66
|
+
// property; the caller/persona thinks only in strength; the concrete model id
|
|
67
|
+
// AND the thinking level both fall out of the per-provider ladder below. The
|
|
68
|
+
// ladder itself and the default provider are user-configurable in config.json;
|
|
69
|
+
// pi accepts the `:thinking` suffix on `--model` (off,minimal,low,medium,high,
|
|
70
|
+
// xhigh — confirmed via `pi --help`), so the resolved spec is exactly the
|
|
71
|
+
// string buildPiArgv passes to `--model`; no extra launch plumbing.
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/** Cached user config for model resolution. The launch path is hot and reads
|
|
74
|
+
* config synchronously, so cache the merged config and invalidate on mtime/size
|
|
75
|
+
* changes rather than rereading on every normalizeModel/buildLaunchSpec call. */
|
|
76
|
+
let cachedUserConfig = null;
|
|
77
|
+
function readUserConfigCached() {
|
|
78
|
+
const path = userConfigPath('user');
|
|
79
|
+
if (path === null)
|
|
80
|
+
return defaultScopeConfig();
|
|
81
|
+
try {
|
|
82
|
+
const stat = statSync(path);
|
|
83
|
+
if (cachedUserConfig !== null &&
|
|
84
|
+
cachedUserConfig.path === path &&
|
|
85
|
+
cachedUserConfig.mtimeMs === stat.mtimeMs &&
|
|
86
|
+
cachedUserConfig.size === stat.size) {
|
|
87
|
+
return cachedUserConfig.config;
|
|
88
|
+
}
|
|
89
|
+
const config = readConfig('user');
|
|
90
|
+
cachedUserConfig = { path, mtimeMs: stat.mtimeMs, size: stat.size, config };
|
|
91
|
+
return config;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error.code === 'ENOENT') {
|
|
95
|
+
cachedUserConfig = null;
|
|
96
|
+
return defaultScopeConfig();
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const DEFAULT_MODEL_LADDERS = defaultScopeConfig().modelLadders;
|
|
102
|
+
function modelLadders() {
|
|
103
|
+
return readUserConfigCached().modelLadders ?? DEFAULT_MODEL_LADDERS;
|
|
104
|
+
}
|
|
105
|
+
function ladderFor(provider, ladders = modelLadders()) {
|
|
106
|
+
return ladders[provider];
|
|
107
|
+
}
|
|
108
|
+
/** Env var naming the DEFAULT provider for an UNqualified bare strength only.
|
|
109
|
+
* `CRTR_MODEL_PROVIDER=openai` (case-insensitive) makes a bare `strong` resolve
|
|
110
|
+
* on the OpenAI ladder; anything else defaults to Anthropic. A configured
|
|
111
|
+
* defaultProvider in config.json wins over this env. */
|
|
112
|
+
export const OPENAI_PROVIDER_ENV = 'CRTR_MODEL_PROVIDER';
|
|
113
|
+
function defaultProvider() {
|
|
114
|
+
const cfgProvider = readUserConfigCached().modelLadders.defaultProvider;
|
|
115
|
+
if (cfgProvider === 'anthropic' || cfgProvider === 'openai')
|
|
116
|
+
return cfgProvider;
|
|
117
|
+
return (process.env[OPENAI_PROVIDER_ENV] ?? '').toLowerCase() === 'openai' ? 'openai' : 'anthropic';
|
|
118
|
+
}
|
|
119
|
+
/** Strength synonyms accepted from personas/callers. */
|
|
120
|
+
const STRENGTH_ALIASES = {
|
|
121
|
+
ultra: 'ultra',
|
|
122
|
+
strong: 'strong',
|
|
123
|
+
medium: 'medium',
|
|
124
|
+
light: 'light',
|
|
125
|
+
normal: 'medium',
|
|
126
|
+
weak: 'light',
|
|
84
127
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
128
|
+
const MODEL_PROVIDER_KEYS = ['anthropic', 'openai'];
|
|
129
|
+
const MODEL_STRENGTHS = ['ultra', 'strong', 'medium', 'light'];
|
|
130
|
+
const THINKING_SUFFIXES = new Set(['off', 'minimal', 'low', 'medium', 'high', 'xhigh']);
|
|
131
|
+
function stripThinkingSuffix(spec) {
|
|
132
|
+
const i = spec.lastIndexOf(':');
|
|
133
|
+
if (i <= 0)
|
|
134
|
+
return spec;
|
|
135
|
+
return THINKING_SUFFIXES.has(spec.slice(i + 1)) ? spec.slice(0, i) : spec;
|
|
136
|
+
}
|
|
137
|
+
/** Map a concrete ladder model back to its provider×strength cell, then return
|
|
138
|
+
* the other configured provider's model at the same strength. `failedProviders`
|
|
139
|
+
* is per-turn state: once a provider has produced a retryable outage error, do
|
|
140
|
+
* not bounce back to it or keep reattempting the same failed provider. */
|
|
141
|
+
export function equivalentOtherProviderModel(currentModel, failedProviders = new Set()) {
|
|
142
|
+
const ladders = modelLadders();
|
|
143
|
+
const currentComparable = stripThinkingSuffix(normalizeModel(currentModel));
|
|
144
|
+
for (const fromProvider of MODEL_PROVIDER_KEYS) {
|
|
145
|
+
for (const strength of MODEL_STRENGTHS) {
|
|
146
|
+
if (stripThinkingSuffix(ladders[fromProvider][strength]) !== currentComparable)
|
|
147
|
+
continue;
|
|
148
|
+
if (failedProviders.has(fromProvider))
|
|
149
|
+
return null;
|
|
150
|
+
const toProvider = fromProvider === 'anthropic' ? 'openai' : 'anthropic';
|
|
151
|
+
if (failedProviders.has(toProvider))
|
|
152
|
+
return null;
|
|
153
|
+
return { fromProvider, toProvider, strength, model: ladders[toProvider][strength] };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
/** Resolve a persona/caller `model:` token to the concrete `model:thinking`
|
|
159
|
+
* spec pi gets via `--model`, in order:
|
|
160
|
+
* 1. `provider/rest` — apply strength aliases to `rest`; if `provider` is
|
|
161
|
+
* `anthropic`/`openai` AND `rest` is a strength, return that ladder cell
|
|
162
|
+
* (the word `openai` maps to the openai-codex ladder). Otherwise it is a
|
|
163
|
+
* concrete `provider/id` (incl. any `:thinking` suffix) → pass through.
|
|
164
|
+
* 2. Bare strength (incl. aliases normal/weak) → the default-provider ladder
|
|
165
|
+
* cell (config default provider, then CRTR_MODEL_PROVIDER, else anthropic).
|
|
166
|
+
* 3. Bare family alias (opus/sonnet/haiku) → the Anthropic ladder's
|
|
167
|
+
* strong/medium/light, ALWAYS anthropic (ignores the env/config default).
|
|
168
|
+
* 4. Anything else → pass through unchanged. */
|
|
89
169
|
export function normalizeModel(model) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (model
|
|
93
|
-
|
|
170
|
+
const ladders = modelLadders();
|
|
171
|
+
// 1. Qualified provider/rest.
|
|
172
|
+
if (model.includes('/')) {
|
|
173
|
+
const slash = model.indexOf('/');
|
|
174
|
+
const provider = model.slice(0, slash);
|
|
175
|
+
const rest = model.slice(slash + 1);
|
|
176
|
+
const strength = STRENGTH_ALIASES[rest];
|
|
177
|
+
if (strength !== undefined && (provider === 'anthropic' || provider === 'openai')) {
|
|
178
|
+
return ladderFor(provider, ladders)[strength];
|
|
179
|
+
}
|
|
180
|
+
// Concrete `provider/id` (optionally `...:thinking`) — pass through.
|
|
181
|
+
return model;
|
|
182
|
+
}
|
|
183
|
+
// 2. Bare strength → default-provider ladder.
|
|
184
|
+
const strength = STRENGTH_ALIASES[model];
|
|
185
|
+
if (strength !== undefined)
|
|
186
|
+
return ladderFor(defaultProvider(), ladders)[strength];
|
|
187
|
+
// 3. Bare family alias → always Anthropic.
|
|
188
|
+
if (model === 'opus')
|
|
189
|
+
return ladders.anthropic.strong;
|
|
190
|
+
if (model === 'sonnet')
|
|
191
|
+
return ladders.anthropic.medium;
|
|
192
|
+
if (model === 'haiku')
|
|
193
|
+
return ladders.anthropic.light;
|
|
194
|
+
// 4. Unknown — pass through.
|
|
94
195
|
return model;
|
|
95
196
|
}
|
|
96
197
|
// ---------------------------------------------------------------------------
|
|
@@ -105,10 +206,14 @@ export function normalizeModel(model) {
|
|
|
105
206
|
* declared extensions follow. */
|
|
106
207
|
export function buildLaunchSpec(kind, mode, opts) {
|
|
107
208
|
const p = resolvePersona(kind, mode, { lifecycle: opts.lifecycle, hasManager: opts.hasManager });
|
|
108
|
-
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
209
|
+
const cfg = readUserConfigCached();
|
|
210
|
+
// Precedence, from strongest to weakest:
|
|
211
|
+
// 1. Caller-supplied override (durable on `meta.model_override`, re-passed on
|
|
212
|
+
// every polymorph).
|
|
213
|
+
// 2. Configured persona strength for this kind.
|
|
214
|
+
// 3. The persona file's own frontmatter model.
|
|
215
|
+
// 4. Unset → pi default.
|
|
216
|
+
const chosenModel = opts.model ?? cfg.personaStrengths[kind] ?? p.model;
|
|
112
217
|
const launch = {
|
|
113
218
|
model: chosenModel !== undefined ? normalizeModel(chosenModel) : undefined,
|
|
114
219
|
tools: p.tools,
|
|
@@ -116,7 +221,7 @@ export function buildLaunchSpec(kind, mode, opts) {
|
|
|
116
221
|
systemPrompt: p.systemPrompt,
|
|
117
222
|
env: { ...(opts.extraEnv ?? {}) },
|
|
118
223
|
};
|
|
119
|
-
return { launch, lifecycle: p.lifecycle
|
|
224
|
+
return { launch, lifecycle: p.lifecycle };
|
|
120
225
|
}
|
|
121
226
|
// ---------------------------------------------------------------------------
|
|
122
227
|
// Build the pi argv to launch / revive a node
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type NodeMeta } from '../canvas/index.js';
|
|
2
|
+
/** True when the node's broker engine is reachable: its recorded pid is alive
|
|
3
|
+
* AND its view.sock exists. Both are required — a dead pid with a stale socket,
|
|
4
|
+
* or a live pid mid-boot before it binds the socket, is NOT a live broker. */
|
|
5
|
+
export declare function isBrokerLive(meta: NodeMeta): boolean;
|
|
6
|
+
/** Swap a LIVE node's model over its view.sock. Connects, claims controller
|
|
7
|
+
* (hello + request_control, so an attached viewer never blocks the swap), sends
|
|
8
|
+
* `set_model`, and resolves with the broker-resolved `provider/id` on its ack —
|
|
9
|
+
* read back from the row the broker just persisted (model_changed.model carries
|
|
10
|
+
* only the bare id). Rejects with the broker's message on an `error` frame (e.g.
|
|
11
|
+
* `no model matching '<spec>' in the registry`), on connect failure, or timeout. */
|
|
12
|
+
export declare function setModelLive(nodeId: string, spec: string): Promise<string>;
|
|
13
|
+
/** Persist a model choice into a DORMANT node's durable recipe — mirrors exactly
|
|
14
|
+
* what the broker's persistModelChoice writes on a live switch: `model_override`
|
|
15
|
+
* (so polymorphs preserve it via buildLaunchSpec) and `launch.model` (the recipe
|
|
16
|
+
* buildPiArgv replays on revive). `spec` must already be a concrete `provider/id`
|
|
17
|
+
* (normalizeModel maps tiers/aliases; a bare substring cannot resolve offline). */
|
|
18
|
+
export declare function persistDormantModel(nodeId: string, spec: string): void;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// model-swap.ts — the engine behind `crtr node model`: swap a node's model
|
|
2
|
+
// (and thus its provider) WITHOUT attaching a viewer, persisting the choice so
|
|
3
|
+
// it survives revives. Two paths, chosen on broker liveness:
|
|
4
|
+
//
|
|
5
|
+
// LIVE — the node's broker is up (recorded pi_pid alive AND view.sock
|
|
6
|
+
// present): connect to view.sock, claim controller, send `set_model`,
|
|
7
|
+
// await the ack. The broker's broadcastModelChanged already persists
|
|
8
|
+
// the choice via persistModelChoice (model_override + launch.model), so
|
|
9
|
+
// this path NEVER writes the recipe itself — it reads back the resolved
|
|
10
|
+
// provider/id from the freshly-updated row.
|
|
11
|
+
// DORMANT — no live broker: write the durable recipe directly (mirroring exactly
|
|
12
|
+
// what persistModelChoice writes) so the next revive picks it up.
|
|
13
|
+
//
|
|
14
|
+
// This complements the in-viewer `/model` slash command for the headless case.
|
|
15
|
+
import { existsSync } from 'node:fs';
|
|
16
|
+
import { randomUUID } from 'node:crypto';
|
|
17
|
+
import { getNode, updateNode } from '../canvas/index.js';
|
|
18
|
+
import { viewSocketPath } from '../canvas/paths.js';
|
|
19
|
+
import { isPidAlive } from '../canvas/pid.js';
|
|
20
|
+
import { ViewSocketClient } from './view-socket-client.js';
|
|
21
|
+
/** How long to wait for the broker's `set_model` ack before giving up. The
|
|
22
|
+
* switch is local (engine method + a DB write), so this is generous slack. */
|
|
23
|
+
const ACK_TIMEOUT_MS = 15_000;
|
|
24
|
+
/** True when the node's broker engine is reachable: its recorded pid is alive
|
|
25
|
+
* AND its view.sock exists. Both are required — a dead pid with a stale socket,
|
|
26
|
+
* or a live pid mid-boot before it binds the socket, is NOT a live broker. */
|
|
27
|
+
export function isBrokerLive(meta) {
|
|
28
|
+
return isPidAlive(meta.pi_pid) && existsSync(viewSocketPath(meta.node_id));
|
|
29
|
+
}
|
|
30
|
+
/** Swap a LIVE node's model over its view.sock. Connects, claims controller
|
|
31
|
+
* (hello + request_control, so an attached viewer never blocks the swap), sends
|
|
32
|
+
* `set_model`, and resolves with the broker-resolved `provider/id` on its ack —
|
|
33
|
+
* read back from the row the broker just persisted (model_changed.model carries
|
|
34
|
+
* only the bare id). Rejects with the broker's message on an `error` frame (e.g.
|
|
35
|
+
* `no model matching '<spec>' in the registry`), on connect failure, or timeout. */
|
|
36
|
+
export function setModelLive(nodeId, spec) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
const client = new ViewSocketClient(nodeId);
|
|
39
|
+
const clientId = randomUUID();
|
|
40
|
+
let settled = false;
|
|
41
|
+
let resolvedId;
|
|
42
|
+
const timer = setTimeout(() => finish(() => reject(new Error(`timed out after ${ACK_TIMEOUT_MS}ms waiting for the broker to ack the model switch`))), ACK_TIMEOUT_MS);
|
|
43
|
+
if (typeof timer.unref === 'function')
|
|
44
|
+
timer.unref();
|
|
45
|
+
function finish(act) {
|
|
46
|
+
if (settled)
|
|
47
|
+
return;
|
|
48
|
+
settled = true;
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
try {
|
|
51
|
+
client.close();
|
|
52
|
+
}
|
|
53
|
+
catch { /* best-effort */ }
|
|
54
|
+
act();
|
|
55
|
+
}
|
|
56
|
+
client.on('error', (err) => finish(() => reject(err)));
|
|
57
|
+
client.on('close', () => finish(() => reject(new Error('broker connection closed before it acked the model switch'))));
|
|
58
|
+
client.on('connect', () => {
|
|
59
|
+
// Claim control unconditionally: hello admits us as controller only if none
|
|
60
|
+
// is held; request_control preempts an attached viewer (idempotent when we
|
|
61
|
+
// already hold it). The broker processes these in order before set_model.
|
|
62
|
+
client.send({ type: 'hello', role: 'controller', client_id: clientId });
|
|
63
|
+
client.send({ type: 'request_control' });
|
|
64
|
+
client.send({ type: 'set_model', model: spec });
|
|
65
|
+
});
|
|
66
|
+
client.on('frame', (frame) => {
|
|
67
|
+
if (frame.type === 'model_changed') {
|
|
68
|
+
resolvedId = frame.model;
|
|
69
|
+
}
|
|
70
|
+
else if (frame.type === 'ack' && frame.for === 'set_model' && frame.ok) {
|
|
71
|
+
// The broker persisted model_override (full provider/id) in
|
|
72
|
+
// broadcastModelChanged BEFORE flushing these frames, so the row is
|
|
73
|
+
// authoritative; fall back to the bare id, then the raw spec.
|
|
74
|
+
const persisted = getNode(nodeId)?.model_override ?? undefined;
|
|
75
|
+
finish(() => resolve(persisted ?? resolvedId ?? spec));
|
|
76
|
+
}
|
|
77
|
+
else if (frame.type === 'error') {
|
|
78
|
+
finish(() => reject(new Error(frame.message)));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
client.connect();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/** Persist a model choice into a DORMANT node's durable recipe — mirrors exactly
|
|
85
|
+
* what the broker's persistModelChoice writes on a live switch: `model_override`
|
|
86
|
+
* (so polymorphs preserve it via buildLaunchSpec) and `launch.model` (the recipe
|
|
87
|
+
* buildPiArgv replays on revive). `spec` must already be a concrete `provider/id`
|
|
88
|
+
* (normalizeModel maps tiers/aliases; a bare substring cannot resolve offline). */
|
|
89
|
+
export function persistDormantModel(nodeId, spec) {
|
|
90
|
+
const meta = getNode(nodeId);
|
|
91
|
+
if (meta === null)
|
|
92
|
+
return;
|
|
93
|
+
const launch = meta.launch !== undefined ? { ...meta.launch, model: spec } : undefined;
|
|
94
|
+
updateNode(nodeId, { model_override: spec, ...(launch !== undefined ? { launch } : {}) });
|
|
95
|
+
}
|
|
@@ -16,3 +16,10 @@ export declare function slugFromPrompt(prompt: string): string;
|
|
|
16
16
|
* pi.setSessionName(editorLabel(meta)) so the LIVE editor label updates in the
|
|
17
17
|
* same session, instead of waiting for the next revive/cycle. */
|
|
18
18
|
export declare function generateAndPersistName(nodeId: string, prompt: string, onNamed?: (meta: NodeMeta) => void): void;
|
|
19
|
+
/** A filesystem-safe slug for the `.todos/<slug>.md` fallback path: lowercase,
|
|
20
|
+
* [a-z0-9] kept, everything else → single hyphen, clamped to 8 words. */
|
|
21
|
+
export declare function titleSlug(title: string): string;
|
|
22
|
+
/** Ask pi headlessly for a one-line issue title for `note`. Resolves to the
|
|
23
|
+
* generated title, or a local first-line fallback on any failure (non-zero
|
|
24
|
+
* exit, timeout, empty output). Async; never throws. */
|
|
25
|
+
export declare function generateIssueTitle(note: string): Promise<string>;
|