@crouton-kit/crouter 0.3.31 → 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 +9 -3
- 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 +37 -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/__tests__/empty-node-guard.test.d.ts +1 -0
- package/dist/core/feed/__tests__/empty-node-guard.test.js +45 -0
- package/dist/core/feed/feed.js +8 -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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgentMessage } from '@earendil-works/pi-agent-core';
|
|
1
2
|
/** Test seam (M-1 regression): a FRESH-start kickoff prompt carrying this token
|
|
2
3
|
* makes the fake engine THROW inside bindExtensions BEFORE it fires
|
|
3
4
|
* session_start — the exact failure mode that records no pid and no session, so
|
|
@@ -64,10 +65,23 @@ interface ResourceLoader {
|
|
|
64
65
|
runtime: ExtRuntime;
|
|
65
66
|
};
|
|
66
67
|
}
|
|
68
|
+
interface TreeEntry {
|
|
69
|
+
type: 'message';
|
|
70
|
+
id: string;
|
|
71
|
+
parentId: string | null;
|
|
72
|
+
timestamp: string;
|
|
73
|
+
message: AgentMessage;
|
|
74
|
+
}
|
|
75
|
+
interface SessionTreeNode {
|
|
76
|
+
entry: TreeEntry;
|
|
77
|
+
children: SessionTreeNode[];
|
|
78
|
+
}
|
|
67
79
|
declare class FakeSession {
|
|
68
80
|
private readonly sm;
|
|
69
81
|
private readonly loader;
|
|
70
82
|
private readonly dir;
|
|
83
|
+
private readonly tree;
|
|
84
|
+
private promptFailuresRemaining;
|
|
71
85
|
private extensions;
|
|
72
86
|
private readonly injected;
|
|
73
87
|
private uiContext;
|
|
@@ -78,14 +92,25 @@ declare class FakeSession {
|
|
|
78
92
|
private timer;
|
|
79
93
|
private readonly listeners;
|
|
80
94
|
private readonly messageLog;
|
|
95
|
+
private turnEndLeafId;
|
|
81
96
|
constructor(sm: SessionManager, loader: ResourceLoader | undefined);
|
|
82
|
-
get messages():
|
|
97
|
+
get messages(): AgentMessage[];
|
|
83
98
|
get sessionManager(): {
|
|
84
99
|
buildSessionContext: () => {
|
|
85
|
-
messages:
|
|
100
|
+
messages: AgentMessage[];
|
|
86
101
|
thinkingLevel: string;
|
|
87
102
|
model: null;
|
|
88
103
|
};
|
|
104
|
+
getLeafId: () => string | null;
|
|
105
|
+
getEntry: (id: string) => TreeEntry | undefined;
|
|
106
|
+
getTree: () => SessionTreeNode[];
|
|
107
|
+
getUserMessagesForForking: () => Array<{
|
|
108
|
+
entryId: string;
|
|
109
|
+
text: string;
|
|
110
|
+
timestamp?: string;
|
|
111
|
+
}>;
|
|
112
|
+
getSessionId: () => string;
|
|
113
|
+
getSessionFile: () => string;
|
|
89
114
|
};
|
|
90
115
|
get sessionId(): string;
|
|
91
116
|
get sessionFile(): string | undefined;
|
|
@@ -98,6 +123,7 @@ declare class FakeSession {
|
|
|
98
123
|
}): Promise<void>;
|
|
99
124
|
get isStreaming(): boolean;
|
|
100
125
|
getSessionStats(): Record<string, unknown>;
|
|
126
|
+
private syncTreeSnapshot;
|
|
101
127
|
setSessionName(_name: string): void;
|
|
102
128
|
prompt(text: string, _options?: unknown): Promise<void>;
|
|
103
129
|
steer(_text: string): Promise<void>;
|
|
@@ -106,8 +132,8 @@ declare class FakeSession {
|
|
|
106
132
|
/** In-place tree rewind (broker `navigate_tree`). Mirrors the real SDK's
|
|
107
133
|
* contract: the session file is unchanged, the live history is truncated,
|
|
108
134
|
* and the navigated-to user message's text comes back as `editorText`. The
|
|
109
|
-
* fake's "rewind"
|
|
110
|
-
*
|
|
135
|
+
* fake's "rewind" re-anchors the active branch without deleting the abandoned
|
|
136
|
+
* tail so the welcome snapshot can prove the one-nudge property. */
|
|
111
137
|
navigateTree(targetId: string, _options?: unknown): Promise<{
|
|
112
138
|
editorText?: string;
|
|
113
139
|
cancelled: boolean;
|
|
@@ -140,10 +140,86 @@ function assistantMessage(text, pad = 0) {
|
|
|
140
140
|
function textDelta(partial, delta) {
|
|
141
141
|
return { type: 'text_delta', contentIndex: 0, delta, partial };
|
|
142
142
|
}
|
|
143
|
+
class SessionTree {
|
|
144
|
+
entries = new Map();
|
|
145
|
+
children = new Map();
|
|
146
|
+
leafId = null;
|
|
147
|
+
append(message) {
|
|
148
|
+
const entry = {
|
|
149
|
+
type: 'message',
|
|
150
|
+
id: randomUUID().slice(0, 8),
|
|
151
|
+
parentId: this.leafId,
|
|
152
|
+
timestamp: new Date().toISOString(),
|
|
153
|
+
message,
|
|
154
|
+
};
|
|
155
|
+
this.entries.set(entry.id, entry);
|
|
156
|
+
const siblings = this.children.get(entry.parentId) ?? [];
|
|
157
|
+
siblings.push(entry.id);
|
|
158
|
+
this.children.set(entry.parentId, siblings);
|
|
159
|
+
this.leafId = entry.id;
|
|
160
|
+
return entry;
|
|
161
|
+
}
|
|
162
|
+
navigateTree(targetId) {
|
|
163
|
+
const entry = this.entries.get(targetId);
|
|
164
|
+
if (entry === undefined)
|
|
165
|
+
throw new Error(`[fake-engine] navigateTree: unknown entry ${targetId}`);
|
|
166
|
+
this.leafId = targetId;
|
|
167
|
+
return { cancelled: false, editorText: this.textOf(entry.message) };
|
|
168
|
+
}
|
|
169
|
+
getLeafId() {
|
|
170
|
+
return this.leafId;
|
|
171
|
+
}
|
|
172
|
+
getEntry(id) {
|
|
173
|
+
return this.entries.get(id);
|
|
174
|
+
}
|
|
175
|
+
getTree() {
|
|
176
|
+
return (this.children.get(null) ?? []).map((id) => this.node(id)).filter((node) => node !== null);
|
|
177
|
+
}
|
|
178
|
+
getUserMessagesForForking() {
|
|
179
|
+
return [...this.entries.values()]
|
|
180
|
+
.filter((entry) => entry.message.role === 'user')
|
|
181
|
+
.map((entry) => ({ entryId: entry.id, text: this.textOf(entry.message), timestamp: entry.timestamp }));
|
|
182
|
+
}
|
|
183
|
+
buildSessionContext() {
|
|
184
|
+
return { messages: this.branchMessages(), thinkingLevel: 'off', model: null };
|
|
185
|
+
}
|
|
186
|
+
branchMessages() {
|
|
187
|
+
const ids = [];
|
|
188
|
+
let cur = this.leafId;
|
|
189
|
+
while (cur !== null) {
|
|
190
|
+
const entry = this.entries.get(cur);
|
|
191
|
+
if (entry === undefined)
|
|
192
|
+
break;
|
|
193
|
+
ids.push(cur);
|
|
194
|
+
cur = entry.parentId;
|
|
195
|
+
}
|
|
196
|
+
ids.reverse();
|
|
197
|
+
return ids.map((id) => this.entries.get(id)?.message).filter((m) => m !== undefined);
|
|
198
|
+
}
|
|
199
|
+
node(id) {
|
|
200
|
+
const entry = this.entries.get(id);
|
|
201
|
+
if (entry === undefined)
|
|
202
|
+
return null;
|
|
203
|
+
return {
|
|
204
|
+
entry,
|
|
205
|
+
children: (this.children.get(id) ?? []).map((child) => this.node(child)).filter((node) => node !== null),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
textOf(message) {
|
|
209
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
210
|
+
for (const part of content) {
|
|
211
|
+
if (typeof part?.text === 'string' && part.text !== '')
|
|
212
|
+
return part.text;
|
|
213
|
+
}
|
|
214
|
+
return '';
|
|
215
|
+
}
|
|
216
|
+
}
|
|
143
217
|
class FakeSession {
|
|
144
218
|
sm;
|
|
145
219
|
loader;
|
|
146
220
|
dir;
|
|
221
|
+
tree = new SessionTree();
|
|
222
|
+
promptFailuresRemaining;
|
|
147
223
|
extensions = [];
|
|
148
224
|
injected = [];
|
|
149
225
|
uiContext;
|
|
@@ -156,25 +232,35 @@ class FakeSession {
|
|
|
156
232
|
// accumulating message history the catch-up snapshot serializes (G3).
|
|
157
233
|
listeners = new Set();
|
|
158
234
|
messageLog = [];
|
|
235
|
+
turnEndLeafId = null;
|
|
159
236
|
constructor(sm, loader) {
|
|
160
237
|
this.sm = sm;
|
|
161
238
|
this.loader = loader;
|
|
162
239
|
this.dir = nodeDirFromEnv();
|
|
240
|
+
const configured = Number(process.env['CRTR_FAKE_ENGINE_PROMPT_FAILS'] ?? '0');
|
|
241
|
+
this.promptFailuresRemaining = Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : 0;
|
|
242
|
+
this.syncTreeSnapshot();
|
|
163
243
|
}
|
|
164
244
|
// --- broker-read getters. buildSnapshot (on a client hello) reads messages/
|
|
165
245
|
// stats/model: T8 clients attach and assert on welcome.snapshot, so messages
|
|
166
246
|
// returns the accrued history; stats/model stay cheap stubs. ---------------
|
|
167
247
|
get messages() {
|
|
168
|
-
return this.
|
|
248
|
+
return this.tree.buildSessionContext().messages;
|
|
169
249
|
}
|
|
170
250
|
// The broker's catch-up snapshot reconstructs history from the persisted
|
|
171
251
|
// session tree (sessionManager.buildSessionContext) — the single source of
|
|
172
252
|
// truth, since `session.messages` can be sliced by pi's retry/overflow/
|
|
173
253
|
// compaction recovery. Mirror that surface so the fake's snapshot still serves
|
|
174
|
-
// the accrued history (G2/G3). The fake's persisted analog
|
|
254
|
+
// the accrued history (G2/G3). The fake's persisted analog is the active branch.
|
|
175
255
|
get sessionManager() {
|
|
176
256
|
return {
|
|
177
|
-
buildSessionContext: () =>
|
|
257
|
+
buildSessionContext: () => this.tree.buildSessionContext(),
|
|
258
|
+
getLeafId: () => this.tree.getLeafId(),
|
|
259
|
+
getEntry: (id) => this.tree.getEntry(id),
|
|
260
|
+
getTree: () => this.tree.getTree(),
|
|
261
|
+
getUserMessagesForForking: () => this.tree.getUserMessagesForForking(),
|
|
262
|
+
getSessionId: () => this.sm.getSessionId(),
|
|
263
|
+
getSessionFile: () => this.sm.getSessionFile(),
|
|
178
264
|
};
|
|
179
265
|
}
|
|
180
266
|
get sessionId() {
|
|
@@ -200,19 +286,33 @@ class FakeSession {
|
|
|
200
286
|
getSessionStats() {
|
|
201
287
|
return { messages: 0, tokens: 0 };
|
|
202
288
|
}
|
|
289
|
+
syncTreeSnapshot() {
|
|
290
|
+
try {
|
|
291
|
+
writeFileSync(join(this.dir, 'fake-pi.tree.json'), JSON.stringify({
|
|
292
|
+
leafId: this.tree.getLeafId(),
|
|
293
|
+
activeBranch: this.tree.buildSessionContext().messages,
|
|
294
|
+
tree: this.tree.getTree(),
|
|
295
|
+
userMessagesForForking: this.tree.getUserMessagesForForking(),
|
|
296
|
+
}, null, 2));
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
/* best effort */
|
|
300
|
+
}
|
|
301
|
+
}
|
|
203
302
|
// --- naming is cosmetic (broker calls it in a try/catch) ---------------------
|
|
204
303
|
setSessionName(_name) {
|
|
205
304
|
/* no-op recording stub */
|
|
206
305
|
}
|
|
207
306
|
// --- controller-drive stubs (no controller connects in these tests) ----------
|
|
208
307
|
prompt(text, _options) {
|
|
209
|
-
// The broker calls this for
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
308
|
+
// The broker calls this for the controller-less retry path. Emit the turn
|
|
309
|
+
// on the broker's subscribe channel and also run the real extension hooks so
|
|
310
|
+
// the canvas stophook records the fault marker and the broker can schedule
|
|
311
|
+
// the next retry.
|
|
312
|
+
const fail = this.promptFailuresRemaining > 0;
|
|
313
|
+
if (fail)
|
|
314
|
+
this.promptFailuresRemaining--;
|
|
315
|
+
return this.emitTurn(text, { fail, runHooks: true });
|
|
216
316
|
}
|
|
217
317
|
steer(_text) {
|
|
218
318
|
return Promise.resolve();
|
|
@@ -226,11 +326,12 @@ class FakeSession {
|
|
|
226
326
|
/** In-place tree rewind (broker `navigate_tree`). Mirrors the real SDK's
|
|
227
327
|
* contract: the session file is unchanged, the live history is truncated,
|
|
228
328
|
* and the navigated-to user message's text comes back as `editorText`. The
|
|
229
|
-
* fake's "rewind"
|
|
230
|
-
*
|
|
329
|
+
* fake's "rewind" re-anchors the active branch without deleting the abandoned
|
|
330
|
+
* tail so the welcome snapshot can prove the one-nudge property. */
|
|
231
331
|
navigateTree(targetId, _options) {
|
|
232
|
-
this.
|
|
233
|
-
|
|
332
|
+
const result = this.tree.navigateTree(targetId);
|
|
333
|
+
this.syncTreeSnapshot();
|
|
334
|
+
return Promise.resolve(result);
|
|
234
335
|
}
|
|
235
336
|
// --- the single engine event stream the broker fans out (broker.ts subscribes
|
|
236
337
|
// here and broadcasts each event VERBATIM to every attached viewer) --------
|
|
@@ -266,6 +367,7 @@ class FakeSession {
|
|
|
266
367
|
const updates = opts.updates ?? 3;
|
|
267
368
|
const pad = opts.padBytes ?? 0;
|
|
268
369
|
const withTool = opts.tool ?? true;
|
|
370
|
+
const fail = opts.fail === true;
|
|
269
371
|
const small = assistantMessage(text);
|
|
270
372
|
// A normal turn yields with setImmediate (fast). A sized flood (G8, pad>0) is
|
|
271
373
|
// PACED with a small real delay so a fast (reading) viewer always drains each
|
|
@@ -274,6 +376,9 @@ class FakeSession {
|
|
|
274
376
|
// pace the burst outruns the reader's event loop and sheds the fast viewer too.
|
|
275
377
|
const yieldTick = () => pad > 0 ? new Promise((r) => setTimeout(r, 3)) : new Promise((r) => setImmediate(r));
|
|
276
378
|
this.streaming = true;
|
|
379
|
+
if (opts.runHooks === true) {
|
|
380
|
+
await this.fire('agent_start', {}, this.buildCtx());
|
|
381
|
+
}
|
|
277
382
|
this.emit({ type: 'agent_start' });
|
|
278
383
|
this.emit({ type: 'turn_start' });
|
|
279
384
|
this.emit({ type: 'message_start', message: small });
|
|
@@ -337,14 +442,39 @@ class FakeSession {
|
|
|
337
442
|
}
|
|
338
443
|
if (this.disposed)
|
|
339
444
|
return;
|
|
340
|
-
const
|
|
445
|
+
const userMsg = {
|
|
446
|
+
role: 'user',
|
|
447
|
+
content: [{ type: 'text', text }],
|
|
448
|
+
timestamp: Date.now(),
|
|
449
|
+
};
|
|
450
|
+
this.tree.append(userMsg);
|
|
451
|
+
this.syncTreeSnapshot();
|
|
452
|
+
const finalMsg = fail
|
|
453
|
+
? { ...assistantMessage(text), stopReason: 'error', errorMessage: 'Connection error.' }
|
|
454
|
+
: assistantMessage(text);
|
|
341
455
|
this.emit({ type: 'message_end', message: finalMsg });
|
|
342
456
|
this.emit({ type: 'turn_end', message: finalMsg, toolResults: [] });
|
|
343
457
|
this.messageLog.push(finalMsg);
|
|
458
|
+
const anchorBeforeFinal = this.tree.getLeafId();
|
|
459
|
+
this.turnEndLeafId = anchorBeforeFinal;
|
|
460
|
+
this.tree.append(finalMsg);
|
|
461
|
+
this.syncTreeSnapshot();
|
|
462
|
+
if (opts.runHooks === true) {
|
|
463
|
+
const hookCtx = this.buildCtx();
|
|
464
|
+
await this.fire('turn_end', { message: finalMsg, toolResults: [] }, hookCtx);
|
|
465
|
+
const hookMessages = fail
|
|
466
|
+
? [...this.tree.buildSessionContext().messages, finalMsg]
|
|
467
|
+
: this.tree.buildSessionContext().messages.slice();
|
|
468
|
+
await this.fire('agent_end', { messages: hookMessages, willRetry: false }, hookCtx);
|
|
469
|
+
}
|
|
470
|
+
this.turnEndLeafId = null;
|
|
344
471
|
this.streaming = false;
|
|
345
472
|
// agent_end on the SUBSCRIBE channel is a relayed frame ONLY (willRetry=false);
|
|
346
473
|
// it never runs the stophook, so the broker stays alive (G2/G3/G4/G8).
|
|
347
|
-
|
|
474
|
+
const agentEndMessages = fail
|
|
475
|
+
? [...this.tree.buildSessionContext().messages, finalMsg]
|
|
476
|
+
: this.tree.buildSessionContext().messages.slice();
|
|
477
|
+
this.emit({ type: 'agent_end', messages: agentEndMessages, willRetry: false });
|
|
348
478
|
}
|
|
349
479
|
dispose() {
|
|
350
480
|
this.disposed = true;
|
|
@@ -470,6 +600,7 @@ class FakeSession {
|
|
|
470
600
|
sessionManager: {
|
|
471
601
|
getSessionId: () => this.sm.getSessionId(),
|
|
472
602
|
getSessionFile: () => this.sm.getSessionFile(),
|
|
603
|
+
getLeafId: () => this.turnEndLeafId ?? this.tree.getLeafId(),
|
|
473
604
|
},
|
|
474
605
|
getContextUsage: () => ({ tokens: 1000 }),
|
|
475
606
|
// Wired to the broker's shutdownHandler: the stophook calls ctx.shutdown()
|
|
@@ -557,7 +688,15 @@ class FakeSession {
|
|
|
557
688
|
this.streaming = false;
|
|
558
689
|
await this.fire('agent_end', {
|
|
559
690
|
messages: [
|
|
560
|
-
{
|
|
691
|
+
{
|
|
692
|
+
role: 'assistant',
|
|
693
|
+
stopReason: cmd.reason ?? 'stop',
|
|
694
|
+
// A real engine attaches errorMessage on a stopReason:'error'
|
|
695
|
+
// turn; the stophook classifies it for the active-fault marker.
|
|
696
|
+
// Forward it so a test can drive a CONNECTION stall faithfully.
|
|
697
|
+
...(cmd.errorMessage === undefined ? {} : { errorMessage: cmd.errorMessage }),
|
|
698
|
+
content: [{ type: 'text', text: cmd.text ?? '' }],
|
|
699
|
+
},
|
|
561
700
|
],
|
|
562
701
|
}, ctx);
|
|
563
702
|
break;
|
|
@@ -255,7 +255,16 @@ async function dispatch(cmd) {
|
|
|
255
255
|
streaming = false;
|
|
256
256
|
await fire('agent_end', {
|
|
257
257
|
messages: [
|
|
258
|
-
{
|
|
258
|
+
{
|
|
259
|
+
role: 'assistant',
|
|
260
|
+
stopReason: cmd.reason ?? 'stop',
|
|
261
|
+
// A real engine attaches errorMessage on a stopReason:'error' turn;
|
|
262
|
+
// the stophook classifies it (connection/rate-limit/…) for the
|
|
263
|
+
// active-fault marker. Forward it so a test can drive a CONNECTION
|
|
264
|
+
// stall faithfully.
|
|
265
|
+
...(cmd.errorMessage === undefined ? {} : { errorMessage: cmd.errorMessage }),
|
|
266
|
+
content: [{ type: 'text', text: cmd.text ?? '' }],
|
|
267
|
+
},
|
|
259
268
|
],
|
|
260
269
|
}, ctx);
|
|
261
270
|
ack(id, { ok: true });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Run with: node --import tsx/esm --test src/core/__tests__/human-new-window-regression.test.ts
|
|
2
2
|
//
|
|
3
|
-
// BUG REGRESSION: `crtr human ask|
|
|
3
|
+
// BUG REGRESSION: `crtr human ask|review|notify` opened NO pane at all.
|
|
4
4
|
//
|
|
5
5
|
// spawnAndDetach's 'new-window' placement passed a tmux PANE id straight to
|
|
6
6
|
// `new-window -t` (`new-window -d -a -t %<pane>`). tmux REJECTS a pane id for
|
|
@@ -76,7 +76,7 @@ export interface Harness {
|
|
|
76
76
|
spawnHeadlessChild(parentId: string, task: string, o?: SpawnOpts): Promise<string>;
|
|
77
77
|
spawnHeadlessChildNoBoot(parentId: string, task: string, o?: SpawnOpts): Promise<string>;
|
|
78
78
|
turn(nodeId: string, text?: string): Promise<void>;
|
|
79
|
-
stop(nodeId: string, reason?: 'stop' | 'length' | 'aborted' | 'error'): Promise<void>;
|
|
79
|
+
stop(nodeId: string, reason?: 'stop' | 'length' | 'aborted' | 'error', errorMessage?: string): Promise<void>;
|
|
80
80
|
finish(nodeId: string, finalText: string): Promise<void>;
|
|
81
81
|
tick(now?: number): Promise<void>;
|
|
82
82
|
awaitBoot(nodeId: string, o?: {
|
|
@@ -200,7 +200,13 @@ export async function createHarness(opts = {}) {
|
|
|
200
200
|
}
|
|
201
201
|
function nodeDirs() {
|
|
202
202
|
try {
|
|
203
|
-
|
|
203
|
+
// Node ids are minted by newNodeId() (runtime/nodes.ts) as `<base36-ts>-<8
|
|
204
|
+
// hex>`. Scope the scan to that shape so a stray non-node sibling under
|
|
205
|
+
// nodes/ is never miscounted as a new node dir — e.g. a `reports/` dir
|
|
206
|
+
// created for an empty node id (the rare CI flake on full-tier run
|
|
207
|
+
// 27459304455 that broke the spawn counters with
|
|
208
|
+
// `expected exactly 1 new node dir, got [<id>, reports]`).
|
|
209
|
+
return readdirSync(join(home, 'nodes')).filter((d) => /^[0-9a-z]+-[0-9a-f]{8}$/.test(d));
|
|
204
210
|
}
|
|
205
211
|
catch {
|
|
206
212
|
return [];
|
|
@@ -383,9 +389,9 @@ export async function createHarness(opts = {}) {
|
|
|
383
389
|
sendCmd(nodeId, { cmd: 'turn', id: `turn-${Date.now()}`, text });
|
|
384
390
|
await awaitAgentEnd(nodeId, base, `turn agent_end for ${nodeId}`);
|
|
385
391
|
},
|
|
386
|
-
async stop(nodeId, reason = 'stop') {
|
|
392
|
+
async stop(nodeId, reason = 'stop', errorMessage) {
|
|
387
393
|
const base = eventCount(nodeId, 'agent_end');
|
|
388
|
-
sendCmd(nodeId, { cmd: 'stop', id: `stop-${Date.now()}`, reason });
|
|
394
|
+
sendCmd(nodeId, { cmd: 'stop', id: `stop-${Date.now()}`, reason, errorMessage });
|
|
389
395
|
await awaitAgentEnd(nodeId, base, `stop agent_end for ${nodeId}`);
|
|
390
396
|
},
|
|
391
397
|
async finish(nodeId, finalText) {
|
|
@@ -22,7 +22,7 @@ import { closeDb } from '../canvas/db.js';
|
|
|
22
22
|
import { readInboxSince } from '../feed/inbox.js';
|
|
23
23
|
import { interactionDir, interactionsRoot } from '../artifact.js';
|
|
24
24
|
import { atomicWriteJson, deckPath } from '@crouton-kit/humanloop';
|
|
25
|
-
import { finalizeResolvedInteraction } from '../../commands/human/queue.js';
|
|
25
|
+
import { finalizeResolvedInteraction, acquireInteractionClaim } from '../../commands/human/queue.js';
|
|
26
26
|
let home;
|
|
27
27
|
// A unique cwd so interactionDir (keyed off the REAL homedir + mangled cwd, NOT
|
|
28
28
|
// CRTR_HOME) lands in an isolated, cleanable subtree.
|
|
@@ -96,6 +96,43 @@ test('a canceled-on-disk response reaps the bridge without delivering a result',
|
|
|
96
96
|
const blob = JSON.stringify(readInboxSince('A'));
|
|
97
97
|
assert.match(blob, /no answer is coming/);
|
|
98
98
|
});
|
|
99
|
+
test('the _run held-claim path delivers, and finalize without the claim self-conflicts', async () => {
|
|
100
|
+
// BUG REGRESSION (_run ask self-claim): the detached `_run` ask worker holds
|
|
101
|
+
// the exclusive resolve.lock across the blocking ask(), then must deliver the
|
|
102
|
+
// answer. If it calls finalizeResolvedInteraction WITHOUT passing its held
|
|
103
|
+
// claim, finalize re-acquires the same lock, sees this very process's live
|
|
104
|
+
// lock (EEXIST, owner.pid == self, pidAlive true) -> 'claimed' -> returns
|
|
105
|
+
// false, and the human's answer never reaches the asker. Passing the held
|
|
106
|
+
// claim lets finalize run under the worker's own lock.
|
|
107
|
+
createNode(node('A', null));
|
|
108
|
+
createNode(node('B', 'A'));
|
|
109
|
+
subscribe('A', 'B');
|
|
110
|
+
// Set up the deck + run.json but NOT the answer yet, then hold the claim
|
|
111
|
+
// exactly as the _run worker does (no opts) BEFORE ask() resolves the deck.
|
|
112
|
+
const idir = interactionDir('B', workCwd);
|
|
113
|
+
mkdirSync(idir, { recursive: true });
|
|
114
|
+
atomicWriteJson(deckPath(idir), { interactions: [] });
|
|
115
|
+
atomicWriteJson(join(idir, 'run.json'), { mode: 'ask', job_id: 'B' });
|
|
116
|
+
const claim = acquireInteractionClaim(idir, 'B');
|
|
117
|
+
assert.equal(typeof claim === 'object', true, 'claim must be acquired');
|
|
118
|
+
if (typeof claim !== 'object')
|
|
119
|
+
return;
|
|
120
|
+
// ask() then writes the human's answer to disk while the claim is held.
|
|
121
|
+
atomicWriteJson(join(idir, 'response.json'), {
|
|
122
|
+
responses: [{ id: 'q', selectedOptionId: 'resync', freetext: 'go ahead' }],
|
|
123
|
+
completedAt: '2026-06-10T22:27:07.000Z',
|
|
124
|
+
});
|
|
125
|
+
// Without the held claim, finalize from the SAME pid self-conflicts on the
|
|
126
|
+
// live lock and must NOT deliver (this is the stranding regression).
|
|
127
|
+
assert.equal(await finalizeResolvedInteraction('B'), false, 'self-held lock must block claimless finalize');
|
|
128
|
+
assert.equal(getNode('B')?.status, 'active', 'bridge must still be live after the self-conflicting call');
|
|
129
|
+
// Passing the held claim lets finalize run under the worker's own lock and
|
|
130
|
+
// deliver the answer back to the asker, then reap the bridge.
|
|
131
|
+
assert.equal(await finalizeResolvedInteraction('B', claim), true, 'held claim must deliver');
|
|
132
|
+
assert.equal(getNode('B')?.status, 'done', 'bridge reaped after held-claim deliver');
|
|
133
|
+
assert.match(JSON.stringify(readInboxSince('A')), /resync/, 'asker must receive the human selection');
|
|
134
|
+
claim.release();
|
|
135
|
+
});
|
|
99
136
|
test('an unresolved (no response.json) live bridge is left untouched', async () => {
|
|
100
137
|
createNode(node('A', null));
|
|
101
138
|
createNode(node('B', 'A'));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Run with: node --import tsx/esm --test src/core/__tests__/human-surface-target.test.ts
|
|
2
2
|
//
|
|
3
|
-
// BUG REGRESSION: `crtr human ask|
|
|
3
|
+
// BUG REGRESSION: `crtr human ask|review|notify` surfaced its humanloop
|
|
4
4
|
// TUI in the backstage `crtr` session (the asking node's own pane) — a session
|
|
5
5
|
// the user never watches — because spawnAndDetach was called with no `-t`
|
|
6
6
|
// target. The fix routes the TUI to the HIGHEST FOCUSED node of the asking
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { test, before, after, beforeEach } from 'node:test';
|
|
9
9
|
import assert from 'node:assert/strict';
|
|
10
10
|
import { mkdtempSync, rmSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
11
|
-
import {
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
13
|
import { createNode, subscribe } from '../canvas/canvas.js';
|
|
14
14
|
import { reportsDir } from '../canvas/paths.js';
|
|
@@ -29,9 +29,7 @@ function node(id) {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
before(() => {
|
|
32
|
-
|
|
33
|
-
// exercise the tildify() collapse in reportHistoryLines.
|
|
34
|
-
home = mkdtempSync(join(homedir(), '.crtr-kickoff-'));
|
|
32
|
+
home = mkdtempSync(join(tmpdir(), 'crtr-kickoff-'));
|
|
35
33
|
process.env['CRTR_HOME'] = home;
|
|
36
34
|
});
|
|
37
35
|
beforeEach(() => {
|
|
@@ -76,10 +74,10 @@ test('the feed block frames awaiting workers as alive + auto-waking, so a fresh
|
|
|
76
74
|
test('a fresh revive is pointed at its subscriptions\' on-disk report history (catch-up bug)', () => {
|
|
77
75
|
// Regression for the fresh-revive catch-up bug: on a refresh-yield
|
|
78
76
|
// (resume:false) the old conversation is gone AND the inbox cursor has already
|
|
79
|
-
// advanced past everything drained pre-yield, so the revived node
|
|
80
|
-
//
|
|
81
|
-
// forever at reports/<ts>-<kind>.md; the kickoff
|
|
82
|
-
//
|
|
77
|
+
// advanced past everything drained pre-yield, so the revived node needs a
|
|
78
|
+
// durable catch-up breadcrumb for reports its subscriptions pushed BEFORE the
|
|
79
|
+
// yield. The bodies persist forever at reports/<ts>-<kind>.md; the kickoff
|
|
80
|
+
// surfaces a collapsed index with the newest ref and expansion commands.
|
|
83
81
|
const parent = createNode(node('p1'));
|
|
84
82
|
const worker = createNode(node('w1'));
|
|
85
83
|
subscribe(parent.node_id, worker.node_id);
|
|
@@ -91,17 +89,18 @@ test('a fresh revive is pointed at its subscriptions\' on-disk report history (c
|
|
|
91
89
|
writeFileSync(older, '---\n---\nearly progress', 'utf8');
|
|
92
90
|
writeFileSync(newer, '---\n---\nthe result', 'utf8');
|
|
93
91
|
const msg = buildReviveKickoff(parent, drainBearings(parent));
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
assert.ok(msg.includes(
|
|
101
|
-
assert.ok(msg.includes(
|
|
102
|
-
assert.ok(!msg.includes(older), 'absolute
|
|
103
|
-
assert.ok(msg.
|
|
104
|
-
|
|
92
|
+
// The feed block stays condensed: it reports that history exists and surfaces
|
|
93
|
+
// the newest report ref for immediate dereference, while older refs stay one
|
|
94
|
+
// command away instead of being preloaded into the revive prompt.
|
|
95
|
+
const olderRef = `${worker.node_id}:reports/20260608T120000-update.md`;
|
|
96
|
+
const newerRef = `${worker.node_id}:reports/20260608T130000-final.md`;
|
|
97
|
+
assert.ok(/Report history on disk \(collapsed\): 2 reports across 1 subscribed node/.test(msg));
|
|
98
|
+
assert.ok(msg.includes(`latest final: ${newerRef}`), 'newest report ref surfaced');
|
|
99
|
+
assert.ok(!msg.includes(olderRef), 'older report ref omitted from collapsed kickoff');
|
|
100
|
+
assert.ok(!msg.includes(older), 'absolute report paths not rendered');
|
|
101
|
+
assert.ok(!msg.includes(newer), 'absolute report paths not rendered');
|
|
102
|
+
assert.ok(/canvas history read <ref>/.test(msg), 'points at one-report expansion');
|
|
103
|
+
assert.ok(/canvas history show <node-id> --type report/.test(msg), 'points at full per-node expansion');
|
|
105
104
|
assert.ok(/feed read --all/.test(msg), 'points at the cursor-independent full-history replay');
|
|
106
105
|
});
|
|
107
106
|
test('buildReviveKickoff is pure — building twice eats nothing', () => {
|
|
@@ -26,7 +26,7 @@ import assert from 'node:assert/strict';
|
|
|
26
26
|
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
27
27
|
import { tmpdir } from 'node:os';
|
|
28
28
|
import { join } from 'node:path';
|
|
29
|
-
import { resolveMemoryDoc } from '../memory-resolver.js';
|
|
29
|
+
import { listAllMemoryDocs, resolveMemoryDoc } from '../memory-resolver.js';
|
|
30
30
|
import { resetScopeCache } from '../scope.js';
|
|
31
31
|
// Fixture: a temp PROJECT scope (a `.crouter/` dir discovered by walking up from
|
|
32
32
|
// cwd) holding both a native memory/ tree and one enabled fixture plugin whose
|
|
@@ -55,6 +55,9 @@ before(() => {
|
|
|
55
55
|
writeFileSync(join(pluginMem, 'widget.md'), doc('plugin-widget'));
|
|
56
56
|
writeFileSync(join(pluginMem, 'area', 'zone.md'), doc('plugin-zone'));
|
|
57
57
|
writeFileSync(join(pluginMem, 'shared.md'), doc('plugin-shared'));
|
|
58
|
+
writeFileSync(join(pluginMem, 'SKILL.md'), doc('legacy-plugin-skill'));
|
|
59
|
+
mkdirSync(join(memDir, 'legacy'), { recursive: true });
|
|
60
|
+
writeFileSync(join(memDir, 'legacy', 'SKILL.md'), doc('legacy-native-skill'));
|
|
58
61
|
// Plugin-root INDEX.md: a BARE plugin name must resolve this (mirrors the
|
|
59
62
|
// native bare-dir-name -> INDEX.md contract for the plugin mount root).
|
|
60
63
|
writeFileSync(join(pluginMem, 'INDEX.md'), doc('plugin-root-index'));
|
|
@@ -106,3 +109,11 @@ test('native doc wins over a plugin doc of the same canonical name', () => {
|
|
|
106
109
|
assert.ok(d.path.endsWith(join('memory', PLUGIN, 'shared.md')));
|
|
107
110
|
assert.ok(!d.path.includes(join('plugins', PLUGIN)));
|
|
108
111
|
});
|
|
112
|
+
test('legacy SKILL.md bundles are not memory docs', () => {
|
|
113
|
+
const names = listAllMemoryDocs('project').map((d) => d.name);
|
|
114
|
+
assert.ok(!names.includes('legacy'));
|
|
115
|
+
assert.ok(!names.includes(`${PLUGIN}/SKILL`));
|
|
116
|
+
assert.throws(() => resolveMemoryDoc('legacy'), /memory document not found/);
|
|
117
|
+
assert.throws(() => resolveMemoryDoc('legacy/SKILL'), /memory document not found/);
|
|
118
|
+
assert.throws(() => resolveMemoryDoc(`${PLUGIN}/SKILL`), /memory document not found/);
|
|
119
|
+
});
|
|
@@ -219,20 +219,55 @@ test('v4 is idempotent on re-open', () => {
|
|
|
219
219
|
assert.equal(userVersion(db2), MIGRATIONS.length);
|
|
220
220
|
assert.equal(edgeCount(db2), 1);
|
|
221
221
|
});
|
|
222
|
-
|
|
223
|
-
|
|
222
|
+
function buildV4DbWithLaterColumns() {
|
|
223
|
+
ensureHome();
|
|
224
|
+
const raw = new DatabaseSync(canvasDbPath());
|
|
225
|
+
raw.exec(`
|
|
226
|
+
CREATE TABLE nodes (
|
|
227
|
+
node_id TEXT PRIMARY KEY,
|
|
228
|
+
name TEXT NOT NULL,
|
|
229
|
+
kind TEXT NOT NULL,
|
|
230
|
+
mode TEXT NOT NULL DEFAULT 'base',
|
|
231
|
+
lifecycle TEXT NOT NULL DEFAULT 'terminal',
|
|
232
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
233
|
+
cwd TEXT NOT NULL,
|
|
234
|
+
parent TEXT,
|
|
235
|
+
created TEXT NOT NULL,
|
|
236
|
+
intent TEXT,
|
|
237
|
+
pi_pid INTEGER,
|
|
238
|
+
window TEXT,
|
|
239
|
+
tmux_session TEXT,
|
|
240
|
+
pane TEXT,
|
|
241
|
+
host_kind TEXT
|
|
242
|
+
);
|
|
243
|
+
CREATE TABLE edges (
|
|
244
|
+
type TEXT NOT NULL,
|
|
245
|
+
from_id TEXT NOT NULL,
|
|
246
|
+
to_id TEXT NOT NULL,
|
|
247
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
248
|
+
created TEXT NOT NULL,
|
|
249
|
+
PRIMARY KEY (type, from_id, to_id)
|
|
250
|
+
);
|
|
251
|
+
PRAGMA user_version = 4;
|
|
252
|
+
`);
|
|
253
|
+
raw
|
|
254
|
+
.prepare('INSERT INTO nodes (node_id, name, kind, status, cwd, created, pane, host_kind) VALUES (?,?,?,?,?,?,?,?)')
|
|
255
|
+
.run('stale', 'Stale', 'general', 'idle', '/tmp/work', '2020-01-01T00:00:00Z', '%9', 'broker');
|
|
256
|
+
return raw;
|
|
257
|
+
}
|
|
258
|
+
test('bug #99: a stale v4 db with later columns already present migrates cleanly', () => {
|
|
259
|
+
const raw = buildV4DbWithLaterColumns();
|
|
260
|
+
assert.equal(userVersion(raw), 4);
|
|
261
|
+
assert.equal(nodeColumns(raw).includes('pane'), true, 'precondition: pane already exists even though user_version is stale');
|
|
262
|
+
raw.close();
|
|
224
263
|
const db = openDb();
|
|
225
264
|
assert.equal(userVersion(db), MIGRATIONS.length);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
assert.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
assert.equal(
|
|
234
|
-
const cols = nodeColumns(db2);
|
|
235
|
-
for (const c of RUNTIME_COLUMNS) {
|
|
236
|
-
assert.ok(cols.includes(c), `expected nodes.${c} to persist`);
|
|
237
|
-
}
|
|
265
|
+
const cols = nodeColumns(db);
|
|
266
|
+
assert.ok(cols.includes('pane'), 'pane survives the migration');
|
|
267
|
+
assert.ok(cols.includes('host_kind'), 'host_kind survives the migration');
|
|
268
|
+
const row = db
|
|
269
|
+
.prepare('SELECT pane, host_kind FROM nodes WHERE node_id = ?')
|
|
270
|
+
.get('stale');
|
|
271
|
+
assert.equal(row.pane, '%9');
|
|
272
|
+
assert.equal(row.host_kind, 'broker');
|
|
238
273
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|