@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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { defineLeaf } from '../../core/command.js';
|
|
5
|
+
import { InputError } from '../../core/io.js';
|
|
6
|
+
import { getNode, jobDir } from '../../core/canvas/index.js';
|
|
7
|
+
import { focusOf, openNodeWindow } from '../../core/runtime/placement.js';
|
|
8
|
+
function shellQuote(s) {
|
|
9
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
10
|
+
}
|
|
11
|
+
export const sysSyspromptLeaf = defineLeaf({
|
|
12
|
+
name: 'sysprompt',
|
|
13
|
+
description: 'print a node\'s assembled system prompt',
|
|
14
|
+
whenToUse: 'you want to inspect a node\'s assembled system prompt headlessly, without paying conversation/context cost — the broker writes it to disk while live, and this command reads that artifact back as markdown.',
|
|
15
|
+
help: {
|
|
16
|
+
name: 'sys sysprompt',
|
|
17
|
+
summary: 'print a node\'s assembled system prompt',
|
|
18
|
+
params: [
|
|
19
|
+
{ kind: 'positional', name: 'node', required: false, constraint: 'Node id. Defaults to the calling node (CRTR_NODE_ID).' },
|
|
20
|
+
{ kind: 'flag', name: 'window', type: 'bool', required: false, constraint: 'Open the assembled prompt in a NEW tmux window in the session currently showing that node instead of printing to stdout.' },
|
|
21
|
+
],
|
|
22
|
+
output: [
|
|
23
|
+
{ name: 'node_id', type: 'string', required: true, constraint: 'The node whose prompt was read.' },
|
|
24
|
+
{ name: 'captured_at', type: 'string', required: true, constraint: 'File mtime in ISO-8601 UTC.' },
|
|
25
|
+
{ name: 'chars', type: 'number', required: true, constraint: 'Character count of the stored prompt.' },
|
|
26
|
+
{ name: 'prompt', type: 'string', required: true, constraint: 'The assembled system prompt, exactly as pi built it.' },
|
|
27
|
+
{ name: 'session', type: 'string', required: false, constraint: 'tmux session showing the node (only when --window is used).' },
|
|
28
|
+
{ name: 'window', type: 'string', required: false, constraint: 'tmux window opened for the prompt (only when --window is used).' },
|
|
29
|
+
],
|
|
30
|
+
outputKind: 'object',
|
|
31
|
+
effects: ['Read-only: reads the node\'s job/system-prompt.md artifact, or opens it in a tmux window.'],
|
|
32
|
+
},
|
|
33
|
+
run: async (input) => {
|
|
34
|
+
const nodeId = input['node']?.trim() || process.env['CRTR_NODE_ID'];
|
|
35
|
+
if (nodeId === undefined || nodeId === '') {
|
|
36
|
+
throw new InputError({ error: 'empty_node', message: 'a node id is required', field: 'node', next: 'Pass a node id or run from inside a node.' });
|
|
37
|
+
}
|
|
38
|
+
if (getNode(nodeId) === null) {
|
|
39
|
+
throw new InputError({ error: 'not_found', message: `no node: ${nodeId}`, field: 'node', next: 'List nodes with `crtr node inspect list`.' });
|
|
40
|
+
}
|
|
41
|
+
const path = join(jobDir(nodeId), 'system-prompt.md');
|
|
42
|
+
if (!existsSync(path)) {
|
|
43
|
+
throw new InputError({
|
|
44
|
+
error: 'not_found',
|
|
45
|
+
message: `system prompt not captured yet for ${nodeId}`,
|
|
46
|
+
field: 'node',
|
|
47
|
+
next: 'Focus or revive the node once while its broker is live, then rerun `crtr sys sysprompt <node>`.',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const prompt = readFileSync(path, 'utf8');
|
|
51
|
+
const capturedAt = statSync(path).mtime.toISOString();
|
|
52
|
+
if (input['window'] === true) {
|
|
53
|
+
const focus = focusOf(nodeId);
|
|
54
|
+
if (focus === null || focus.session === null || focus.session === '') {
|
|
55
|
+
throw new InputError({
|
|
56
|
+
error: 'not_focused',
|
|
57
|
+
message: `node ${nodeId} is not currently shown in tmux`,
|
|
58
|
+
field: 'node',
|
|
59
|
+
next: `Focus the node first with \`crtr node focus ${nodeId}\`, then rerun \`crtr sys sysprompt --window ${nodeId}\`.`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const tmpPath = join(tmpdir(), `crtr-sysprompt-${nodeId}.md`);
|
|
63
|
+
writeFileSync(tmpPath, prompt, 'utf8');
|
|
64
|
+
const q = shellQuote(tmpPath);
|
|
65
|
+
// Syntax-highlight the prompt (markdown + its structural XML tags both stay visible).
|
|
66
|
+
// bat pages by default; markdown RENDERERS (termrender, glow) are wrong here — they
|
|
67
|
+
// silently swallow block-level XML tags, dropping most of the assembled prompt.
|
|
68
|
+
// Fall back to `less -R` when bat is not installed.
|
|
69
|
+
const command = `if command -v bat >/dev/null 2>&1; then bat --language=markdown --color=always --paging=always --style=plain ${q}; else less -R ${q}; fi`;
|
|
70
|
+
const opened = openNodeWindow({ session: focus.session, name: `sysprompt:${nodeId}`, cwd: process.cwd(), env: {}, command });
|
|
71
|
+
if (opened === null) {
|
|
72
|
+
throw new InputError({
|
|
73
|
+
error: 'window_open_failed',
|
|
74
|
+
message: `tmux could not open a window for node ${nodeId}`,
|
|
75
|
+
field: 'node',
|
|
76
|
+
next: 'Check the tmux server is reachable, then retry.',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return { node_id: nodeId, session: focus.session, window: opened.window };
|
|
80
|
+
}
|
|
81
|
+
return { node_id: nodeId, captured_at: capturedAt, chars: prompt.length, prompt };
|
|
82
|
+
},
|
|
83
|
+
render: (r) => ('window' in r)
|
|
84
|
+
? `Opened system prompt for node ${r['node_id']} in tmux session ${r['session']} — window ${r['window']}.`
|
|
85
|
+
: `- node_id: ${r['node_id']}\n- captured_at: ${r['captured_at']}\n- chars: ${r['chars']}\n\n${r['prompt']}`,
|
|
86
|
+
});
|
package/dist/commands/sys.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
// `crtr sys` subtree: config {get,set,path}, doctor, update, version.
|
|
1
|
+
// `crtr sys` subtree: config {get,set,path}, doctor, sync, update, version.
|
|
2
2
|
// Replaces old config.ts + doctor.ts + update.ts command files.
|
|
3
3
|
import { defineBranch } from '../core/command.js';
|
|
4
4
|
import { configBranch } from './sys/config.js';
|
|
5
5
|
import { sysDoctorLeaf } from './sys/doctor.js';
|
|
6
6
|
import { sysFeedbackLeaf } from './sys/feedback.js';
|
|
7
|
+
import { sysSettingsLeaf } from './sys/settings.js';
|
|
8
|
+
import { sysSyspromptLeaf } from './sys/sysprompt.js';
|
|
9
|
+
import { promptReviewLeaf } from './sys/prompt-review.js';
|
|
10
|
+
import { sysPromptStudioLeaf } from './sys/promptstudio.js';
|
|
7
11
|
import { sysSyncLeaf } from './sys/sync.js';
|
|
8
12
|
import { sysUpdateLeaf, sysVersionLeaf } from './sys/update.js';
|
|
9
13
|
export function registerSys() {
|
|
@@ -11,13 +15,13 @@ export function registerSys() {
|
|
|
11
15
|
name: 'sys',
|
|
12
16
|
rootEntry: {
|
|
13
17
|
concept: 'crtr configuration, diagnostics, and self-management',
|
|
14
|
-
desc: 'config, doctor, sync, update, version, feedback',
|
|
15
|
-
useWhen: 'managing the crtr installation',
|
|
18
|
+
desc: 'config, settings, doctor, sysprompt, prompt-review, promptstudio, sync, update, version, feedback',
|
|
19
|
+
useWhen: 'managing the crtr installation or opening built-in system views',
|
|
16
20
|
},
|
|
17
21
|
help: {
|
|
18
22
|
name: 'sys',
|
|
19
|
-
summary: 'crtr system configuration, diagnostics, and self-management',
|
|
23
|
+
summary: 'crtr system configuration, settings, diagnostics, and self-management',
|
|
20
24
|
},
|
|
21
|
-
children: [configBranch, sysDoctorLeaf,
|
|
25
|
+
children: [configBranch, sysSettingsLeaf, sysDoctorLeaf, sysSyspromptLeaf, promptReviewLeaf, sysPromptStudioLeaf, sysFeedbackLeaf, sysUpdateLeaf, sysVersionLeaf, sysSyncLeaf],
|
|
22
26
|
});
|
|
23
27
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Writes the four contract files (core.mjs + tui.mjs + web.jsx + text.mjs) under
|
|
4
4
|
// <viewsDir(scope)>/<name>/ from the stubs in prompts/view.ts (mkdir -p first).
|
|
5
5
|
// Refuses if a view of that name already resolves. The scaffold runs as-is via
|
|
6
|
-
// `crtr view run <name>` (TUI) and `crtr view serve <name>` (web). Mirrors
|
|
7
|
-
//
|
|
6
|
+
// `crtr view run <name>` (TUI) and `crtr view serve <name>` (web). Mirrors the
|
|
7
|
+
// scope → ensure dir → write → point-at-run shape of other authoring commands.
|
|
8
8
|
import { mkdirSync, existsSync } from 'node:fs';
|
|
9
9
|
import { writeFileSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { test, before, beforeEach, after } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { existsSync, mkdtempSync, mkdirSync, rmSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { interactionsRoot, migrateLegacyWorkspaceDirs, workspaceRoot } from '../artifact.js';
|
|
7
|
+
let home;
|
|
8
|
+
let origHome;
|
|
9
|
+
before(() => {
|
|
10
|
+
home = mkdtempSync(join(tmpdir(), 'crtr-workspaces-'));
|
|
11
|
+
origHome = process.env['HOME'];
|
|
12
|
+
process.env['HOME'] = home;
|
|
13
|
+
});
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
rmSync(home, { recursive: true, force: true });
|
|
16
|
+
mkdirSync(join(home, '.crouter'), { recursive: true });
|
|
17
|
+
});
|
|
18
|
+
after(() => {
|
|
19
|
+
rmSync(home, { recursive: true, force: true });
|
|
20
|
+
if (origHome === undefined)
|
|
21
|
+
delete process.env['HOME'];
|
|
22
|
+
else
|
|
23
|
+
process.env['HOME'] = origHome;
|
|
24
|
+
});
|
|
25
|
+
test('workspace roots live under ~/.crouter/workspaces/<mangled-cwd>', () => {
|
|
26
|
+
const cwd = '/Users/silasrhyneer/Code/cli/crouter';
|
|
27
|
+
const root = join(home, '.crouter', 'workspaces', '-Users-silasrhyneer-Code-cli-crouter');
|
|
28
|
+
assert.equal(workspaceRoot(cwd), root);
|
|
29
|
+
assert.equal(interactionsRoot(cwd), join(root, 'interactions'));
|
|
30
|
+
});
|
|
31
|
+
test('migrateLegacyWorkspaceDirs moves only legacy mangled cwd dirs', () => {
|
|
32
|
+
const legacy = join(home, '.crouter', '-Users-silasrhyneer-Code-cli-crouter');
|
|
33
|
+
const workspace = join(home, '.crouter', 'workspaces', '-Users-silasrhyneer-Code-cli-crouter');
|
|
34
|
+
const memory = join(home, '.crouter', 'memory');
|
|
35
|
+
const canvas = join(home, '.crouter', 'canvas');
|
|
36
|
+
mkdirSync(legacy, { recursive: true });
|
|
37
|
+
mkdirSync(memory, { recursive: true });
|
|
38
|
+
mkdirSync(canvas, { recursive: true });
|
|
39
|
+
migrateLegacyWorkspaceDirs();
|
|
40
|
+
assert.ok(!existsSync(legacy));
|
|
41
|
+
assert.ok(existsSync(workspace));
|
|
42
|
+
assert.ok(existsSync(memory));
|
|
43
|
+
assert.ok(existsSync(canvas));
|
|
44
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Run with: node --import tsx/esm --test src/core/__tests__/broker-preflight.test.ts
|
|
2
|
+
//
|
|
3
|
+
// Bug-regression: preflight broker launch validation should resolve the default
|
|
4
|
+
// broker package, reject broken custom engines, and fail before a detached broker
|
|
5
|
+
// process is spawned when session args are invalid or missing.
|
|
6
|
+
import { test, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import { mkdtempSync, rmSync, readFileSync } from 'node:fs';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { join, dirname } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { preflightBrokerLaunch } from '../runtime/host.js';
|
|
13
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const FAKE_ENGINE = join(HERE, 'fixtures', 'fake-engine.ts');
|
|
15
|
+
let home;
|
|
16
|
+
before(() => {
|
|
17
|
+
home = mkdtempSync(join(tmpdir(), 'crtr-preflight-'));
|
|
18
|
+
process.env['CRTR_HOME'] = home;
|
|
19
|
+
});
|
|
20
|
+
after(() => {
|
|
21
|
+
rmSync(home, { recursive: true, force: true });
|
|
22
|
+
delete process.env['CRTR_HOME'];
|
|
23
|
+
});
|
|
24
|
+
function assertLaunchWrote(nodeId, inv) {
|
|
25
|
+
const launchPath = join(home, 'nodes', nodeId, 'broker-launch.json');
|
|
26
|
+
const parsed = JSON.parse(readFileSync(launchPath, 'utf8'));
|
|
27
|
+
assert.deepEqual(parsed, inv, 'the launch recipe exists and parses as JSON');
|
|
28
|
+
}
|
|
29
|
+
test('preflightBrokerLaunch uses the installed default broker package when no engine override is set', () => {
|
|
30
|
+
const nodeId = 'preflight-default-engine';
|
|
31
|
+
const inv = {
|
|
32
|
+
argv: ['hello'],
|
|
33
|
+
env: {},
|
|
34
|
+
};
|
|
35
|
+
const prev = process.env['CRTR_BROKER_ENGINE'];
|
|
36
|
+
delete process.env['CRTR_BROKER_ENGINE'];
|
|
37
|
+
try {
|
|
38
|
+
assert.doesNotThrow(() => preflightBrokerLaunch(nodeId, inv, '/tmp/work'));
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
if (prev === undefined)
|
|
42
|
+
delete process.env['CRTR_BROKER_ENGINE'];
|
|
43
|
+
else
|
|
44
|
+
process.env['CRTR_BROKER_ENGINE'] = prev;
|
|
45
|
+
}
|
|
46
|
+
assertLaunchWrote(nodeId, inv);
|
|
47
|
+
});
|
|
48
|
+
test('preflightBrokerLaunch fails fast when the broker engine module cannot be resolved', () => {
|
|
49
|
+
const nodeId = 'preflight-bad-engine';
|
|
50
|
+
const inv = {
|
|
51
|
+
argv: ['hello'],
|
|
52
|
+
env: { CRTR_BROKER_ENGINE: '@crouton-kit/does-not-exist' },
|
|
53
|
+
};
|
|
54
|
+
assert.throws(() => preflightBrokerLaunch(nodeId, inv, '/tmp/work'), /cannot resolve broker engine/i);
|
|
55
|
+
assertLaunchWrote(nodeId, inv);
|
|
56
|
+
});
|
|
57
|
+
test('preflightBrokerLaunch validates the exact broker engine env passed to the detached broker', () => {
|
|
58
|
+
const nodeId = 'preflight-empty-engine';
|
|
59
|
+
const inv = {
|
|
60
|
+
argv: ['hello'],
|
|
61
|
+
env: { CRTR_BROKER_ENGINE: ' ' },
|
|
62
|
+
};
|
|
63
|
+
assert.throws(() => preflightBrokerLaunch(nodeId, inv, '/tmp/work'), /cannot resolve broker engine/i);
|
|
64
|
+
assertLaunchWrote(nodeId, inv);
|
|
65
|
+
});
|
|
66
|
+
test('preflightBrokerLaunch fails fast when a bare --session id would be rejected by the broker', () => {
|
|
67
|
+
const nodeId = 'preflight-bare-session';
|
|
68
|
+
const inv = {
|
|
69
|
+
argv: ['--session', 'uuid-123'],
|
|
70
|
+
env: { CRTR_BROKER_ENGINE: FAKE_ENGINE },
|
|
71
|
+
};
|
|
72
|
+
assert.throws(() => preflightBrokerLaunch(nodeId, inv, '/tmp/work'), /bare --session id/i);
|
|
73
|
+
assertLaunchWrote(nodeId, inv);
|
|
74
|
+
});
|
|
75
|
+
test('preflightBrokerLaunch fails fast when a plainly-encoded session file path is missing', () => {
|
|
76
|
+
const nodeId = 'preflight-missing-session';
|
|
77
|
+
const inv = {
|
|
78
|
+
argv: ['--session', join(home, 'sessions', 'missing.jsonl')],
|
|
79
|
+
env: { CRTR_BROKER_ENGINE: FAKE_ENGINE },
|
|
80
|
+
};
|
|
81
|
+
assert.throws(() => preflightBrokerLaunch(nodeId, inv, '/tmp/work'), /missing session file/i);
|
|
82
|
+
const launchPath = join(home, 'nodes', nodeId, 'broker-launch.json');
|
|
83
|
+
const parsed = JSON.parse(readFileSync(launchPath, 'utf8'));
|
|
84
|
+
assert.deepEqual(parsed, inv, 'the launch recipe exists and parses as JSON');
|
|
85
|
+
});
|
|
@@ -71,6 +71,21 @@ test('C3 — services path registers an extension model provider; the broker ses
|
|
|
71
71
|
rmSync(cwd, { recursive: true, force: true });
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
|
+
test('C3b — broker splits model thinking suffix before SDK registry lookup', async () => {
|
|
75
|
+
const cwd = mkdtempSync(join(tmpdir(), 'crtr-c3b-'));
|
|
76
|
+
try {
|
|
77
|
+
const { session } = await buildBrokerSession(realEngine, cfg(cwd, { extensionPaths: [C3_EXT], model: 'c3prov/c3model:high' }));
|
|
78
|
+
try {
|
|
79
|
+
assert.equal(session.model?.id, 'c3model', 'C3b: suffixed model specs resolve by model id instead of falling back to SDK default');
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
session.dispose();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
rmSync(cwd, { recursive: true, force: true });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
74
89
|
// ===========================================================================
|
|
75
90
|
// C4 — project context (AGENTS.md) is injected into the system prompt. The
|
|
76
91
|
// services path loads project context files; on the 0.78.1 pin there is no
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test, before, after, beforeEach } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { mkdtempSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { mkdtempSync, rmSync, existsSync, readFileSync, writeFileSync, statSync } from 'node:fs';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { createNode, getNode, getRow, updateNode, setStatus, listNodes, subscribe, unsubscribe, setSubscriptionActive, recordSpawn, subscribersOf, subscriptionsOf, view, hasActiveLiveSubscription, rebuildIndex, } from '../canvas/canvas.js';
|
|
@@ -139,6 +139,42 @@ test('recordSpawn writes the audit-only spawned_by edge', () => {
|
|
|
139
139
|
assert.equal(subscriptionsOf('child').length, 0);
|
|
140
140
|
assert.equal(subscribersOf('parent').length, 0);
|
|
141
141
|
});
|
|
142
|
+
// --- Regression: Hearth M0 cold-boot 0-byte meta.json -----------------------
|
|
143
|
+
// A bare write-temp-then-rename WITHOUT fsync left meta.json 0-byte on the guest's
|
|
144
|
+
// durable NFS home after a sandbox recreate (the open inode is silly-renamed and the
|
|
145
|
+
// new content never flushed). Cold-boot's `canvas rebuild-index` + `canvas revive`
|
|
146
|
+
// then JSON.parse('') → "Unexpected end of JSON input" → the chain dies → guest
|
|
147
|
+
// daemon-only, no web on 7878. Fix: writeMeta fsyncs temp+parent dir (durable), and
|
|
148
|
+
// readMeta/rebuildIndex tolerate an empty/corrupt meta as MISSING instead of throwing.
|
|
149
|
+
test('writeMeta produces a durable, non-empty, atomically-named meta.json', () => {
|
|
150
|
+
createNode(node('a'));
|
|
151
|
+
const p = nodeMetaPath('a');
|
|
152
|
+
assert.ok(statSync(p).size > 0, 'meta.json must be non-empty');
|
|
153
|
+
assert.ok(!existsSync(`${p}.tmp`), 'temp file must be renamed away, not left behind');
|
|
154
|
+
assert.equal(getNode('a')?.node_id, 'a');
|
|
155
|
+
});
|
|
156
|
+
test('getNode returns null (not throw) on a 0-byte meta.json', () => {
|
|
157
|
+
createNode(node('a'));
|
|
158
|
+
writeFileSync(nodeMetaPath('a'), ''); // the observed NFS 0-byte corruption
|
|
159
|
+
assert.equal(getNode('a'), null);
|
|
160
|
+
});
|
|
161
|
+
test('getNode returns null (not throw) on an unparseable meta.json', () => {
|
|
162
|
+
createNode(node('a'));
|
|
163
|
+
writeFileSync(nodeMetaPath('a'), '{ broken json');
|
|
164
|
+
assert.equal(getNode('a'), null);
|
|
165
|
+
});
|
|
166
|
+
test('rebuildIndex skips a corrupt-meta node instead of throwing', () => {
|
|
167
|
+
createNode(node('good'));
|
|
168
|
+
createNode(node('bad'));
|
|
169
|
+
writeFileSync(nodeMetaPath('bad'), ''); // 0-byte corruption on one node
|
|
170
|
+
closeDb();
|
|
171
|
+
rmSync(join(home, 'canvas.db'), { force: true });
|
|
172
|
+
rmSync(join(home, 'canvas.db-wal'), { force: true });
|
|
173
|
+
rmSync(join(home, 'canvas.db-shm'), { force: true });
|
|
174
|
+
assert.doesNotThrow(() => rebuildIndex());
|
|
175
|
+
assert.equal(getRow('good')?.node_id, 'good'); // healthy node reindexed
|
|
176
|
+
assert.equal(getRow('bad'), null); // corrupt node skipped, no crash
|
|
177
|
+
});
|
|
142
178
|
test('rebuildIndex reconstructs node rows from on-disk metas', () => {
|
|
143
179
|
createNode(node('a', { parent: null }));
|
|
144
180
|
createNode(node('b', { parent: 'a' }));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { test } from 'node:test';
|
|
3
|
+
// @ts-ignore -- chat core is a plain built view module with no generated .d.ts.
|
|
4
|
+
import core from '../../builtin-views/chat/core.mjs';
|
|
5
|
+
import { openLocalStream } from '../view/stream-local.js';
|
|
6
|
+
const flushMicrotasks = async () => {
|
|
7
|
+
await Promise.resolve();
|
|
8
|
+
};
|
|
9
|
+
test('local invalid/no-node stream failures close asynchronously after the channel returns', async () => {
|
|
10
|
+
for (const target of ['not/valid', 'definitely-not-a-real-node-review-5362b32']) {
|
|
11
|
+
const events = [];
|
|
12
|
+
const channel = openLocalStream({ kind: 'stream', target, role: 'controller' }, {
|
|
13
|
+
onFrame() {
|
|
14
|
+
events.push('frame');
|
|
15
|
+
},
|
|
16
|
+
onClose(reason) {
|
|
17
|
+
events.push(`close:${reason}`);
|
|
18
|
+
},
|
|
19
|
+
onError(err) {
|
|
20
|
+
events.push(`error:${err.kind}`);
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
assert.equal(events.length, 0, `${target}: callbacks stay deferred until after openLocalStream returns`);
|
|
24
|
+
assert.equal(typeof channel.close, 'function', `${target}: channel is returned before failure callbacks fire`);
|
|
25
|
+
await flushMicrotasks();
|
|
26
|
+
assert.ok(events[0]?.startsWith('error:'), `${target}: reports the terminal open failure as an error`);
|
|
27
|
+
assert.equal(events[1]?.startsWith('close:'), true, `${target}: follows with streamClose`);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
test('control_changed demotion updates reconnect intent without clobbering an explicit controller request', () => {
|
|
31
|
+
let current = {
|
|
32
|
+
target: 'n1',
|
|
33
|
+
desiredRole: 'controller',
|
|
34
|
+
channel: null,
|
|
35
|
+
clientId: 'client-1',
|
|
36
|
+
conn: 'open',
|
|
37
|
+
role: 'controller',
|
|
38
|
+
controllerId: 'client-1',
|
|
39
|
+
session: null,
|
|
40
|
+
transcript: [],
|
|
41
|
+
draft: '',
|
|
42
|
+
notices: [],
|
|
43
|
+
pendingDialog: null,
|
|
44
|
+
queued: [],
|
|
45
|
+
contextTokens: undefined,
|
|
46
|
+
};
|
|
47
|
+
const ctx = {
|
|
48
|
+
get state() {
|
|
49
|
+
return current;
|
|
50
|
+
},
|
|
51
|
+
set(next) {
|
|
52
|
+
current = typeof next === 'function' ? next(current) : next;
|
|
53
|
+
},
|
|
54
|
+
signal: {
|
|
55
|
+
clearBanner() { },
|
|
56
|
+
setBanner() { },
|
|
57
|
+
setStatus() { },
|
|
58
|
+
setSubtitle() { },
|
|
59
|
+
setMode() { },
|
|
60
|
+
quit() { },
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
core.intents.streamFrame(ctx, { type: 'control_changed', controller_id: 'other-client' });
|
|
64
|
+
assert.equal(current.role, 'observer');
|
|
65
|
+
assert.equal(current.desiredRole, 'observer');
|
|
66
|
+
current = { ...current, role: 'observer', desiredRole: 'controller' };
|
|
67
|
+
core.intents.streamFrame(ctx, { type: 'control_changed', controller_id: 'other-client' });
|
|
68
|
+
assert.equal(current.role, 'observer');
|
|
69
|
+
assert.equal(current.desiredRole, 'controller');
|
|
70
|
+
});
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
// the one genuine-terminal wake that still lives in core: close.ts.
|
|
24
24
|
import { test, before, after, beforeEach } from 'node:test';
|
|
25
25
|
import assert from 'node:assert/strict';
|
|
26
|
-
import { mkdtempSync, rmSync } from 'node:fs';
|
|
26
|
+
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
27
27
|
import { tmpdir } from 'node:os';
|
|
28
28
|
import { join } from 'node:path';
|
|
29
29
|
import { spawnSync } from 'node:child_process';
|
|
@@ -46,6 +46,15 @@ function node(id, over = {}) {
|
|
|
46
46
|
...over,
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
+
/** Write a minimal pi session with one assistant message so the node reads as
|
|
50
|
+
* NON-empty (produced AI output) — otherwise closeNode reaps it outright. */
|
|
51
|
+
function withSession(id) {
|
|
52
|
+
const dir = join(home, 'sessions');
|
|
53
|
+
mkdirSync(dir, { recursive: true });
|
|
54
|
+
const f = join(dir, `${id}.jsonl`);
|
|
55
|
+
writeFileSync(f, JSON.stringify({ type: 'message', message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }) + '\n');
|
|
56
|
+
return f;
|
|
57
|
+
}
|
|
49
58
|
/** A pid that is guaranteed dead. */
|
|
50
59
|
function deadPid() {
|
|
51
60
|
const r = spawnSync('true', [], { stdio: 'ignore' });
|
|
@@ -115,7 +124,7 @@ test('pid-only liveness: a dead-engine booted child is revivable, NOT reaped, an
|
|
|
115
124
|
test('node close of a child wakes its SURVIVING manager (the parent outside the closing set)', async () => {
|
|
116
125
|
inboxWaitingParent('PARENT');
|
|
117
126
|
// A booted child; closeNode tears its (paneless-for-the-test) engine down.
|
|
118
|
-
createNode(node('CHILD', { pane: GONE_PANE, tmux_session: 'crtr-cdw', window: '@1', pi_session_id: 'booted', status: 'active' }));
|
|
127
|
+
createNode(node('CHILD', { pane: GONE_PANE, tmux_session: 'crtr-cdw', window: '@1', pi_session_id: 'booted', pi_session_file: withSession('CHILD'), status: 'active' }));
|
|
119
128
|
subscribe('PARENT', 'CHILD', true); // PARENT is a manager OUTSIDE the close set
|
|
120
129
|
// Closing CHILD: closingSet({CHILD}) cannot pull in PARENT (PARENT is a
|
|
121
130
|
// SUBSCRIBER of CHILD, never a descendant), so PARENT survives + must wake.
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { test, before, after, beforeEach } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { mkdtempSync, rmSync } from 'node:fs';
|
|
3
|
+
import { mkdtempSync, rmSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { createNode, getNode, subscribe } from '../canvas/canvas.js';
|
|
7
|
+
import { nodeDir } from '../canvas/paths.js';
|
|
7
8
|
import { closeDb } from '../canvas/db.js';
|
|
8
9
|
import { closeNode } from '../runtime/close.js';
|
|
9
10
|
import { readInboxSince } from '../feed/inbox.js';
|
|
10
11
|
let home;
|
|
12
|
+
/** Write a minimal pi session containing one assistant message, so the node
|
|
13
|
+
* reads as NON-empty (it produced AI output). Without this a node has no
|
|
14
|
+
* session and closeNode reaps it outright instead of cancel+notice — see the
|
|
15
|
+
* dedicated empty-reap test below. */
|
|
16
|
+
function withSession(id) {
|
|
17
|
+
const dir = join(home, 'sessions');
|
|
18
|
+
mkdirSync(dir, { recursive: true });
|
|
19
|
+
const f = join(dir, `${id}.jsonl`);
|
|
20
|
+
writeFileSync(f, JSON.stringify({ type: 'message', message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }) + '\n');
|
|
21
|
+
return f;
|
|
22
|
+
}
|
|
11
23
|
function node(id, over = {}) {
|
|
12
24
|
return {
|
|
13
25
|
node_id: id,
|
|
@@ -18,6 +30,7 @@ function node(id, over = {}) {
|
|
|
18
30
|
mode: 'base',
|
|
19
31
|
lifecycle: 'terminal',
|
|
20
32
|
status: 'active',
|
|
33
|
+
pi_session_file: withSession(id),
|
|
21
34
|
...over,
|
|
22
35
|
};
|
|
23
36
|
}
|
|
@@ -132,6 +145,31 @@ test('closing a leaf node closes only itself', () => {
|
|
|
132
145
|
test('throws on an unknown node', () => {
|
|
133
146
|
assert.throws(() => closeNode('ghost'), /unknown node/);
|
|
134
147
|
});
|
|
148
|
+
// Regression: empty nodes (engine never produced an assistant message) are
|
|
149
|
+
// USELESS shells, so closing one must DELETE it from the graph + disk, not park
|
|
150
|
+
// a canceled husk. (Reported: "many nodes on the canvas that haven't generated
|
|
151
|
+
// anything yet, just taking up space" — close/detach now reap them.)
|
|
152
|
+
test('closing an EMPTY node (no AI output) deletes it from the graph + disk', () => {
|
|
153
|
+
createNode(node('shell', { pi_session_file: null }));
|
|
154
|
+
const dir = nodeDir('shell');
|
|
155
|
+
assert.ok(existsSync(dir), 'dir exists before close');
|
|
156
|
+
const res = closeNode('shell');
|
|
157
|
+
assert.deepEqual(res.closed, ['shell']);
|
|
158
|
+
assert.equal(getNode('shell'), null, 'row deleted from the graph');
|
|
159
|
+
assert.equal(existsSync(dir), false, 'on-disk dir removed');
|
|
160
|
+
});
|
|
161
|
+
// A close that reaps an EMPTY child still wakes a SURVIVING manager — the child
|
|
162
|
+
// is gone either way, so a parent that delegated must learn it vanished.
|
|
163
|
+
test('reaping an empty child still notifies a surviving external manager', () => {
|
|
164
|
+
createNode(node('child', { pi_session_file: null })); // empty → reaped
|
|
165
|
+
createNode(node('mgr')); // external manager, NOT closed
|
|
166
|
+
spawnEdge('mgr', 'child');
|
|
167
|
+
closeNode('child');
|
|
168
|
+
assert.equal(getNode('child'), null, 'empty child reaped');
|
|
169
|
+
const note = readInboxSince('mgr').at(-1);
|
|
170
|
+
assert.match(note.label, /Child closed/);
|
|
171
|
+
assert.equal(note.data?.['child'], 'child');
|
|
172
|
+
});
|
|
135
173
|
// NOTE (broker-host cut): the former "Step 7: closing a FOCUSED node closes its
|
|
136
174
|
// focus row + nulls its pane (tearDownNode)" test was DELETED. close.ts no longer
|
|
137
175
|
// routes through the synchronous tearDownNode — it sends the broker `shutdown`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Run with: node --import tsx/esm --test src/core/__tests__/connection-reconnect-resume.test.ts
|
|
2
|
+
//
|
|
3
|
+
// BUG-REGRESSION: connection faults follow the shared broker-owned retry schedule.
|
|
4
|
+
// The broker exercises this policy table directly.
|
|
5
|
+
import { test } from 'node:test';
|
|
6
|
+
import assert from 'node:assert/strict';
|
|
7
|
+
import { nextFaultRetry, policyFor } from '../runtime/fault-recovery.js';
|
|
8
|
+
const NOW = Date.parse('2026-06-23T20:00:00.000Z');
|
|
9
|
+
const ISO = (ms) => new Date(ms).toISOString();
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// J1 — the policy table itself: connection faults retry on a 5s → 2x → 60s
|
|
12
|
+
// cap, and the planner advances the next attempt.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
test('J1 policy table: connection faults use the shared retry schedule', () => {
|
|
15
|
+
assert.deepEqual(policyFor('connection'), {
|
|
16
|
+
kind: 'connection',
|
|
17
|
+
gate: 'online',
|
|
18
|
+
max: 8,
|
|
19
|
+
baseDelayMs: 5_000,
|
|
20
|
+
capDelayMs: 60_000,
|
|
21
|
+
fatal: false,
|
|
22
|
+
});
|
|
23
|
+
const plan = nextFaultRetry({
|
|
24
|
+
link: 'pi→provider',
|
|
25
|
+
op: 'generation turn',
|
|
26
|
+
kind: 'connection',
|
|
27
|
+
retry: { disposition: 'auto', by: 'daemon' },
|
|
28
|
+
message: 'Connection error.',
|
|
29
|
+
since: ISO(NOW),
|
|
30
|
+
logPath: '/tmp/crtr-fault.log',
|
|
31
|
+
}, NOW);
|
|
32
|
+
assert.equal(plan.attempt, 1, 'the first plan primes attempt 1');
|
|
33
|
+
assert.equal(plan.max, 8, 'connection max attempts stay at 8');
|
|
34
|
+
assert.equal(plan.nextAt, ISO(NOW + 5_000), 'connection starts at 5s');
|
|
35
|
+
assert.equal(plan.fault.retry.attempt, 1, 'planned marker carries the primed attempt');
|
|
36
|
+
assert.equal(plan.fault.retry.disposition, 'auto', 'planned marker stays auto');
|
|
37
|
+
});
|