@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,73 @@
|
|
|
1
|
+
// connectivity.ts — "is the network reachable right now" for the daemon.
|
|
2
|
+
//
|
|
3
|
+
// Used by the daemon's connectivity-recovery pass to decide whether a node
|
|
4
|
+
// parked on a CONNECTION error should be nudged to continue. The signal the
|
|
5
|
+
// user thinks in terms of is "my wifi is back" — so this is a coarse internet-
|
|
6
|
+
// reachability probe, NOT a per-provider health check.
|
|
7
|
+
//
|
|
8
|
+
// Implementation: a fast TCP connect to a couple of well-known anycast resolvers
|
|
9
|
+
// on 443. RAW IPs (no DNS lookup) on purpose — DNS itself fails when the network
|
|
10
|
+
// is down, so resolving a hostname would conflate "wifi down" with "DNS down" and
|
|
11
|
+
// add latency; the IPs below are stable anycast addresses. Online = ANY target
|
|
12
|
+
// accepts a TCP connection within the timeout. Never throws.
|
|
13
|
+
//
|
|
14
|
+
// The broker calls this probe for connection-fault retries; tests can stub it
|
|
15
|
+
// directly where the retry loop is exercised, so the network state stays deterministic.
|
|
16
|
+
import { Socket } from 'node:net';
|
|
17
|
+
/** Anycast resolvers reachable from essentially any connected network. A single
|
|
18
|
+
* success means the box has working internet; we don't care WHICH target. */
|
|
19
|
+
const PROBE_TARGETS = [
|
|
20
|
+
{ host: '1.1.1.1', port: 443 }, // Cloudflare
|
|
21
|
+
{ host: '8.8.8.8', port: 443 }, // Google
|
|
22
|
+
];
|
|
23
|
+
/** Default per-target connect timeout. Short — a healthy network connects in
|
|
24
|
+
* tens of ms; a down network should be declared down quickly so the daemon tick
|
|
25
|
+
* isn't held up. */
|
|
26
|
+
export const PROBE_TIMEOUT_MS = 3000;
|
|
27
|
+
/** Resolve true iff a TCP connection to host:port completes within timeoutMs.
|
|
28
|
+
* Any error/timeout resolves false (never rejects). The socket is always torn
|
|
29
|
+
* down. Exported for unit testing against a local listener. */
|
|
30
|
+
export function tcpReachable(host, port, timeoutMs) {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const sock = new Socket();
|
|
33
|
+
let settled = false;
|
|
34
|
+
const done = (ok) => {
|
|
35
|
+
if (settled)
|
|
36
|
+
return;
|
|
37
|
+
settled = true;
|
|
38
|
+
try {
|
|
39
|
+
sock.destroy();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
/* already torn down */
|
|
43
|
+
}
|
|
44
|
+
resolve(ok);
|
|
45
|
+
};
|
|
46
|
+
sock.setTimeout(timeoutMs);
|
|
47
|
+
sock.once('connect', () => done(true));
|
|
48
|
+
sock.once('timeout', () => done(false));
|
|
49
|
+
sock.once('error', () => done(false));
|
|
50
|
+
try {
|
|
51
|
+
sock.connect(port, host);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
done(false);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/** True when the box can reach the internet (any PROBE_TARGET accepts a TCP
|
|
59
|
+
* connection within the timeout). The broker uses this for connection-fault
|
|
60
|
+
* retries. Never throws. */
|
|
61
|
+
export async function probeOnline(timeoutMs = PROBE_TIMEOUT_MS) {
|
|
62
|
+
try {
|
|
63
|
+
const results = await Promise.all(PROBE_TARGETS.map((t) => tcpReachable(t.host, t.port, timeoutMs)));
|
|
64
|
+
return results.some((ok) => ok);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Promise.all only rejects if a target promise rejects — they never do, but
|
|
68
|
+
// be defensive: an unexpected throw reads as "can't tell", and "can't tell"
|
|
69
|
+
// must never claim the network is UP (which would nudge nodes into a retry
|
|
70
|
+
// that fails again). Treat as offline.
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const CONNECTION_FAULT_RECOVERY_BODY = "The network connection is back online. Your previous turn stopped on a connection error (the network was down). Continue from where you left off and retry the work that failed.";
|
|
2
|
+
export declare const PROVIDER_FAULT_RECOVERY_BODY = "Your previous turn stopped on a provider fault. Continue from where you left off and retry the work that failed.";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export const CONNECTION_FAULT_RECOVERY_BODY = 'The network connection is back online. Your previous turn stopped on a connection error (the network was down). Continue from where you left off and retry the work that failed.';
|
|
2
|
+
export const PROVIDER_FAULT_RECOVERY_BODY = 'Your previous turn stopped on a provider fault. Continue from where you left off and retry the work that failed.';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Fault, FaultKind } from '../fault-classifier.js';
|
|
2
|
+
export type FaultRecoveryGate = 'online' | 'time' | 'none';
|
|
3
|
+
export interface FaultRecoveryPolicy {
|
|
4
|
+
kind: FaultKind;
|
|
5
|
+
gate: FaultRecoveryGate;
|
|
6
|
+
max: number;
|
|
7
|
+
baseDelayMs: number;
|
|
8
|
+
capDelayMs: number;
|
|
9
|
+
fatal: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface FaultRetryPlan {
|
|
12
|
+
fault: Fault;
|
|
13
|
+
attempt: number;
|
|
14
|
+
max: number;
|
|
15
|
+
nextAt: string;
|
|
16
|
+
delayMs: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function policyFor(kind: FaultKind): FaultRecoveryPolicy;
|
|
19
|
+
export declare function nextFaultRetry(fault: Fault, now: number, opts?: {
|
|
20
|
+
retryAfterMs?: number | null;
|
|
21
|
+
jitterMs?: (fault: Fault, attempt: number) => number;
|
|
22
|
+
}): FaultRetryPlan | null;
|
|
23
|
+
export declare function markFatalOnExhaust(fault: Fault): Fault;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const POLICIES = {
|
|
2
|
+
connection: { kind: 'connection', gate: 'online', max: 8, baseDelayMs: 5_000, capDelayMs: 60_000, fatal: false },
|
|
3
|
+
'rate-limit': { kind: 'rate-limit', gate: 'time', max: 6, baseDelayMs: 30_000, capDelayMs: 5 * 60_000, fatal: false },
|
|
4
|
+
overloaded: { kind: 'overloaded', gate: 'time', max: 6, baseDelayMs: 10_000, capDelayMs: 2 * 60_000, fatal: false },
|
|
5
|
+
auth: { kind: 'auth', gate: 'none', max: 0, baseDelayMs: 0, capDelayMs: 0, fatal: true },
|
|
6
|
+
protocol: { kind: 'protocol', gate: 'none', max: 0, baseDelayMs: 0, capDelayMs: 0, fatal: true },
|
|
7
|
+
other: { kind: 'other', gate: 'none', max: 0, baseDelayMs: 0, capDelayMs: 0, fatal: true },
|
|
8
|
+
};
|
|
9
|
+
export function policyFor(kind) {
|
|
10
|
+
return POLICIES[kind];
|
|
11
|
+
}
|
|
12
|
+
function clampDelay(delayMs, capMs) {
|
|
13
|
+
if (!Number.isFinite(delayMs) || delayMs <= 0)
|
|
14
|
+
return 0;
|
|
15
|
+
return Math.min(Math.max(1, Math.round(delayMs)), capMs);
|
|
16
|
+
}
|
|
17
|
+
function defaultOverloadedJitterMs(fault, attempt) {
|
|
18
|
+
const seed = `${fault.link}|${fault.kind}|${fault.message}|${fault.since}|${attempt}`;
|
|
19
|
+
let hash = 0;
|
|
20
|
+
for (let i = 0; i < seed.length; i++) {
|
|
21
|
+
hash = (hash * 33 + seed.charCodeAt(i)) >>> 0;
|
|
22
|
+
}
|
|
23
|
+
return hash % 1_000;
|
|
24
|
+
}
|
|
25
|
+
function retryDelayMs(fault, attempt, opts) {
|
|
26
|
+
const policy = policyFor(fault.kind);
|
|
27
|
+
if (policy.fatal)
|
|
28
|
+
return 0;
|
|
29
|
+
if (fault.kind === 'rate-limit' && typeof opts?.retryAfterMs === 'number' && Number.isFinite(opts.retryAfterMs)) {
|
|
30
|
+
return clampDelay(opts.retryAfterMs, policy.capDelayMs);
|
|
31
|
+
}
|
|
32
|
+
const delay = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);
|
|
33
|
+
const jitter = fault.kind === 'overloaded'
|
|
34
|
+
? Math.max(0, Math.round(opts?.jitterMs?.(fault, attempt) ?? defaultOverloadedJitterMs(fault, attempt)))
|
|
35
|
+
: 0;
|
|
36
|
+
return clampDelay(delay + jitter, policy.capDelayMs);
|
|
37
|
+
}
|
|
38
|
+
export function nextFaultRetry(fault, now, opts) {
|
|
39
|
+
const policy = policyFor(fault.kind);
|
|
40
|
+
if (policy.fatal)
|
|
41
|
+
return null;
|
|
42
|
+
const attempt = (fault.retry.attempt ?? 0) + 1;
|
|
43
|
+
if (attempt > policy.max)
|
|
44
|
+
return null;
|
|
45
|
+
const delayMs = retryDelayMs(fault, attempt, opts);
|
|
46
|
+
return {
|
|
47
|
+
fault: {
|
|
48
|
+
...fault,
|
|
49
|
+
retry: {
|
|
50
|
+
...fault.retry,
|
|
51
|
+
disposition: 'auto',
|
|
52
|
+
by: fault.retry.by ?? 'daemon',
|
|
53
|
+
attempt,
|
|
54
|
+
max: policy.max,
|
|
55
|
+
nextAt: new Date(now + delayMs).toISOString(),
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
attempt,
|
|
59
|
+
max: policy.max,
|
|
60
|
+
nextAt: new Date(now + delayMs).toISOString(),
|
|
61
|
+
delayMs,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function markFatalOnExhaust(fault) {
|
|
65
|
+
const retry = {
|
|
66
|
+
...fault.retry,
|
|
67
|
+
disposition: 'fatal',
|
|
68
|
+
by: fault.retry.by ?? 'daemon',
|
|
69
|
+
max: policyFor(fault.kind).max,
|
|
70
|
+
attempt: fault.retry.attempt ?? 0,
|
|
71
|
+
};
|
|
72
|
+
delete retry.nextAt;
|
|
73
|
+
return { ...fault, retry };
|
|
74
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Fault, type FaultKind, type FaultLink, type FaultRetry } from '../fault-classifier.js';
|
|
2
|
+
export type { Fault, FaultKind, FaultLink, FaultRetry, FaultRetryDisposition, FaultRetryOwner } from '../fault-classifier.js';
|
|
3
|
+
export declare const FAULT_QUIET_MS: number;
|
|
4
|
+
export interface FaultRecordInput {
|
|
5
|
+
link: FaultLink;
|
|
6
|
+
op: string;
|
|
7
|
+
kind: FaultKind;
|
|
8
|
+
retry: FaultRetry;
|
|
9
|
+
message: string;
|
|
10
|
+
logPath?: string;
|
|
11
|
+
since?: string;
|
|
12
|
+
anchorEntryId?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function recordFault(nodeId: string, input: FaultRecordInput): void;
|
|
15
|
+
export declare function recordFaultForCurrentNode(input: FaultRecordInput): boolean;
|
|
16
|
+
export declare function clearFault(nodeId: string, opts?: {
|
|
17
|
+
link?: FaultLink;
|
|
18
|
+
}): boolean;
|
|
19
|
+
export declare function clearFaultForCurrentNode(opts?: {
|
|
20
|
+
link?: FaultLink;
|
|
21
|
+
}): boolean;
|
|
22
|
+
export declare function readFault(nodeId: string): Fault | null;
|
|
23
|
+
export declare function readFaultAsync(nodeId: string): Promise<Fault | null>;
|
|
24
|
+
/** True when a raw engine error is the provider 404 "model missing" signal. */
|
|
25
|
+
export declare function isModelNotFoundError(message: string): boolean;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { jobDir } from '../canvas/paths.js';
|
|
4
|
+
import { appendLogLine, formatLogLine } from '../log.js';
|
|
5
|
+
export const FAULT_QUIET_MS = 5 * 60_000;
|
|
6
|
+
const MESSAGE_CAP = 500;
|
|
7
|
+
const DEFAULT_OP = 'generation turn';
|
|
8
|
+
const DEFAULT_LOG_BASENAME = 'fault.log';
|
|
9
|
+
function faultPath(nodeId) {
|
|
10
|
+
return join(jobDir(nodeId), 'fault');
|
|
11
|
+
}
|
|
12
|
+
function legacyFaultPath(nodeId) {
|
|
13
|
+
return join(jobDir(nodeId), 'error-stall');
|
|
14
|
+
}
|
|
15
|
+
function defaultLogPath(nodeId) {
|
|
16
|
+
return join(jobDir(nodeId), DEFAULT_LOG_BASENAME);
|
|
17
|
+
}
|
|
18
|
+
function clampMessage(message) {
|
|
19
|
+
return message.slice(0, MESSAGE_CAP);
|
|
20
|
+
}
|
|
21
|
+
function faultPriority(fault) {
|
|
22
|
+
if (fault.link === 'pi→provider' && fault.retry.disposition === 'auto' && fault.retry.by === 'daemon')
|
|
23
|
+
return 3;
|
|
24
|
+
if (fault.retry.disposition === 'fatal')
|
|
25
|
+
return 2;
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
function parseFault(raw, nodeId, legacy = false) {
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(raw);
|
|
31
|
+
const kind = parsed.kind;
|
|
32
|
+
const message = typeof parsed.message === 'string' ? parsed.message : undefined;
|
|
33
|
+
const since = typeof parsed.since === 'string' ? parsed.since : undefined;
|
|
34
|
+
if (kind === undefined || message === undefined || since === undefined)
|
|
35
|
+
return null;
|
|
36
|
+
const link = parsed.link ?? (legacy ? 'pi→provider' : undefined);
|
|
37
|
+
const retryRaw = (parsed.retry ?? {});
|
|
38
|
+
const retry = {
|
|
39
|
+
disposition: retryRaw.disposition === 'auto' || retryRaw.disposition === 'manual' || retryRaw.disposition === 'fatal'
|
|
40
|
+
? retryRaw.disposition
|
|
41
|
+
: legacy
|
|
42
|
+
? 'auto'
|
|
43
|
+
: 'manual',
|
|
44
|
+
...(retryRaw.by === 'sdk' || retryRaw.by === 'daemon' || retryRaw.by === 'client' ? { by: retryRaw.by } : legacy ? { by: 'daemon' } : {}),
|
|
45
|
+
...(typeof retryRaw.attempt === 'number' ? { attempt: retryRaw.attempt } : {}),
|
|
46
|
+
...(typeof retryRaw.max === 'number' ? { max: retryRaw.max } : {}),
|
|
47
|
+
...(typeof retryRaw.nextAt === 'string' ? { nextAt: retryRaw.nextAt } : {}),
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
link: link ?? 'pi→provider',
|
|
51
|
+
op: typeof parsed.op === 'string' && parsed.op !== '' ? parsed.op : DEFAULT_OP,
|
|
52
|
+
kind,
|
|
53
|
+
retry,
|
|
54
|
+
message,
|
|
55
|
+
since,
|
|
56
|
+
logPath: typeof parsed.logPath === 'string' && parsed.logPath !== '' ? parsed.logPath : defaultLogPath(nodeId),
|
|
57
|
+
...(typeof parsed.anchorEntryId === 'string' && parsed.anchorEntryId !== '' ? { anchorEntryId: parsed.anchorEntryId } : {}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function readFaultFromPath(path, nodeId, legacy = false) {
|
|
65
|
+
try {
|
|
66
|
+
return parseFault(readFileSync(path, 'utf8'), nodeId, legacy);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function currentFault(nodeId) {
|
|
73
|
+
return readFaultFromPath(faultPath(nodeId), nodeId) ?? readFaultFromPath(legacyFaultPath(nodeId), nodeId, true);
|
|
74
|
+
}
|
|
75
|
+
function writeFault(nodeId, fault) {
|
|
76
|
+
try {
|
|
77
|
+
mkdirSync(jobDir(nodeId), { recursive: true });
|
|
78
|
+
writeFileSync(faultPath(nodeId), JSON.stringify(fault));
|
|
79
|
+
rmSync(legacyFaultPath(nodeId), { force: true });
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
/* best-effort */
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function updateMarker(nodeId, fault) {
|
|
86
|
+
const current = currentFault(nodeId);
|
|
87
|
+
if (current !== null) {
|
|
88
|
+
if (current.link === fault.link && current.kind === fault.kind) {
|
|
89
|
+
const retry = { ...current.retry, ...fault.retry };
|
|
90
|
+
if (fault.retry.disposition === 'fatal' && !('nextAt' in fault.retry)) {
|
|
91
|
+
delete retry.nextAt;
|
|
92
|
+
}
|
|
93
|
+
fault = { ...fault, since: current.since, anchorEntryId: current.anchorEntryId ?? fault.anchorEntryId, retry };
|
|
94
|
+
}
|
|
95
|
+
else if (faultPriority(current) > faultPriority(fault)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
writeFault(nodeId, fault);
|
|
100
|
+
}
|
|
101
|
+
function appendDurableLog(fault) {
|
|
102
|
+
const line = formatLogLine(fault.retry.disposition === 'fatal' ? 'warn' : 'info', 'fault', fault.message, {
|
|
103
|
+
link: fault.link,
|
|
104
|
+
op: fault.op,
|
|
105
|
+
kind: fault.kind,
|
|
106
|
+
retry: fault.retry,
|
|
107
|
+
since: fault.since,
|
|
108
|
+
logPath: fault.logPath,
|
|
109
|
+
anchorEntryId: fault.anchorEntryId,
|
|
110
|
+
});
|
|
111
|
+
appendLogLine(fault.logPath, line);
|
|
112
|
+
}
|
|
113
|
+
export function recordFault(nodeId, input) {
|
|
114
|
+
const fault = {
|
|
115
|
+
link: input.link,
|
|
116
|
+
op: input.op,
|
|
117
|
+
kind: input.kind,
|
|
118
|
+
retry: input.retry,
|
|
119
|
+
message: clampMessage(input.message),
|
|
120
|
+
since: input.since ?? new Date().toISOString(),
|
|
121
|
+
logPath: input.logPath ?? defaultLogPath(nodeId),
|
|
122
|
+
...(typeof input.anchorEntryId === 'string' && input.anchorEntryId !== '' ? { anchorEntryId: input.anchorEntryId } : {}),
|
|
123
|
+
};
|
|
124
|
+
appendDurableLog(fault);
|
|
125
|
+
updateMarker(nodeId, fault);
|
|
126
|
+
}
|
|
127
|
+
export function recordFaultForCurrentNode(input) {
|
|
128
|
+
const nodeId = process.env['CRTR_NODE_ID'];
|
|
129
|
+
if (nodeId === undefined || nodeId.trim() === '')
|
|
130
|
+
return false;
|
|
131
|
+
if (!existsSync(jobDir(nodeId)))
|
|
132
|
+
return false;
|
|
133
|
+
try {
|
|
134
|
+
recordFault(nodeId, input);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export function clearFault(nodeId, opts) {
|
|
142
|
+
try {
|
|
143
|
+
const current = currentFault(nodeId);
|
|
144
|
+
if (current !== null && opts?.link !== undefined && current.link !== opts.link)
|
|
145
|
+
return false;
|
|
146
|
+
rmSync(faultPath(nodeId), { force: true });
|
|
147
|
+
rmSync(legacyFaultPath(nodeId), { force: true });
|
|
148
|
+
return current !== null;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export function clearFaultForCurrentNode(opts) {
|
|
155
|
+
const nodeId = process.env['CRTR_NODE_ID'];
|
|
156
|
+
if (nodeId === undefined || nodeId.trim() === '')
|
|
157
|
+
return false;
|
|
158
|
+
if (!existsSync(jobDir(nodeId)))
|
|
159
|
+
return false;
|
|
160
|
+
return clearFault(nodeId, opts);
|
|
161
|
+
}
|
|
162
|
+
export function readFault(nodeId) {
|
|
163
|
+
return currentFault(nodeId);
|
|
164
|
+
}
|
|
165
|
+
export async function readFaultAsync(nodeId) {
|
|
166
|
+
return readFault(nodeId);
|
|
167
|
+
}
|
|
168
|
+
/** True when a raw engine error is the provider 404 "model missing" signal. */
|
|
169
|
+
export function isModelNotFoundError(message) {
|
|
170
|
+
const m = message ?? '';
|
|
171
|
+
if (/not_found_error/i.test(m))
|
|
172
|
+
return true;
|
|
173
|
+
if (/model[^.]{0,40}(not\s*found|does not exist|is not available|unavailable)|(no such|unknown) model/i.test(m))
|
|
174
|
+
return true;
|
|
175
|
+
return /\b404\b/.test(m) && /not\s*found|not available|does not exist/i.test(m);
|
|
176
|
+
}
|
|
@@ -7,4 +7,4 @@ export declare const FRONT_DOOR_ENV = "CRTR_FRONT_DOOR";
|
|
|
7
7
|
/** If this invocation is a front-door (root) launch, boot it and never return.
|
|
8
8
|
* Returns false when it's a recognized subcommand / help / unknown token (let
|
|
9
9
|
* the dispatcher handle it — for unknown tokens it errors cleanly). */
|
|
10
|
-
export declare function maybeBootRoot(root: RootDef, argv: string[]): boolean
|
|
10
|
+
export declare function maybeBootRoot(root: RootDef, argv: string[]): Promise<boolean>;
|
|
@@ -63,7 +63,7 @@ export const FRONT_DOOR_ENV = 'CRTR_FRONT_DOOR';
|
|
|
63
63
|
/** If this invocation is a front-door (root) launch, boot it and never return.
|
|
64
64
|
* Returns false when it's a recognized subcommand / help / unknown token (let
|
|
65
65
|
* the dispatcher handle it — for unknown tokens it errors cleanly). */
|
|
66
|
-
export function maybeBootRoot(root, argv) {
|
|
66
|
+
export async function maybeBootRoot(root, argv) {
|
|
67
67
|
const tokens = argv.slice(2);
|
|
68
68
|
const first = tokens[0];
|
|
69
69
|
// Recursion guard: never boot a root from inside a front-door-booted pi.
|
|
@@ -99,6 +99,6 @@ export function maybeBootRoot(root, argv) {
|
|
|
99
99
|
// Unambiguous front-door launch → boot a resident root inline (exec pi in
|
|
100
100
|
// this terminal). Does not return.
|
|
101
101
|
const args = parseRootArgs(tokens);
|
|
102
|
-
bootRoot({ ...args });
|
|
102
|
+
await bootRoot({ ...args });
|
|
103
103
|
return true;
|
|
104
104
|
}
|
|
@@ -15,6 +15,12 @@ export interface HostHandle {
|
|
|
15
15
|
* detached child reports its pid; the broker re-records pi_pid during its
|
|
16
16
|
* extension bind, and reviveNode clearPid()s right after launch. */
|
|
17
17
|
pid: number | null;
|
|
18
|
+
/** Real child-exit signal from the launch handle. Resolves when the detached
|
|
19
|
+
* broker exits, so readiness waits can fail fast without pid probes. */
|
|
20
|
+
exited: Promise<{
|
|
21
|
+
code: number | null;
|
|
22
|
+
signal: NodeJS.Signals | null;
|
|
23
|
+
}>;
|
|
18
24
|
}
|
|
19
25
|
export interface Host {
|
|
20
26
|
/** Bring a node's ENGINE into existence from its launch recipe; return a
|
|
@@ -30,4 +36,8 @@ export interface Host {
|
|
|
30
36
|
* reaches around the abstraction. */
|
|
31
37
|
signal(nodeId: string, sig: NodeJS.Signals): void;
|
|
32
38
|
}
|
|
39
|
+
/** Validate the broker launch recipe before we detach a child process. This is
|
|
40
|
+
* the fast-fail seam for known module/path errors: a bad engine resolve or a
|
|
41
|
+
* missing plain session path throws here, before spawn(). */
|
|
42
|
+
export declare function preflightBrokerLaunch(nodeId: string, inv: PiInvocation, cwd: string): void;
|
|
33
43
|
export declare const headlessBrokerHost: Host;
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
// needs no tmux verb at all (it supervises via the broker pid, tears down over
|
|
12
12
|
// the node's unix socket).
|
|
13
13
|
import { spawn } from 'node:child_process';
|
|
14
|
-
import { mkdirSync, writeFileSync, existsSync, unlinkSync, openSync, closeSync } from 'node:fs';
|
|
15
|
-
import { dirname, join } from 'node:path';
|
|
14
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync, unlinkSync, openSync, closeSync } from 'node:fs';
|
|
15
|
+
import { dirname, join, resolve, isAbsolute } from 'node:path';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { connect } from 'node:net';
|
|
18
18
|
import { getNode } from '../canvas/index.js';
|
|
19
|
-
import { nodeDir, jobDir } from '../canvas/paths.js';
|
|
19
|
+
import { nodeDir, jobDir, viewSocketPath } from '../canvas/paths.js';
|
|
20
20
|
import { encodeFrame } from './broker-protocol.js';
|
|
21
21
|
import { FRONT_DOOR_ENV } from './front-door.js';
|
|
22
22
|
import { hostExecPath } from './branded-host.js';
|
|
@@ -34,15 +34,66 @@ function resolveBrokerEntry() {
|
|
|
34
34
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
35
35
|
return join(here, 'broker-cli.js');
|
|
36
36
|
}
|
|
37
|
+
const DEFAULT_BROKER_ENGINE = '@earendil-works/pi-coding-agent';
|
|
38
|
+
function isPlainLaunchSessionPath(token) {
|
|
39
|
+
return token.includes('/') || token.endsWith('.jsonl');
|
|
40
|
+
}
|
|
41
|
+
function resolvePlainLaunchPath(token, cwd) {
|
|
42
|
+
return isAbsolute(token) ? token : resolve(cwd, token);
|
|
43
|
+
}
|
|
44
|
+
function preflightLaunchSessionArg(nodeId, cwd, flag, value) {
|
|
45
|
+
if (flag === '--session' && !isPlainLaunchSessionPath(value)) {
|
|
46
|
+
throw new Error(`[broker] refusing to launch ${nodeId}: launch recipe passes bare --session id '${value}' — the broker fatally rejects bare ids; pass the .jsonl path instead.`);
|
|
47
|
+
}
|
|
48
|
+
if (!isPlainLaunchSessionPath(value))
|
|
49
|
+
return;
|
|
50
|
+
const sessionPath = resolvePlainLaunchPath(value, cwd);
|
|
51
|
+
if (!existsSync(sessionPath)) {
|
|
52
|
+
const label = flag === '--session' ? 'session file' : 'fork source';
|
|
53
|
+
throw new Error(`[broker] refusing to launch ${nodeId}: launch recipe references missing ${label} '${value}' (${sessionPath})`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Validate the broker launch recipe before we detach a child process. This is
|
|
57
|
+
* the fast-fail seam for known module/path errors: a bad engine resolve or a
|
|
58
|
+
* missing plain session path throws here, before spawn(). */
|
|
59
|
+
export function preflightBrokerLaunch(nodeId, inv, cwd) {
|
|
60
|
+
const dir = nodeDir(nodeId);
|
|
61
|
+
mkdirSync(dir, { recursive: true });
|
|
62
|
+
const launchPath = join(dir, 'broker-launch.json');
|
|
63
|
+
writeFileSync(launchPath, JSON.stringify(inv));
|
|
64
|
+
const raw = readFileSync(launchPath, 'utf8');
|
|
65
|
+
const recipe = JSON.parse(raw);
|
|
66
|
+
if (!Array.isArray(recipe.argv)) {
|
|
67
|
+
throw new Error(`[broker] refusing to launch ${nodeId}: broker-launch.json is missing the argv array`);
|
|
68
|
+
}
|
|
69
|
+
const engineSpec = inv.env['CRTR_BROKER_ENGINE'] ?? process.env['CRTR_BROKER_ENGINE'] ?? DEFAULT_BROKER_ENGINE;
|
|
70
|
+
try {
|
|
71
|
+
const resolvedEngine = import.meta.resolve(engineSpec);
|
|
72
|
+
if (resolvedEngine.startsWith('file:') && !existsSync(fileURLToPath(resolvedEngine))) {
|
|
73
|
+
throw new Error(`resolved to missing file ${resolvedEngine}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
throw new Error(`[broker] refusing to launch ${nodeId}: cannot resolve broker engine '${engineSpec}' from ${resolveBrokerEntry()} — ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
const argv = recipe.argv;
|
|
80
|
+
for (let i = 0; i < argv.length; i++) {
|
|
81
|
+
const tok = argv[i];
|
|
82
|
+
if (tok !== '--session' && tok !== '--fork')
|
|
83
|
+
continue;
|
|
84
|
+
const value = argv[++i];
|
|
85
|
+
if (value === undefined)
|
|
86
|
+
continue;
|
|
87
|
+
preflightLaunchSessionArg(nodeId, cwd, tok, value);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
37
90
|
/** How long teardown waits after sending the graceful `shutdown` frame before
|
|
38
91
|
* SIGTERMing a broker that connected but never exited (e.g. a hung dispose()). */
|
|
39
92
|
const BROKER_SHUTDOWN_GRACE_MS = 2_000;
|
|
40
93
|
export const headlessBrokerHost = {
|
|
41
94
|
launch(nodeId, inv, opts) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// The broker reads this recipe back via runBroker(nodeId) → broker-launch.json.
|
|
45
|
-
writeFileSync(join(dir, 'broker-launch.json'), JSON.stringify(inv));
|
|
95
|
+
// Fast-fail known launch errors before we detach a broker process.
|
|
96
|
+
preflightBrokerLaunch(nodeId, inv, opts.cwd);
|
|
46
97
|
// Redirect the detached broker's stdout+stderr to a per-node log under the
|
|
47
98
|
// node's existing job/ dir (alongside log.jsonl / telemetry.json). Without
|
|
48
99
|
// this (stdio:'ignore') EVERY broker diagnostic — the version-skew warning,
|
|
@@ -64,8 +115,19 @@ export const headlessBrokerHost = {
|
|
|
64
115
|
// The child holds its own dup of the fd; release the parent's copy so the
|
|
65
116
|
// launching process (CLI or daemon) never leaks it.
|
|
66
117
|
closeSync(logFd);
|
|
118
|
+
const exited = new Promise((resolve) => {
|
|
119
|
+
let settled = false;
|
|
120
|
+
const finish = (state) => {
|
|
121
|
+
if (settled)
|
|
122
|
+
return;
|
|
123
|
+
settled = true;
|
|
124
|
+
resolve(state);
|
|
125
|
+
};
|
|
126
|
+
child.once('exit', (code, signal) => finish({ code, signal }));
|
|
127
|
+
child.once('error', () => finish({ code: null, signal: null }));
|
|
128
|
+
});
|
|
67
129
|
child.unref();
|
|
68
|
-
return { pid: child.pid ?? null };
|
|
130
|
+
return { pid: child.pid ?? null, exited };
|
|
69
131
|
},
|
|
70
132
|
isAlive(node) {
|
|
71
133
|
return isPidAlive((typeof node === 'string' ? getNode(node) : node)?.pi_pid);
|
|
@@ -76,7 +138,7 @@ export const headlessBrokerHost = {
|
|
|
76
138
|
// flipped done/canceled by the caller (crash-safe ordering), so the daemon
|
|
77
139
|
// won't revive. On connect failure (broker dead/crashed) fall back to a
|
|
78
140
|
// SIGTERM of the broker pid + unlink the stale socket.
|
|
79
|
-
const sockPath =
|
|
141
|
+
const sockPath = viewSocketPath(nodeId);
|
|
80
142
|
const cleanupSocket = () => {
|
|
81
143
|
try {
|
|
82
144
|
if (existsSync(sockPath))
|