@north-light/crouter 0.3.170 → 0.3.172
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +37 -11
- package/dist/api/client.js +84 -15
- package/dist/api/dto/human.d.ts +53 -39
- package/dist/api/dto/human.js +2 -3
- package/dist/api/dto/inbox.d.ts +0 -1
- package/dist/api/dto/nodes.d.ts +12 -0
- package/dist/api/dto/review-comments.d.ts +137 -0
- package/dist/api/dto/review-comments.js +5 -0
- package/dist/api/dto/reviews.d.ts +109 -0
- package/dist/api/dto/reviews.js +5 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/routes.d.ts +17 -4
- package/dist/api/routes.js +21 -5
- package/dist/build-root.d.ts +1 -16
- package/dist/build-root.js +1 -25
- package/dist/builtin-memory/00-runtime-base.md +7 -5
- package/dist/builtin-memory/04-base-worker.md +1 -1
- package/dist/builtin-memory/04-orchestration-kernel.md +7 -7
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/review/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +20 -0
- package/dist/builtin-memory/05-kinds/spec/00-base.md +3 -7
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +3 -3
- package/dist/builtin-memory/05-kinds/spec/requirements.md +3 -3
- package/dist/builtin-memory/design.md +2 -2
- package/dist/builtin-memory/insights/capture.md +50 -0
- package/dist/builtin-memory/insights/init.md +45 -0
- package/dist/builtin-memory/insights/listen.md +9 -0
- package/dist/builtin-memory/internal/agent-shaping.md +2 -2
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/plugins.md +12 -11
- package/dist/builtin-memory/internal/storage-tiers.md +2 -2
- package/dist/builtin-memory/{planning.md → plan/roadmap.md} +3 -3
- package/dist/builtin-memory/plan.md +19 -0
- package/dist/builtin-memory/spec/guide.md +38 -0
- package/dist/builtin-memory/spec/requirements.md +31 -0
- package/dist/builtin-memory/spec/roadmap.md +35 -0
- package/dist/builtin-memory/spec.md +10 -86
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +3 -4
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +49 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +56 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +920 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +221 -156
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +4 -5
- package/dist/cli.js +2 -6
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +7 -35
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +32 -16
- package/dist/clients/attach/__tests__/context-message.test.js +142 -6
- package/dist/clients/attach/__tests__/crtr-output.test.js +29 -45
- package/dist/clients/attach/__tests__/diagram.test.js +1 -1
- package/dist/clients/attach/__tests__/file-review-focus.test.js +48 -0
- package/dist/clients/attach/__tests__/group-activity.test.js +266 -0
- package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +32 -0
- package/dist/clients/attach/__tests__/session-identity-refresh.test.js +32 -0
- package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -2
- package/dist/clients/attach/chrome/header.d.ts +10 -0
- package/dist/clients/attach/chrome/header.js +30 -0
- package/dist/clients/attach/chrome/recap.d.ts +7 -0
- package/dist/clients/attach/chrome/recap.js +19 -0
- package/dist/clients/attach/chrome/status-line.d.ts +3 -0
- package/dist/clients/attach/chrome/status-line.js +5 -0
- package/dist/clients/attach/chrome/widgets.js +11 -21
- package/dist/clients/attach/command.js +11 -7
- package/dist/clients/attach/config.d.ts +4 -0
- package/dist/clients/attach/config.js +3 -0
- package/dist/clients/attach/input/controller.d.ts +19 -0
- package/dist/clients/attach/input/controller.js +62 -28
- package/dist/clients/attach/input/titled-editor.d.ts +0 -4
- package/dist/clients/attach/input/titled-editor.js +2 -10
- package/dist/clients/attach/overlays/file-review.d.ts +9 -5
- package/dist/clients/attach/overlays/file-review.js +9 -70
- package/dist/clients/attach/overlays/help.d.ts +33 -0
- package/dist/clients/attach/overlays/help.js +204 -0
- package/dist/clients/attach/render/activity-slot.d.ts +4 -0
- package/dist/clients/attach/render/activity-slot.js +13 -0
- package/dist/clients/attach/render/chat-view.d.ts +117 -12
- package/dist/clients/attach/render/chat-view.js +604 -135
- package/dist/clients/attach/render/context-message.d.ts +2 -1
- package/dist/clients/attach/render/context-message.js +17 -13
- package/dist/clients/attach/render/crtr-output.js +1 -3
- package/dist/clients/attach/render/diagram.js +2 -2
- package/dist/clients/attach/render/edit-diff.d.ts +4 -1
- package/dist/clients/attach/render/edit-diff.js +7 -11
- package/dist/clients/attach/render/group-activity.d.ts +32 -0
- package/dist/clients/attach/render/group-activity.js +206 -0
- package/dist/clients/attach/render/group-recap.d.ts +10 -0
- package/dist/clients/attach/render/group-recap.js +93 -0
- package/dist/clients/attach/render/html-markdown.d.ts +17 -0
- package/dist/clients/attach/render/html-markdown.js +228 -0
- package/dist/clients/attach/render/measured-container.d.ts +96 -0
- package/dist/clients/attach/render/measured-container.js +322 -0
- package/dist/clients/attach/render/scroll-animation.d.ts +8 -0
- package/dist/clients/attach/render/scroll-animation.js +80 -0
- package/dist/clients/attach/render/tool-calls.js +6 -6
- package/dist/clients/attach/render/transcript-copy.d.ts +9 -0
- package/dist/clients/attach/render/transcript-copy.js +81 -0
- package/dist/clients/attach/render/viewport.d.ts +29 -0
- package/dist/clients/attach/render/viewport.js +159 -0
- package/dist/clients/attach/session/bindings.d.ts +10 -7
- package/dist/clients/attach/session/bindings.js +23 -8
- package/dist/clients/attach/session/connection.js +4 -5
- package/dist/clients/attach/session/context.d.ts +6 -4
- package/dist/clients/attach/session/frame.d.ts +34 -0
- package/dist/clients/attach/session/frame.js +176 -0
- package/dist/clients/attach/session/frames.d.ts +4 -1
- package/dist/clients/attach/session/frames.js +14 -1
- package/dist/clients/attach/session/identity.js +4 -3
- package/dist/clients/attach/session/input-wiring.d.ts +14 -4
- package/dist/clients/attach/session/input-wiring.js +15 -13
- package/dist/clients/attach/session/invariant.d.ts +9 -0
- package/dist/clients/attach/session/invariant.js +15 -0
- package/dist/clients/attach/session/keys.d.ts +10 -2
- package/dist/clients/attach/session/keys.js +13 -5
- package/dist/clients/attach/session/layout.d.ts +10 -8
- package/dist/clients/attach/session/layout.js +33 -34
- package/dist/clients/attach/session/mouse.d.ts +18 -0
- package/dist/clients/attach/session/mouse.js +64 -0
- package/dist/clients/attach/session/pane-tag.d.ts +3 -0
- package/dist/clients/attach/session/pane-tag.js +51 -8
- package/dist/clients/attach/session/reconnect.d.ts +3 -0
- package/dist/clients/attach/session/reconnect.js +42 -5
- package/dist/clients/attach/session/surface-frame.d.ts +20 -0
- package/dist/clients/attach/session/surface-frame.js +73 -0
- package/dist/clients/attach/session/surface-host.d.ts +64 -0
- package/dist/clients/attach/session/surface-host.js +255 -0
- package/dist/clients/attach/session/whip.d.ts +17 -0
- package/dist/clients/attach/session/whip.js +334 -0
- package/dist/clients/attach/slash/dispatch.d.ts +13 -1
- package/dist/clients/attach/slash/dispatch.js +51 -29
- package/dist/clients/attach/viewer.js +907 -819
- package/dist/clients/conversation/conversation.d.ts +65 -0
- package/dist/clients/conversation/conversation.js +318 -0
- package/dist/clients/conversation/frames.d.ts +30 -0
- package/dist/clients/conversation/frames.js +52 -0
- package/dist/clients/conversation/region.d.ts +51 -0
- package/dist/clients/conversation/region.js +265 -0
- package/dist/clients/conversation/submit.d.ts +38 -0
- package/dist/clients/conversation/submit.js +51 -0
- package/dist/clients/inbox/__tests__/inbox-controller.test.js +247 -0
- package/dist/clients/inbox/__tests__/mount-panel.test.js +776 -0
- package/dist/clients/inbox/controller.d.ts +79 -0
- package/dist/clients/inbox/controller.js +448 -0
- package/dist/clients/inbox/deck-adapter.d.ts +29 -0
- package/dist/clients/inbox/deck-adapter.js +62 -0
- package/dist/clients/inbox/layout.d.ts +8 -0
- package/dist/clients/inbox/layout.js +9 -0
- package/dist/clients/inbox/resolve.d.ts +10 -0
- package/dist/clients/inbox/resolve.js +34 -0
- package/dist/clients/inbox/review/__tests__/editor-roundtrip.test.js +29 -0
- package/dist/clients/inbox/review/__tests__/remap.test.js +196 -0
- package/dist/clients/inbox/review/__tests__/review-core.test.js +341 -0
- package/dist/clients/inbox/review/anchors.d.ts +41 -0
- package/dist/clients/inbox/review/anchors.js +89 -0
- package/dist/clients/inbox/review/columns.d.ts +22 -0
- package/dist/clients/inbox/review/columns.js +40 -0
- package/dist/clients/inbox/review/comments-client.d.ts +66 -0
- package/dist/clients/inbox/review/comments-client.js +98 -0
- package/dist/clients/inbox/review/composition.d.ts +60 -0
- package/dist/clients/inbox/review/composition.js +278 -0
- package/dist/clients/inbox/review/document-region.d.ts +120 -0
- package/dist/clients/inbox/review/document-region.js +902 -0
- package/dist/clients/inbox/review/frame.d.ts +45 -0
- package/dist/clients/inbox/review/frame.js +215 -0
- package/dist/clients/inbox/review/keys.d.ts +67 -0
- package/dist/clients/inbox/review/keys.js +130 -0
- package/dist/clients/inbox/review/launch.d.ts +10 -0
- package/dist/clients/inbox/review/launch.js +129 -0
- package/dist/clients/inbox/review/live-source.d.ts +24 -0
- package/dist/clients/inbox/review/live-source.js +160 -0
- package/dist/clients/inbox/review/nvim.d.ts +13 -0
- package/dist/clients/inbox/review/nvim.js +42 -0
- package/dist/clients/inbox/review/remap.d.ts +51 -0
- package/dist/clients/inbox/review/remap.js +157 -0
- package/dist/clients/inbox/review/review-client.d.ts +30 -0
- package/dist/clients/inbox/review/review-client.js +107 -0
- package/dist/clients/inbox/review/roundtrip.d.ts +5 -0
- package/dist/clients/inbox/review/roundtrip.js +36 -0
- package/dist/clients/inbox/review/state.d.ts +143 -0
- package/dist/clients/inbox/review/state.js +282 -0
- package/dist/clients/inbox/review-adapter.d.ts +29 -0
- package/dist/clients/inbox/review-adapter.js +92 -0
- package/dist/clients/inbox/review-visibility.d.ts +5 -0
- package/dist/clients/inbox/review-visibility.js +19 -0
- package/dist/clients/inbox/surface.d.ts +19 -0
- package/dist/clients/inbox/surface.js +128 -0
- package/dist/clients/inbox/tui/ansi.d.ts +66 -0
- package/dist/clients/inbox/tui/ansi.js +317 -0
- package/dist/clients/inbox/tui/clipboard.d.ts +2 -0
- package/dist/clients/inbox/tui/clipboard.js +26 -0
- package/dist/clients/inbox/tui/input.d.ts +7 -0
- package/dist/clients/inbox/tui/input.js +653 -0
- package/dist/clients/inbox/tui/keys.d.ts +28 -0
- package/dist/clients/inbox/tui/keys.js +111 -0
- package/dist/clients/inbox/tui/panel.d.ts +2 -0
- package/dist/clients/inbox/tui/panel.js +217 -0
- package/dist/clients/inbox/tui/render.d.ts +37 -0
- package/dist/clients/inbox/tui/render.js +476 -0
- package/dist/clients/inbox/tui/types.d.ts +57 -0
- package/dist/clients/inbox/tui/types.js +1 -0
- package/dist/clients/inbox/tui.d.ts +6 -0
- package/dist/clients/inbox/tui.js +74 -0
- package/dist/clients/surfaces/host.d.ts +66 -0
- package/dist/clients/surfaces/host.js +1 -0
- package/dist/clients/surfaces/region.d.ts +16 -0
- package/dist/clients/surfaces/region.js +1 -0
- package/dist/clients/web/__tests__/push-engine.test.js +14 -158
- package/dist/clients/web/dev-server.d.ts +3 -3
- package/dist/clients/web/dev-server.js +9 -23
- package/dist/clients/web/events.js +4 -6
- package/dist/clients/web/push-engine.d.ts +2 -66
- package/dist/clients/web/push-engine.js +30 -161
- package/dist/clients/web/push-registry.d.ts +0 -4
- package/dist/clients/web/push-registry.js +2 -7
- package/dist/clients/web/server.js +3 -35
- package/dist/clients/web/web-client/shared/protocol.d.ts +0 -108
- package/dist/clients/web/web-cmd.js +2 -2
- package/dist/commands/__tests__/human.test.js +27 -15
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/canvas-issue.js +1 -1
- package/dist/commands/canvas-use.js +1 -1
- package/dist/commands/human/doc.d.ts +2 -0
- package/dist/commands/human/doc.js +98 -0
- package/dist/commands/human/inbox.d.ts +2 -0
- package/dist/commands/human/inbox.js +101 -0
- package/dist/commands/human/prompts.d.ts +2 -3
- package/dist/commands/human/prompts.js +27 -180
- package/dist/commands/human/queue.d.ts +1 -5
- package/dist/commands/human/queue.js +54 -216
- package/dist/commands/human/review.d.ts +2 -0
- package/dist/commands/human/review.js +298 -0
- package/dist/commands/human/shared.d.ts +2 -69
- package/dist/commands/human/shared.js +1 -181
- package/dist/commands/human.js +15 -20
- package/dist/commands/memory/__tests__/lint.test.d.ts +1 -0
- package/dist/commands/memory/__tests__/lint.test.js +15 -0
- package/dist/commands/memory/delete.js +2 -2
- package/dist/commands/memory/lint.d.ts +1 -0
- package/dist/commands/memory/lint.js +21 -3
- package/dist/commands/memory/read.js +4 -4
- package/dist/commands/node.js +8 -7
- package/dist/commands/pkg/plugin-manage.js +143 -68
- package/dist/commands/surface-inspect.js +2 -2
- package/dist/commands/surface.js +6 -12
- package/dist/commands/sys/__tests__/setup-core.test.js +9 -5
- package/dist/commands/sys/__tests__/setup-front-door.test.js +7 -42
- package/dist/commands/sys/config.js +29 -3
- package/dist/commands/sys/feedback.js +1 -1
- package/dist/commands/sys/setup-core.d.ts +12 -11
- package/dist/commands/sys/setup-core.js +55 -46
- package/dist/commands/sys/setup-wizard.d.ts +4 -1
- package/dist/commands/sys/setup-wizard.js +50 -9
- package/dist/commands/sys/sync-deps.js +1 -1
- package/dist/commands/sys.js +4 -6
- package/dist/core/__tests__/broker-sdk-wiring.test.js +6 -1
- package/dist/core/__tests__/broker-snapshot-history.test.js +9 -1
- package/dist/core/__tests__/dead-node-policy-table.test.js +3 -4
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +8 -5
- package/dist/core/__tests__/fixtures/fake-engine.js +28 -2
- package/dist/core/__tests__/full/broker-provider-retry.test.js +87 -2
- package/dist/core/__tests__/full/broker-whip-interrupt.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-whip-interrupt.test.js +39 -0
- package/dist/core/__tests__/full/detach-focus.test.js +1 -1
- package/dist/core/__tests__/human-deliver-e2e.test.js +64 -64
- package/dist/core/__tests__/human-deliver.test.js +71 -345
- package/dist/core/__tests__/inline-memory-refs.test.js +2 -12
- package/dist/core/__tests__/memory-resolver-precedence.test.js +12 -15
- package/dist/core/__tests__/migration.test.js +10 -0
- package/dist/core/__tests__/model-pin-durability.test.js +14 -0
- package/dist/core/__tests__/phase4-review-migration.test.d.ts +1 -0
- package/dist/core/__tests__/phase4-review-migration.test.js +100 -0
- package/dist/core/__tests__/phase4-review-store.test.d.ts +1 -0
- package/dist/core/__tests__/phase4-review-store.test.js +143 -0
- package/dist/core/__tests__/remote-canvas-target.test.js +2 -2
- package/dist/core/__tests__/revive.test.js +22 -53
- package/dist/core/__tests__/session-cycles.test.js +48 -18
- package/dist/core/__tests__/spawn-no-kickoff.test.js +8 -12
- package/dist/core/__tests__/stop-guard.test.js +16 -1
- package/dist/core/__tests__/tmux-surface.test.js +21 -25
- package/dist/core/asset-root.d.ts +7 -0
- package/dist/core/asset-root.js +18 -0
- package/dist/core/broker-client/client.d.ts +0 -7
- package/dist/core/broker-client/client.js +4 -10
- package/dist/core/broker-client/transport-relay.d.ts +3 -4
- package/dist/core/broker-client/transport-relay.js +3 -4
- package/dist/core/canvas/__tests__/attention.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-transport.test.d.ts +1 -0
- package/dist/core/{view/__tests__/transport-remote.test.js → canvas/__tests__/remote-transport.test.js} +2 -2
- package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
- package/dist/core/canvas/attention.js +4 -4
- package/dist/core/canvas/canvas.d.ts +6 -11
- package/dist/core/canvas/canvas.js +11 -33
- package/dist/core/canvas/db.js +207 -38
- package/dist/core/canvas/extensions.d.ts +1 -1
- package/dist/core/canvas/extensions.js +8 -1
- package/dist/core/canvas/index.d.ts +0 -1
- package/dist/core/canvas/index.js +0 -1
- package/dist/core/canvas/nav-model.d.ts +1 -1
- package/dist/core/canvas/nav-model.js +1 -1
- package/dist/core/canvas/paths.d.ts +5 -0
- package/dist/core/canvas/paths.js +16 -0
- package/dist/core/canvas/remote-canvas-source.js +2 -2
- package/dist/core/canvas/remote-transport.d.ts +7 -0
- package/dist/core/{view/transport-remote.js → canvas/remote-transport.js} +3 -4
- package/dist/core/canvas/source.js +1 -1
- package/dist/core/canvas/status-glyph.js +4 -12
- package/dist/core/canvas/types.d.ts +10 -35
- package/dist/core/clipboard-text.d.ts +8 -0
- package/dist/core/clipboard-text.js +18 -0
- package/dist/core/command-manifests/registry.d.ts +1 -3
- package/dist/core/command-plugins/bundle.d.ts +29 -0
- package/dist/core/command-plugins/bundle.js +198 -0
- package/dist/core/command-plugins/discovery.d.ts +0 -8
- package/dist/core/command-plugins/discovery.js +2 -39
- package/dist/core/command-plugins/transport/http-fetch.d.ts +4 -30
- package/dist/core/command-plugins/transport/http-fetch.js +17 -95
- package/dist/core/command.d.ts +1 -13
- package/dist/core/command.js +1 -16
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +28 -3
- package/dist/core/document-lines.d.ts +4 -0
- package/dist/core/document-lines.js +6 -0
- package/dist/core/feed/feed.d.ts +2 -0
- package/dist/core/feed/feed.js +11 -7
- package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-cancel-worker.js +7 -0
- package/dist/core/human/__tests__/inbox-claim-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-claim-worker.js +17 -0
- package/dist/core/human/__tests__/inbox-core.test.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-core.test.js +144 -0
- package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-stale-claim-worker.js +8 -0
- package/dist/core/human/__tests__/visible-paths.test.d.ts +1 -0
- package/dist/{clients/attach → core/human}/__tests__/visible-paths.test.js +9 -0
- package/dist/core/human/claim.d.ts +23 -0
- package/dist/core/human/claim.js +67 -0
- package/dist/core/human/convention.d.ts +30 -0
- package/dist/core/human/convention.js +202 -0
- package/dist/core/human/deck-factories.d.ts +8 -0
- package/dist/core/human/deck-factories.js +15 -0
- package/dist/core/human/deck-schema.d.ts +70 -0
- package/dist/core/human/deck-schema.js +92 -0
- package/dist/core/human/root.d.ts +7 -0
- package/dist/core/human/root.js +37 -0
- package/dist/core/human/scan.d.ts +3 -0
- package/dist/core/human/scan.js +76 -0
- package/dist/core/human/summary.d.ts +8 -0
- package/dist/core/human/summary.js +44 -0
- package/dist/core/human/tickets.d.ts +49 -0
- package/dist/core/human/tickets.js +190 -0
- package/dist/core/human/types.d.ts +136 -0
- package/dist/core/human/types.js +5 -0
- package/dist/{clients/attach → core/human}/visible-paths.d.ts +1 -1
- package/dist/{clients/attach → core/human}/visible-paths.js +3 -2
- package/dist/core/inspector/chrome.d.ts +6 -0
- package/dist/core/inspector/chrome.js +14 -0
- package/dist/core/inspector/contract.d.ts +105 -0
- package/dist/core/inspector/contract.js +1 -0
- package/dist/core/inspector/core.d.ts +4 -5
- package/dist/core/inspector/core.js +3 -5
- package/dist/core/inspector/host.d.ts +46 -0
- package/dist/core/{tui → inspector}/host.js +42 -208
- package/dist/core/inspector/text.d.ts +2 -2
- package/dist/core/inspector/transport.d.ts +6 -0
- package/dist/core/inspector/transport.js +36 -0
- package/dist/core/inspector/tui.d.ts +2 -2
- package/dist/core/inspector/tui.js +15 -15
- package/dist/core/keybindings/__tests__/inbox-affordance.test.js +11 -11
- package/dist/core/keybindings/__tests__/resolve.test.js +10 -19
- package/dist/core/keybindings/attach-control.d.ts +9 -3
- package/dist/core/keybindings/attach-control.js +3 -1
- package/dist/core/keybindings/catalog.d.ts +6 -5
- package/dist/core/keybindings/catalog.js +61 -133
- package/dist/core/keybindings/inbox.d.ts +5 -6
- package/dist/core/keybindings/inbox.js +9 -10
- package/dist/core/keybindings/index.d.ts +3 -3
- package/dist/core/keybindings/index.js +3 -3
- package/dist/core/keybindings/persistence.d.ts +5 -1
- package/dist/core/keybindings/persistence.js +24 -6
- package/dist/core/keybindings/resolve.d.ts +6 -0
- package/dist/core/keybindings/resolve.js +10 -3
- package/dist/core/memory/inline-ref-grammar.js +2 -3
- package/dist/core/memory-resolver.d.ts +27 -29
- package/dist/core/memory-resolver.js +45 -39
- package/dist/core/model-routes.d.ts +33 -0
- package/dist/core/model-routes.js +42 -3
- package/dist/core/preview-registry.js +17 -55
- package/dist/core/profiles/select.js +3 -3
- package/dist/core/{view/remote-canvas-target.d.ts → remote-canvas-target.d.ts} +2 -2
- package/dist/core/{view/remote-canvas-target.js → remote-canvas-target.js} +4 -4
- package/dist/core/review/__tests__/capture-origin.test.d.ts +1 -0
- package/dist/core/review/__tests__/capture-origin.test.js +139 -0
- package/dist/core/review/__tests__/comments.test.d.ts +1 -0
- package/dist/core/review/__tests__/comments.test.js +98 -0
- package/dist/core/review/__tests__/project.test.d.ts +1 -0
- package/dist/core/review/__tests__/project.test.js +71 -0
- package/dist/core/review/__tests__/remap.test.d.ts +1 -0
- package/dist/core/review/__tests__/remap.test.js +159 -0
- package/dist/core/review/__tests__/stage-identity.test.d.ts +1 -0
- package/dist/core/review/__tests__/stage-identity.test.js +140 -0
- package/dist/core/review/birth.d.ts +7 -0
- package/dist/core/review/birth.js +23 -0
- package/dist/core/review/comments.d.ts +69 -0
- package/dist/core/review/comments.js +295 -0
- package/dist/core/review/companion.d.ts +35 -0
- package/dist/core/review/companion.js +205 -0
- package/dist/core/review/document.d.ts +27 -0
- package/dist/core/review/document.js +138 -0
- package/dist/core/review/project.d.ts +13 -0
- package/dist/core/review/project.js +58 -0
- package/dist/core/review/realize.d.ts +19 -0
- package/dist/core/review/realize.js +217 -0
- package/dist/core/review/remap.d.ts +24 -0
- package/dist/core/review/remap.js +156 -0
- package/dist/core/review/signal.d.ts +5 -0
- package/dist/core/review/signal.js +31 -0
- package/dist/core/review/stage.d.ts +26 -0
- package/dist/core/review/stage.js +184 -0
- package/dist/core/review/store.d.ts +46 -0
- package/dist/core/review/store.js +163 -0
- package/dist/core/review/ticket-filter.d.ts +17 -0
- package/dist/core/review/ticket-filter.js +32 -0
- package/dist/core/review/types.d.ts +130 -0
- package/dist/core/review/types.js +23 -0
- package/dist/core/runtime/__tests__/session-visibility.test.d.ts +1 -0
- package/dist/core/runtime/__tests__/session-visibility.test.js +119 -0
- package/dist/core/runtime/bearings.d.ts +5 -17
- package/dist/core/runtime/bearings.js +88 -71
- package/dist/core/runtime/broker-protocol.d.ts +31 -8
- package/dist/core/runtime/broker.d.ts +15 -1
- package/dist/core/runtime/broker.js +573 -141
- package/dist/core/runtime/canvas-extensions.d.ts +4 -5
- package/dist/core/runtime/canvas-extensions.js +5 -5
- package/dist/core/runtime/front-door.js +4 -4
- package/dist/core/runtime/headless-pi.d.ts +24 -0
- package/dist/core/runtime/headless-pi.js +152 -0
- package/dist/core/runtime/kickoff.d.ts +1 -5
- package/dist/core/runtime/kickoff.js +1 -5
- package/dist/core/runtime/model-registry.d.ts +3 -0
- package/dist/core/runtime/model-registry.js +13 -0
- package/dist/core/runtime/naming-persist.d.ts +4 -0
- package/dist/core/runtime/naming-persist.js +27 -1
- package/dist/core/runtime/naming.d.ts +17 -12
- package/dist/core/runtime/naming.js +91 -140
- package/dist/core/runtime/node-read.js +19 -10
- package/dist/core/runtime/nodes.d.ts +9 -14
- package/dist/core/runtime/nodes.js +3 -53
- package/dist/core/runtime/package-health.d.ts +6 -21
- package/dist/core/runtime/package-health.js +12 -123
- package/dist/core/runtime/placement-tmux.d.ts +2 -1
- package/dist/core/runtime/placement-tmux.js +6 -4
- package/dist/core/runtime/placement.d.ts +1 -0
- package/dist/core/runtime/placement.js +4 -8
- package/dist/core/runtime/promote.js +3 -0
- package/dist/core/runtime/recap.d.ts +2 -7
- package/dist/core/runtime/recap.js +35 -69
- package/dist/core/runtime/revive.js +34 -77
- package/dist/core/runtime/session-cycles.d.ts +4 -0
- package/dist/core/runtime/session-cycles.js +14 -1
- package/dist/core/runtime/session-visibility.d.ts +37 -0
- package/dist/core/runtime/session-visibility.js +83 -0
- package/dist/core/runtime/spawn.js +10 -22
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +18 -13
- package/dist/core/runtime/surface-bg.js +1 -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 +35 -7
- package/dist/core/runtime/tmux.js +439 -62
- package/dist/core/runtime/tool-group-summary.d.ts +19 -0
- package/dist/core/runtime/tool-group-summary.js +88 -0
- package/dist/core/scope.d.ts +2 -7
- package/dist/core/scope.js +9 -35
- package/dist/core/substrate/schema.d.ts +2 -2
- package/dist/core/termrender/code-doc.d.ts +12 -0
- package/dist/core/termrender/code-doc.js +92 -0
- package/dist/core/termrender/display.d.ts +12 -0
- package/dist/core/termrender/display.js +19 -0
- package/dist/core/termrender/termrender.d.ts +73 -0
- package/dist/core/termrender/termrender.js +795 -0
- package/dist/core/termrender/version.d.ts +1 -0
- package/dist/core/termrender/version.js +1 -0
- package/dist/core/tui/terminal.js +1 -2
- package/dist/core/user-settings.d.ts +71 -3
- package/dist/core/user-settings.js +83 -1
- package/dist/daemon/api/__tests__/inbox.test.js +44 -133
- package/dist/daemon/api/handlers/human.d.ts +0 -10
- package/dist/daemon/api/handlers/human.js +104 -702
- package/dist/daemon/api/handlers/inbox.js +113 -142
- package/dist/daemon/api/handlers/review-comments.d.ts +2 -0
- package/dist/daemon/api/handlers/review-comments.js +500 -0
- package/dist/daemon/api/handlers/reviews.d.ts +2 -0
- package/dist/daemon/api/handlers/reviews.js +287 -0
- package/dist/daemon/api/map.d.ts +22 -7
- package/dist/daemon/api/map.js +159 -11
- package/dist/daemon/api/server.js +4 -0
- package/dist/daemon/cron-run.js +6 -9
- package/dist/daemon/crtrd.js +17 -33
- package/dist/daemon/fleet.d.ts +1 -8
- package/dist/daemon/fleet.js +4 -30
- package/dist/daemon/human/finish.d.ts +14 -0
- package/dist/daemon/human/finish.js +115 -0
- package/dist/daemon/human/sweep.d.ts +6 -0
- package/dist/daemon/human/sweep.js +49 -0
- package/dist/daemon/messaging/node-message.d.ts +18 -0
- package/dist/daemon/messaging/node-message.js +70 -0
- package/dist/daemon/review/comment-notify.d.ts +26 -0
- package/dist/daemon/review/comment-notify.js +107 -0
- package/dist/daemon/review/deliver.d.ts +7 -0
- package/dist/daemon/review/deliver.js +56 -0
- package/dist/daemon/review/finish.d.ts +22 -0
- package/dist/daemon/review/finish.js +254 -0
- package/dist/daemon/review/sweep.d.ts +11 -0
- package/dist/daemon/review/sweep.js +120 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +42 -64
- package/dist/pi-extensions/__tests__/canvas-recap.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-recap.test.js +159 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +16 -38
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +63 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +8 -11
- package/dist/pi-extensions/canvas-context-intro.js +50 -104
- package/dist/pi-extensions/canvas-recap.d.ts +15 -2
- package/dist/pi-extensions/canvas-recap.js +192 -69
- package/dist/pi-extensions/canvas-review-boundary.d.ts +59 -0
- package/dist/pi-extensions/canvas-review-boundary.js +81 -0
- package/dist/pi-extensions/canvas-stophook.d.ts +3 -3
- package/dist/pi-extensions/canvas-stophook.js +78 -128
- package/dist/pi-extensions/canvas-structured-output.js +52 -8
- package/dist/shared/generated-context.d.ts +29 -4
- package/dist/shared/generated-context.js +122 -7
- package/dist/shared/tool-groups.d.ts +23 -0
- package/dist/shared/tool-groups.js +60 -0
- package/dist/shared/working-activity.d.ts +8 -0
- package/dist/shared/working-activity.js +26 -0
- package/dist/types.d.ts +36 -2
- package/dist/types.js +51 -0
- package/dist/web-client/assets/index-CCPaLeW7.css +2 -0
- package/dist/web-client/assets/index-kyOWsnZK.js +86 -0
- package/dist/web-client/index.html +2 -2
- package/dist/web-client/sw.js +1 -1
- package/docs/compat/hearth-crtr-v4.md +2 -2
- package/docs/compat/hearth-crtr-v5.md +4 -2
- package/docs/public-api.md +3 -3
- package/package.json +21 -14
- package/runtime.lock.json +4661 -1453
- package/scripts/postinstall.mjs +15 -2
- package/dist/builtin-pi-packages/pi-mode-switch/README.md +0 -36
- package/dist/builtin-pi-packages/pi-mode-switch/bin/mode +0 -36
- package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +0 -449
- package/dist/builtin-pi-packages/pi-mode-switch/package.json +0 -14
- package/dist/builtin-pi-packages/pi-mode-switch/tsconfig.json +0 -9
- package/dist/builtin-views/_lib/states.mjs +0 -161
- package/dist/builtin-views/canvas/core.mjs +0 -657
- package/dist/builtin-views/canvas/text.mjs +0 -58
- package/dist/builtin-views/canvas/tui.mjs +0 -168
- package/dist/builtin-views/canvas/web.jsx +0 -121
- package/dist/builtin-views/chat/core.mjs +0 -684
- package/dist/builtin-views/chat/text.mjs +0 -101
- package/dist/builtin-views/chat/tui.mjs +0 -360
- package/dist/builtin-views/chat/web.jsx +0 -362
- package/dist/builtin-views/git-pr/core.mjs +0 -673
- package/dist/builtin-views/git-pr/text.mjs +0 -84
- package/dist/builtin-views/git-pr/tui.mjs +0 -301
- package/dist/builtin-views/git-pr/web.jsx +0 -216
- package/dist/builtin-views/inbox/_lib/render.mjs +0 -175
- package/dist/builtin-views/inbox/core.mjs +0 -1273
- package/dist/builtin-views/inbox/text.mjs +0 -73
- package/dist/builtin-views/inbox/tui.mjs +0 -314
- package/dist/builtin-views/inbox/web.jsx +0 -188
- package/dist/builtin-views/linkedin/core.mjs +0 -906
- package/dist/builtin-views/linkedin/text.mjs +0 -69
- package/dist/builtin-views/linkedin/tui.mjs +0 -413
- package/dist/builtin-views/linkedin/web.jsx +0 -206
- package/dist/builtin-views/prompt-review/core.mjs +0 -735
- package/dist/builtin-views/prompt-review/text.mjs +0 -15
- package/dist/builtin-views/prompt-review/tui.mjs +0 -196
- package/dist/builtin-views/prompt-review/web.jsx +0 -484
- package/dist/builtin-views/workspace-sidebar/__tests__/core.test.js +0 -50
- package/dist/builtin-views/workspace-sidebar/__tests__/core.test.ts +0 -53
- package/dist/builtin-views/workspace-sidebar/core.mjs +0 -670
- package/dist/builtin-views/workspace-sidebar/text.mjs +0 -53
- package/dist/builtin-views/workspace-sidebar/tui.mjs +0 -141
- package/dist/builtin-views/workspace-sidebar/web.jsx +0 -109
- package/dist/clients/attach/session/mode.d.ts +0 -25
- package/dist/clients/attach/session/mode.js +0 -97
- package/dist/commands/sys/promptstudio.d.ts +0 -2
- package/dist/commands/sys/promptstudio.js +0 -65
- package/dist/commands/view-cycle.d.ts +0 -2
- package/dist/commands/view-cycle.js +0 -130
- package/dist/commands/view-list.d.ts +0 -2
- package/dist/commands/view-list.js +0 -66
- package/dist/commands/view-new.d.ts +0 -2
- package/dist/commands/view-new.js +0 -74
- package/dist/commands/view-pick.d.ts +0 -6
- package/dist/commands/view-pick.js +0 -129
- package/dist/commands/view-run.d.ts +0 -2
- package/dist/commands/view-run.js +0 -214
- package/dist/commands/view.d.ts +0 -2
- package/dist/commands/view.js +0 -31
- package/dist/commands/workspace.d.ts +0 -2
- package/dist/commands/workspace.js +0 -165
- package/dist/core/__tests__/chat-view-reconnect.test.js +0 -105
- package/dist/core/__tests__/full/consult-startup-failure.test.js +0 -124
- package/dist/core/canvas/__tests__/human-work-outbox.test.js +0 -123
- package/dist/core/canvas/human-work-outbox.d.ts +0 -73
- package/dist/core/canvas/human-work-outbox.js +0 -261
- package/dist/core/command-plugins/store.d.ts +0 -16
- package/dist/core/command-plugins/store.js +0 -64
- package/dist/core/keybindings/__tests__/bespoke-consumers.test.js +0 -40
- package/dist/core/runtime/fault-recovery-nudge.d.ts +0 -3
- package/dist/core/runtime/fault-recovery-nudge.js +0 -3
- package/dist/core/tui/__tests__/host-keybindings.test.js +0 -113
- package/dist/core/tui/host.d.ts +0 -66
- package/dist/core/view/__tests__/transport-cache.test.js +0 -62
- package/dist/core/view/bridge.d.ts +0 -10
- package/dist/core/view/bridge.js +0 -31
- package/dist/core/view/chrome.d.ts +0 -9
- package/dist/core/view/chrome.js +0 -22
- package/dist/core/view/contract.d.ts +0 -205
- package/dist/core/view/contract.js +0 -23
- package/dist/core/view/loader.d.ts +0 -31
- package/dist/core/view/loader.js +0 -188
- package/dist/core/view/stream-local.d.ts +0 -3
- package/dist/core/view/stream-local.js +0 -231
- package/dist/core/view/transport-cache.d.ts +0 -8
- package/dist/core/view/transport-cache.js +0 -38
- package/dist/core/view/transport-local.d.ts +0 -7
- package/dist/core/view/transport-local.js +0 -83
- package/dist/core/view/transport-remote.d.ts +0 -2
- package/dist/core/view/transport.d.ts +0 -10
- package/dist/core/view/transport.js +0 -15
- package/dist/pi-extensions/naming-tool.d.ts +0 -24
- package/dist/pi-extensions/naming-tool.js +0 -67
- package/dist/prompts/view.d.ts +0 -7
- package/dist/prompts/view.js +0 -172
- package/dist/web/ViewChrome.d.ts +0 -7
- package/dist/web/ViewChrome.js +0 -28
- package/dist/web/ViewPane.d.ts +0 -39
- package/dist/web/ViewPane.js +0 -48
- package/dist/web/index.d.ts +0 -8
- package/dist/web/index.js +0 -22
- package/dist/web/runtime.d.ts +0 -39
- package/dist/web/runtime.js +0 -232
- package/dist/web/states.d.ts +0 -24
- package/dist/web/states.js +0 -24
- package/dist/web/transport-http.d.ts +0 -5
- package/dist/web/transport-http.js +0 -34
- package/dist/web/transport-stream.d.ts +0 -3
- package/dist/web/transport-stream.js +0 -204
- package/dist/web-client/assets/index-U_NZ66VE.js +0 -79
- package/dist/web-client/assets/index-aaeu4adv.css +0 -2
- /package/dist/{builtin-views/workspace-sidebar/__tests__/core.test.d.ts → clients/attach/__tests__/file-review-focus.test.d.ts} +0 -0
- /package/dist/clients/attach/__tests__/{visible-paths.test.d.ts → group-activity.test.d.ts} +0 -0
- /package/dist/{core/__tests__/chat-view-reconnect.test.d.ts → clients/attach/__tests__/input-controller-extension-command.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/consult-startup-failure.test.d.ts → clients/attach/__tests__/session-identity-refresh.test.d.ts} +0 -0
- /package/dist/{core/canvas/__tests__/human-work-outbox.test.d.ts → clients/inbox/__tests__/inbox-controller.test.d.ts} +0 -0
- /package/dist/{core/keybindings/__tests__/bespoke-consumers.test.d.ts → clients/inbox/__tests__/mount-panel.test.d.ts} +0 -0
- /package/dist/{core/tui/__tests__/host-keybindings.test.d.ts → clients/inbox/review/__tests__/editor-roundtrip.test.d.ts} +0 -0
- /package/dist/{core/view/__tests__/transport-cache.test.d.ts → clients/inbox/review/__tests__/remap.test.d.ts} +0 -0
- /package/dist/{core/view/__tests__/transport-remote.test.d.ts → clients/inbox/review/__tests__/review-core.test.d.ts} +0 -0
|
@@ -32,6 +32,8 @@ import { operationIdContext } from '../events/operation-id.js';
|
|
|
32
32
|
import { findMissingPackagePaths } from './package-health.js';
|
|
33
33
|
import { SessionListCache } from './session-list-cache.js';
|
|
34
34
|
import { getNode, updateNode } from '../canvas/index.js';
|
|
35
|
+
import { readConfig } from '../config.js';
|
|
36
|
+
import { atomicWriteJson, readJsonIfExists } from '../fs-utils.js';
|
|
35
37
|
import { buildRefInventory } from '../memory/inline-ref-inventory.js';
|
|
36
38
|
import { buildGuidance } from '../memory/inline-ref-guidance.js';
|
|
37
39
|
import { clearSessionCache } from '../substrate/session-cache.js';
|
|
@@ -44,16 +46,33 @@ import { PoolCredentialStore } from '../pool-credential-store.js';
|
|
|
44
46
|
import { clearFault, isModelNotFoundError, isProviderUnconfiguredError, readFault, recordFault, recordFaultForCurrentNode } from './fault.js';
|
|
45
47
|
import { clearCleanAbort, markBusy, markCleanAbort } from './busy.js';
|
|
46
48
|
import { extractCoolingDeadline } from './managed-provider-cooling.js';
|
|
47
|
-
import { AUTH_FAULT_RECOVERY_BODY, CONNECTION_FAULT_RECOVERY_BODY, PROVIDER_FAULT_RECOVERY_BODY } from '
|
|
49
|
+
import { AUTH_FAULT_RECOVERY_BODY, CONNECTION_FAULT_RECOVERY_BODY, PROVIDER_FAULT_RECOVERY_BODY, formatModelFallbackRecovery, generatedContextText, } from '../../shared/generated-context.js';
|
|
50
|
+
import { assistantVisibleText, isTrueUserMessage, normalizeToolGroupSummaryRecord, } from '../../shared/tool-groups.js';
|
|
51
|
+
import { headlessToolGroupSummary, serializeToolGroup } from './tool-group-summary.js';
|
|
48
52
|
import { markFatalOnExhaust, nextFaultRetry, policyFor } from './fault-recovery.js';
|
|
49
53
|
import { probeOnline as probeOnlineReal } from './connectivity.js';
|
|
50
|
-
import { CRTR_CYCLE_CUSTOM_TYPE, cycleAwareMessages } from './session-cycles.js';
|
|
54
|
+
import { CRTR_CYCLE_CUSTOM_TYPE, cycleAwareMessages, withoutYieldAbort } from './session-cycles.js';
|
|
55
|
+
import { boundaryRootedTree, REVIEW_BOUNDARY_CUSTOM_TYPE, visibleMessages } from './session-visibility.js';
|
|
51
56
|
import { BUILTIN_SLASH_COMMANDS, piSessionsRoot } from './pi-vendored.js';
|
|
52
57
|
import { StreamWatchdog, resolveStreamWatchdogMs, releaseKnownStreamWait, KNOWN_STREAM_WAIT_UI } from './stream-watchdog.js';
|
|
53
58
|
import { isDeterministicCommandExpansion } from './command-expansion.js';
|
|
59
|
+
import { DEFAULT_WORKING_ACTIVITY, randomWorkingActivity } from '../../shared/working-activity.js';
|
|
54
60
|
import { isManagedProvider, readSubscriptionPool, resolveProviderCandidates } from '../subscription-state.js';
|
|
55
|
-
import { expandModelCandidates, modelRequestFromConfig, probeRouteAvailability } from '../model-routes.js';
|
|
61
|
+
import { expandModelCandidates, modelRequestFromConfig, probeRouteAvailability, selectInitialModelRoute } from '../model-routes.js';
|
|
56
62
|
import { encodeFrame, FrameDecoder, FrameOverflowError, BROKER_READ_CAPS, } from './broker-protocol.js';
|
|
63
|
+
/** A review companion may only navigate or fork within a branch whose ancestry
|
|
64
|
+
* has crossed its durable transcript boundary. The broker sends this typed local
|
|
65
|
+
* failure as a dedicated error frame without invoking pi's tree mutation. */
|
|
66
|
+
export const REVIEW_BOUNDARY_NAVIGATION_CODE = 'review_boundary_navigation';
|
|
67
|
+
export class ReviewBoundaryNavigationError extends Error {
|
|
68
|
+
targetId;
|
|
69
|
+
code = REVIEW_BOUNDARY_NAVIGATION_CODE;
|
|
70
|
+
constructor(targetId) {
|
|
71
|
+
super(`review companion cannot navigate before the visible transcript boundary (target: ${targetId})`);
|
|
72
|
+
this.targetId = targetId;
|
|
73
|
+
this.name = 'ReviewBoundaryNavigationError';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
57
76
|
const THINKING_LEVELS = new Set(['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
|
|
58
77
|
/**
|
|
59
78
|
* pi 0.82 made `ModelRuntime` the canonical model/auth surface and demoted
|
|
@@ -174,6 +193,13 @@ export function isLeadingEngineCommand(text, session) {
|
|
|
174
193
|
}
|
|
175
194
|
return false;
|
|
176
195
|
}
|
|
196
|
+
/** A `steer` frame normally maps directly to pi's steer queue. Engine commands,
|
|
197
|
+
* skills, and templates must instead pass through prompt()'s leading-command
|
|
198
|
+
* preflight; with streamingBehavior:'steer', ordinary expanded text still lands
|
|
199
|
+
* in the same queue. */
|
|
200
|
+
export function resolveSteerCall(text, session) {
|
|
201
|
+
return isLeadingEngineCommand(text, session) ? 'prompt' : 'steer';
|
|
202
|
+
}
|
|
177
203
|
/**
|
|
178
204
|
* Enumerate every leading-token STRING this session's engine would
|
|
179
205
|
* independently consume as a leading command/skill/template dispatch BEFORE
|
|
@@ -399,6 +425,23 @@ const REPLACEMENT_DEADLINE_MS = 30_000;
|
|
|
399
425
|
* the budget elapses before the runtime promise settles — never compared via
|
|
400
426
|
* string matching. */
|
|
401
427
|
const REPLACEMENT_DEADLINE_EXCEEDED = Symbol('replacement-deadline-exceeded');
|
|
428
|
+
const PENDING_TOOL_SUMMARY_KEY_MAX_CHARS = 1_000;
|
|
429
|
+
const PENDING_TOOL_SUMMARY_SEGMENT_MAX_CHARS = 10_000;
|
|
430
|
+
function normalizePendingToolGroupSummary(value) {
|
|
431
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
432
|
+
return null;
|
|
433
|
+
const record = value;
|
|
434
|
+
if (Object.keys(record).sort().join(',') !== 'key,segment,version')
|
|
435
|
+
return null;
|
|
436
|
+
if (record.version !== 1 || typeof record.key !== 'string' || typeof record.segment !== 'string')
|
|
437
|
+
return null;
|
|
438
|
+
if (record.key.length === 0 ||
|
|
439
|
+
record.key.length > PENDING_TOOL_SUMMARY_KEY_MAX_CHARS ||
|
|
440
|
+
record.segment.length === 0 ||
|
|
441
|
+
record.segment.length > PENDING_TOOL_SUMMARY_SEGMENT_MAX_CHARS)
|
|
442
|
+
return null;
|
|
443
|
+
return { version: 1, key: record.key, segment: record.segment };
|
|
444
|
+
}
|
|
402
445
|
// ---------------------------------------------------------------------------
|
|
403
446
|
// M3 (scout mq5thyli): the active engine session, exposed so broker-cli's FATAL
|
|
404
447
|
// handlers (uncaughtException / unhandledRejection / the runBroker reject) can
|
|
@@ -521,6 +564,10 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
521
564
|
// that does not expose the replacement API (the fake-engine fixture).
|
|
522
565
|
// eslint-disable-next-line prefer-const
|
|
523
566
|
let { session, services, resuming, runtime } = await buildBrokerSession(engine, cfg);
|
|
567
|
+
// Review identity is immutable for the broker's lifetime. Snapshot producers
|
|
568
|
+
// close over this one construction-time classification rather than re-reading
|
|
569
|
+
// canvas metadata for every welcome or session replacement.
|
|
570
|
+
const reviewCompanion = getNode(nodeId)?.review_binding != null;
|
|
524
571
|
// M3: name the boot session as the process-fatal disposal target immediately —
|
|
525
572
|
// enqueueRebind reassigns this on every later replacement, but without this
|
|
526
573
|
// initial assignment `activeSession` stays null (and disposeActiveSession() a
|
|
@@ -588,7 +635,28 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
588
635
|
const displayStatuses = new Map();
|
|
589
636
|
const displayWidgets = new Map();
|
|
590
637
|
let displayTitle;
|
|
638
|
+
let workingActivity = DEFAULT_WORKING_ACTIVITY;
|
|
639
|
+
const toolSummariesPath = join(jobDir(nodeId), 'tool-summaries.json');
|
|
640
|
+
const pendingToolSummaryPath = join(jobDir(nodeId), 'pending-tool-summary.json');
|
|
641
|
+
const toolGroupSummaries = new Map();
|
|
642
|
+
try {
|
|
643
|
+
const persisted = readJsonIfExists(toolSummariesPath);
|
|
644
|
+
for (const [key, summary] of Object.entries(normalizeToolGroupSummaryRecord(persisted))) {
|
|
645
|
+
toolGroupSummaries.set(key, summary);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
catch {
|
|
649
|
+
// A malformed sidecar merely withholds summaries; transcript tools remain visible.
|
|
650
|
+
}
|
|
651
|
+
const summaryAttempted = new Set(toolGroupSummaries.keys());
|
|
652
|
+
const generatingToolGroups = new Map();
|
|
653
|
+
const toolGroupsByCallId = new Map();
|
|
654
|
+
const summaryTasks = new Set();
|
|
655
|
+
let openToolGroup;
|
|
656
|
+
let lastToolGroupBoundaryProse = '';
|
|
657
|
+
let sawAssistantProse = false;
|
|
591
658
|
let disposed = false;
|
|
659
|
+
let disposePending = false;
|
|
592
660
|
let agentRunActive = false;
|
|
593
661
|
let shutdownRequested = false;
|
|
594
662
|
let server;
|
|
@@ -729,6 +797,224 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
729
797
|
}
|
|
730
798
|
broadcast(frame);
|
|
731
799
|
};
|
|
800
|
+
/** Record and asynchronously summarize the maximal non-boundary region between
|
|
801
|
+
* visible prose messages. A boundary can land while a tool is still running,
|
|
802
|
+
* so closed groups retain their call ids until every result has joined the
|
|
803
|
+
* same serialized segment; only then may they become collapsible. */
|
|
804
|
+
const ensureOpenToolGroup = () => {
|
|
805
|
+
if (openToolGroup === undefined) {
|
|
806
|
+
openToolGroup = {
|
|
807
|
+
toolCount: 0,
|
|
808
|
+
pieces: [],
|
|
809
|
+
boundaryProse: lastToolGroupBoundaryProse,
|
|
810
|
+
pendingToolIds: new Set(),
|
|
811
|
+
closed: false,
|
|
812
|
+
summarizeWhenSettled: false,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
return openToolGroup;
|
|
816
|
+
};
|
|
817
|
+
const clearPendingToolGroupSummary = (input) => {
|
|
818
|
+
try {
|
|
819
|
+
const persisted = normalizePendingToolGroupSummary(readJsonIfExists(pendingToolSummaryPath));
|
|
820
|
+
if (persisted?.key === input.key && persisted.segment === input.segment)
|
|
821
|
+
unlinkSync(pendingToolSummaryPath);
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
824
|
+
// The durable summary is already authoritative. A stale handoff may cost a
|
|
825
|
+
// no-op read on the next boot, but must not revoke the recap just written.
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
const generateToolGroupSummary = (input) => {
|
|
829
|
+
const { key, segment } = input;
|
|
830
|
+
if (summaryAttempted.has(key) || generatingToolGroups.has(key))
|
|
831
|
+
return;
|
|
832
|
+
summaryAttempted.add(key);
|
|
833
|
+
generatingToolGroups.set(key, input);
|
|
834
|
+
let task;
|
|
835
|
+
task = headlessToolGroupSummary(segment)
|
|
836
|
+
.then((summary) => {
|
|
837
|
+
if (summary === null)
|
|
838
|
+
return;
|
|
839
|
+
toolGroupSummaries.set(key, summary);
|
|
840
|
+
try {
|
|
841
|
+
atomicWriteJson(toolSummariesPath, Object.fromEntries(toolGroupSummaries));
|
|
842
|
+
}
|
|
843
|
+
catch {
|
|
844
|
+
// A delivery without durable state would disappear on reattach, so do
|
|
845
|
+
// not advertise it. The calls remain folded normally instead.
|
|
846
|
+
toolGroupSummaries.delete(key);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
clearPendingToolGroupSummary(input);
|
|
850
|
+
broadcast({ type: 'tool_group_summary', key, summary });
|
|
851
|
+
})
|
|
852
|
+
.catch(() => undefined)
|
|
853
|
+
.finally(() => {
|
|
854
|
+
generatingToolGroups.delete(key);
|
|
855
|
+
summaryTasks.delete(task);
|
|
856
|
+
});
|
|
857
|
+
summaryTasks.add(task);
|
|
858
|
+
};
|
|
859
|
+
const generateSettledToolGroup = (group) => {
|
|
860
|
+
if (!group.closed || group.pendingToolIds.size > 0 || group.key === undefined || !group.summarizeWhenSettled)
|
|
861
|
+
return;
|
|
862
|
+
generateToolGroupSummary({
|
|
863
|
+
version: 1,
|
|
864
|
+
key: group.key,
|
|
865
|
+
segment: serializeToolGroup(group.boundaryProse, group.pieces),
|
|
866
|
+
});
|
|
867
|
+
};
|
|
868
|
+
try {
|
|
869
|
+
const pending = normalizePendingToolGroupSummary(readJsonIfExists(pendingToolSummaryPath));
|
|
870
|
+
if (pending === null) {
|
|
871
|
+
if (existsSync(pendingToolSummaryPath)) {
|
|
872
|
+
unlinkSync(pendingToolSummaryPath);
|
|
873
|
+
emitEvent({
|
|
874
|
+
level: 'warn',
|
|
875
|
+
event: 'broker.startup.warning',
|
|
876
|
+
fields: { stage: 'pending_tool_summary', message: 'removed an invalid pending tool summary' },
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
else if (toolGroupSummaries.has(pending.key)) {
|
|
881
|
+
unlinkSync(pendingToolSummaryPath);
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
generateToolGroupSummary(pending);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
catch (error) {
|
|
888
|
+
emitEvent({
|
|
889
|
+
level: 'warn',
|
|
890
|
+
event: 'broker.startup.warning',
|
|
891
|
+
fields: {
|
|
892
|
+
stage: 'pending_tool_summary',
|
|
893
|
+
message: error instanceof Error ? error.message : String(error),
|
|
894
|
+
},
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
const finishOpenToolGroup = (settleInterruptedTools = false) => {
|
|
898
|
+
const group = openToolGroup;
|
|
899
|
+
openToolGroup = undefined;
|
|
900
|
+
if (group === undefined)
|
|
901
|
+
return;
|
|
902
|
+
group.closed = true;
|
|
903
|
+
// An abort is itself the visible evidence that any pending calls did not
|
|
904
|
+
// finish. Their recorded arguments still belong to the recap, and Ctrl+O
|
|
905
|
+
// retains the full rows, so let the completed pre-abort span summarize.
|
|
906
|
+
if (settleInterruptedTools) {
|
|
907
|
+
for (const id of group.pendingToolIds)
|
|
908
|
+
toolGroupsByCallId.delete(id);
|
|
909
|
+
group.pendingToolIds.clear();
|
|
910
|
+
}
|
|
911
|
+
// Configuration is read at the boundary, not when a slow tool later settles.
|
|
912
|
+
group.summarizeWhenSettled = readConfig('user').summarize_tool_calls;
|
|
913
|
+
generateSettledToolGroup(group);
|
|
914
|
+
};
|
|
915
|
+
const visibleBoundary = (prose) => {
|
|
916
|
+
finishOpenToolGroup();
|
|
917
|
+
lastToolGroupBoundaryProse = prose;
|
|
918
|
+
};
|
|
919
|
+
const observeToolGroupEvent = (event) => {
|
|
920
|
+
// The viewer turns every compaction_end into a summary, cancellation, or
|
|
921
|
+
// error line, each of which is a hard transcript seam.
|
|
922
|
+
if (event.type === 'compaction_end') {
|
|
923
|
+
finishOpenToolGroup();
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
// A failed automatic retry is rendered as ChatView's visible error line.
|
|
927
|
+
// Successful retries only clear activity and remain within the same group.
|
|
928
|
+
if (event.type === 'auto_retry_end' && event.success !== true) {
|
|
929
|
+
finishOpenToolGroup();
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
// The attach viewer renders aborted and errored assistant endings as visible
|
|
933
|
+
// transcript seams. Close and settle the pre-abort group here so pending tool
|
|
934
|
+
// arguments can be summarized without pulling the seam into the recap.
|
|
935
|
+
if (event.type === 'message_end') {
|
|
936
|
+
const message = event.message;
|
|
937
|
+
if (message !== null && typeof message === 'object') {
|
|
938
|
+
const item = message;
|
|
939
|
+
if (item.role === 'assistant' && (item.stopReason === 'aborted' || item.stopReason === 'error')) {
|
|
940
|
+
finishOpenToolGroup(true);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
if (event.type === 'tool_execution_start') {
|
|
946
|
+
const group = ensureOpenToolGroup();
|
|
947
|
+
const id = typeof event.toolCallId === 'string' ? event.toolCallId : '';
|
|
948
|
+
if (group.key === undefined && id !== '')
|
|
949
|
+
group.key = id;
|
|
950
|
+
if (id !== '') {
|
|
951
|
+
group.pendingToolIds.add(id);
|
|
952
|
+
toolGroupsByCallId.set(id, group);
|
|
953
|
+
}
|
|
954
|
+
group.toolCount += 1;
|
|
955
|
+
group.pieces.push({ type: 'tool', name: typeof event.toolName === 'string' ? event.toolName : 'tool', args: event.args });
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
if (event.type === 'tool_execution_end') {
|
|
959
|
+
const id = typeof event.toolCallId === 'string' ? event.toolCallId : '';
|
|
960
|
+
const group = id === '' ? openToolGroup : toolGroupsByCallId.get(id);
|
|
961
|
+
if (group !== undefined) {
|
|
962
|
+
group.pieces.push({
|
|
963
|
+
type: 'result',
|
|
964
|
+
name: typeof event.toolName === 'string' ? event.toolName : 'tool',
|
|
965
|
+
result: event.result,
|
|
966
|
+
isError: event.isError === true,
|
|
967
|
+
});
|
|
968
|
+
if (id !== '') {
|
|
969
|
+
group.pendingToolIds.delete(id);
|
|
970
|
+
toolGroupsByCallId.delete(id);
|
|
971
|
+
}
|
|
972
|
+
generateSettledToolGroup(group);
|
|
973
|
+
}
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
if (event.type !== 'message_start' && event.type !== 'message_update')
|
|
977
|
+
return;
|
|
978
|
+
const message = event.message;
|
|
979
|
+
if (message === null || typeof message !== 'object')
|
|
980
|
+
return;
|
|
981
|
+
const item = message;
|
|
982
|
+
const role = item.role;
|
|
983
|
+
if (role === 'assistant') {
|
|
984
|
+
if (event.type === 'message_start')
|
|
985
|
+
sawAssistantProse = false;
|
|
986
|
+
const prose = assistantVisibleText(item);
|
|
987
|
+
if (!sawAssistantProse && prose.trim() !== '') {
|
|
988
|
+
sawAssistantProse = true;
|
|
989
|
+
visibleBoundary(prose);
|
|
990
|
+
}
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
if (event.type !== 'message_start')
|
|
994
|
+
return;
|
|
995
|
+
if (role === 'compactionSummary' || role === 'branchSummary' || role === 'bashExecution' || role === 'status' || role === 'error' || item.customType === 'crtr-cycle-divider') {
|
|
996
|
+
finishOpenToolGroup();
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
if (isTrueUserMessage(item)) {
|
|
1000
|
+
visibleBoundary(generatedContextText(item));
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
if (item.display === false)
|
|
1004
|
+
return;
|
|
1005
|
+
const text = generatedContextText(item);
|
|
1006
|
+
const isBearingsOrRevive = item.customType === 'crtr-context' || (role === 'user' && text.startsWith('You have been revived fresh after a context refresh'));
|
|
1007
|
+
if (isBearingsOrRevive) {
|
|
1008
|
+
if (openToolGroup !== undefined && text !== '')
|
|
1009
|
+
openToolGroup.pieces.push({ type: 'text', text });
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
// Generated inbox/situational/nudge and visible custom output are part of
|
|
1013
|
+
// the between-message activity and may begin a group before its first tool.
|
|
1014
|
+
const group = ensureOpenToolGroup();
|
|
1015
|
+
if (text !== '')
|
|
1016
|
+
group.pieces.push({ type: 'text', text });
|
|
1017
|
+
};
|
|
732
1018
|
// ---------------------------------------------------------------------------
|
|
733
1019
|
// F2: message_update coalescing. `relayEvent` is the single entry point for
|
|
734
1020
|
// engine events headed to viewers: a message_update is held (latest wins) and
|
|
@@ -797,9 +1083,9 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
797
1083
|
// behavior.
|
|
798
1084
|
let kickoffPending;
|
|
799
1085
|
let kickoffInFlight = false;
|
|
800
|
-
//
|
|
801
|
-
//
|
|
802
|
-
//
|
|
1086
|
+
// One /login can fan the same reload into this broker through several clients.
|
|
1087
|
+
// Coalesce those frames onto one local auth refresh + route reconciliation so
|
|
1088
|
+
// an idle prompt can await the exact work promised by the reload ack.
|
|
803
1089
|
let reloadAuthInFlight = null;
|
|
804
1090
|
// A fresh auth reload is also the explicit recovery trigger for a turn parked
|
|
805
1091
|
// on a fatal provider-auth fault. Keep at most one rewind + prompt in flight
|
|
@@ -951,9 +1237,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
951
1237
|
// node from reviving straight back into fault-recovery chrome.
|
|
952
1238
|
if (unconfigured)
|
|
953
1239
|
clearFault(nodeId, { link: 'pi→provider' });
|
|
954
|
-
await operationIdContext.fresh(() => session.prompt(unconfigured
|
|
955
|
-
? `The previous model (${currentSpec}) had no usable provider credential, so you were automatically switched to ${resolved}. Continue the task from where the failed turn left off.`
|
|
956
|
-
: `The previous model (${currentSpec}) was unavailable (provider 404 not_found), so you were automatically switched to ${resolved}. Continue the task from where the failed turn left off.`));
|
|
1240
|
+
await operationIdContext.fresh(() => session.prompt(formatModelFallbackRecovery(currentSpec, resolved, unconfigured ? 'credential' : 'not-found')));
|
|
957
1241
|
})()
|
|
958
1242
|
.catch((err) => {
|
|
959
1243
|
emitEvent({
|
|
@@ -1158,6 +1442,15 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1158
1442
|
maybeFallbackOnUnusableModel(event);
|
|
1159
1443
|
const type = event.type;
|
|
1160
1444
|
if (type === 'agent_start') {
|
|
1445
|
+
try {
|
|
1446
|
+
workingActivity = randomWorkingActivity(readConfig('user').working_gerunds);
|
|
1447
|
+
}
|
|
1448
|
+
catch {
|
|
1449
|
+
workingActivity = DEFAULT_WORKING_ACTIVITY;
|
|
1450
|
+
}
|
|
1451
|
+
// Announce the selected label before relaying pi's verbatim agent_start so
|
|
1452
|
+
// every viewer paints this turn with the same random choice.
|
|
1453
|
+
broadcast({ type: 'working_activity', activity: workingActivity });
|
|
1161
1454
|
notifyTurnAccepted();
|
|
1162
1455
|
emitStartupMilestone('broker.startup.agent_start', formatModelSpec(session.model, session.thinkingLevel));
|
|
1163
1456
|
}
|
|
@@ -1208,24 +1501,33 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1208
1501
|
return;
|
|
1209
1502
|
}
|
|
1210
1503
|
// Any non-update event: flush the held update FIRST so a viewer never sees
|
|
1211
|
-
// (e.g.) message_end before the update it supersedes
|
|
1504
|
+
// (e.g.) message_end before the update it supersedes. A refresh-yield aborts
|
|
1505
|
+
// its own turn; the visible cycle divider names that boundary, so do not also
|
|
1506
|
+
// render it as an operation failure.
|
|
1212
1507
|
flushPendingUpdate();
|
|
1508
|
+
if (type === 'message_end' && getNode(nodeId)?.intent === 'refresh') {
|
|
1509
|
+
const messageEnd = event;
|
|
1510
|
+
broadcast({ ...messageEnd, message: withoutYieldAbort(messageEnd.message) });
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1213
1513
|
broadcast(event);
|
|
1214
1514
|
};
|
|
1215
|
-
// Persist
|
|
1216
|
-
//
|
|
1217
|
-
//
|
|
1218
|
-
// persona/spawn
|
|
1219
|
-
//
|
|
1515
|
+
// Persist the live model + thinking level into the node's durable launch
|
|
1516
|
+
// recipe so a daemon restart/revive recreates the settings the user sees.
|
|
1517
|
+
// pi's model/thinking controls only mutate the in-memory engine; without this
|
|
1518
|
+
// the node reverts to its persona/spawn settings on the next broker boot. We
|
|
1519
|
+
// write the full `provider/id:thinking` spec to BOTH model_override (so
|
|
1220
1520
|
// polymorphs preserve it via buildLaunchSpec) and launch.model (the recipe
|
|
1221
1521
|
// buildPiArgv replays on revive). Best-effort: a degenerate/ephemeral node
|
|
1222
1522
|
// with no canvas row just skips persistence. CRTR_NODE_ID is set in the
|
|
1223
1523
|
// broker's own env (merged by piInvocationToSdkConfig at boot).
|
|
1224
1524
|
// `pinnedOverride`, when passed, is the durable launch-recipe decision for
|
|
1225
|
-
// an explicit model change, derived from its raw pre-normalize token.
|
|
1226
|
-
//
|
|
1227
|
-
//
|
|
1228
|
-
|
|
1525
|
+
// an explicit model change, derived from its raw pre-normalize token. A
|
|
1526
|
+
// `userSelected` change also clears the older logical route intent: otherwise
|
|
1527
|
+
// resolveLaunchModel ignores the newly persisted concrete spec at reboot and
|
|
1528
|
+
// resolves the persona's original family/strength again. Algorithmic provider
|
|
1529
|
+
// fallback leaves that intent intact.
|
|
1530
|
+
const persistModelChoice = (pinnedOverride, userSelected = false) => {
|
|
1229
1531
|
const nodeId = process.env['CRTR_NODE_ID'];
|
|
1230
1532
|
const spec = formatModelSpec(session.model, session.thinkingLevel);
|
|
1231
1533
|
if (nodeId === undefined || nodeId === '' || spec === null)
|
|
@@ -1236,7 +1538,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1236
1538
|
return;
|
|
1237
1539
|
const launch = meta.launch === undefined
|
|
1238
1540
|
? undefined
|
|
1239
|
-
:
|
|
1541
|
+
: userSelected
|
|
1240
1542
|
? (() => {
|
|
1241
1543
|
const { modelIntent: _modelIntent, env, ...rest } = meta.launch;
|
|
1242
1544
|
const { CRTR_MODEL_INTENT: _intentEnv, ...remainingEnv } = env;
|
|
@@ -1248,7 +1550,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1248
1550
|
};
|
|
1249
1551
|
})()
|
|
1250
1552
|
: { ...meta.launch, model: spec, ...(pinnedOverride !== undefined ? { modelProviderPinned: pinnedOverride } : {}) };
|
|
1251
|
-
if (
|
|
1553
|
+
if (userSelected) {
|
|
1252
1554
|
delete process.env['CRTR_MODEL_INTENT'];
|
|
1253
1555
|
cfg.model = spec;
|
|
1254
1556
|
}
|
|
@@ -1262,13 +1564,13 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1262
1564
|
// set_model/cycle_model the broker announces it itself — otherwise the new
|
|
1263
1565
|
// model reaches no viewer (the requester gets only a bare ack) and every
|
|
1264
1566
|
// footer shows the stale model until the next unrelated event.
|
|
1265
|
-
const broadcastModelChanged = (persist = true, pinnedOverride,
|
|
1567
|
+
const broadcastModelChanged = (persist = true, pinnedOverride, userSelected = false) => {
|
|
1266
1568
|
if (persist)
|
|
1267
|
-
persistModelChoice(pinnedOverride,
|
|
1569
|
+
persistModelChoice(pinnedOverride, userSelected);
|
|
1268
1570
|
broadcast({ type: 'model_changed', model: isUnknownModel(session.model) ? undefined : session.model, spec: formatModelSpec(session.model, session.thinkingLevel) });
|
|
1269
1571
|
};
|
|
1270
1572
|
const buildSnapshot = () => ({
|
|
1271
|
-
messages: snapshotMessages(session),
|
|
1573
|
+
messages: snapshotMessages(session, reviewCompanion),
|
|
1272
1574
|
stats: session.getSessionStats(),
|
|
1273
1575
|
// pi's `get_state` (rpc-mode.js), field for field, from the same getters.
|
|
1274
1576
|
// The ONE divergence: a model-less session (undefined OR the SDK
|
|
@@ -1295,6 +1597,8 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1295
1597
|
widgets: Object.fromEntries([...displayWidgets].map(([k, w]) => [k, { lines: [...w.lines], placement: w.placement }])),
|
|
1296
1598
|
title: displayTitle,
|
|
1297
1599
|
},
|
|
1600
|
+
toolGroupSummaries: Object.fromEntries(toolGroupSummaries),
|
|
1601
|
+
workingActivity,
|
|
1298
1602
|
});
|
|
1299
1603
|
// Send a client its catch-up snapshot. welcome.pending_dialog carries a single
|
|
1300
1604
|
// still-in-flight dialog to a WRITABLE client attaching mid-dialog; an observer
|
|
@@ -1337,10 +1641,28 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1337
1641
|
// The single exit helper (plan §0): dispose the engine, close + unlink the
|
|
1338
1642
|
// socket, exit 0. Idempotent — every trigger converges here.
|
|
1339
1643
|
// -------------------------------------------------------------------------
|
|
1340
|
-
const
|
|
1644
|
+
const completeExit = (status) => {
|
|
1341
1645
|
if (disposed)
|
|
1342
1646
|
return;
|
|
1343
1647
|
disposed = true;
|
|
1648
|
+
// Daemon teardown cannot wait for a recap's 45-second model timeout. Preserve
|
|
1649
|
+
// exactly the newest already-capped input so the successor broker can finish
|
|
1650
|
+
// it, persist the normal summary, and remove the handoff record.
|
|
1651
|
+
const pendingSummary = [...generatingToolGroups.values()].at(-1);
|
|
1652
|
+
if (pendingSummary !== undefined) {
|
|
1653
|
+
try {
|
|
1654
|
+
atomicWriteJson(pendingToolSummaryPath, pendingSummary);
|
|
1655
|
+
}
|
|
1656
|
+
catch (error) {
|
|
1657
|
+
emitEvent({
|
|
1658
|
+
level: 'warn',
|
|
1659
|
+
event: 'broker.runtime.diagnostic',
|
|
1660
|
+
fields: {
|
|
1661
|
+
message: `failed to persist pending tool summary: ${error instanceof Error ? error.message : String(error)}`,
|
|
1662
|
+
},
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1344
1666
|
streamWatchdog.reset();
|
|
1345
1667
|
// disposeActiveSession atomically clears the process-global pointer before
|
|
1346
1668
|
// disposing — whichever engine session (committed or an in-flight rebind
|
|
@@ -1381,6 +1703,34 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1381
1703
|
}
|
|
1382
1704
|
process.exit(status);
|
|
1383
1705
|
};
|
|
1706
|
+
const disposeAndExit = (reason, status = 0) => {
|
|
1707
|
+
if (disposed)
|
|
1708
|
+
return;
|
|
1709
|
+
const graceful = reason === 'shutdown-hook' || reason === 'shutdown-after-settle';
|
|
1710
|
+
// A graceful shutdown (yield, dormancy, daemon restart) is the final
|
|
1711
|
+
// boundary of the conversation's last tool group. Group state lives only in
|
|
1712
|
+
// this process, so a group left open here is unsummarizable forever and its
|
|
1713
|
+
// calls render raw in every future view of the transcript. Close it now —
|
|
1714
|
+
// generateSettledToolGroup registers its task synchronously, so the wait
|
|
1715
|
+
// below covers the summary it launches. Any pending tool arguments are
|
|
1716
|
+
// included without inventing a result; the visible abort remains outside
|
|
1717
|
+
// the recap and Ctrl+O retains every underlying call row.
|
|
1718
|
+
if (graceful)
|
|
1719
|
+
finishOpenToolGroup(true);
|
|
1720
|
+
// A normal terminal shutdown follows a visible boundary that may have just
|
|
1721
|
+
// launched a capped (45s) headless summary. Keep the broker and its child
|
|
1722
|
+
// alive until those already-started tasks persist/broadcast, rather than
|
|
1723
|
+
// process.exit() orphaning pi and losing the sidecar. Abrupt failures and
|
|
1724
|
+
// signals retain their existing immediate teardown behavior.
|
|
1725
|
+
if (graceful && summaryTasks.size > 0) {
|
|
1726
|
+
if (disposePending)
|
|
1727
|
+
return;
|
|
1728
|
+
disposePending = true;
|
|
1729
|
+
void Promise.allSettled([...summaryTasks]).finally(() => completeExit(status));
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
completeExit(status);
|
|
1733
|
+
};
|
|
1384
1734
|
// A stophook can request shutdown while pi is still unwinding a completed
|
|
1385
1735
|
// turn. Let that turn reach agent_settled first: inbox delivery commits its
|
|
1386
1736
|
// durable cursor there, and only there. Explicit operator shutdown and OS
|
|
@@ -1491,6 +1841,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1491
1841
|
return;
|
|
1492
1842
|
}
|
|
1493
1843
|
}
|
|
1844
|
+
observeToolGroupEvent(event);
|
|
1494
1845
|
relayEvent(event, generation);
|
|
1495
1846
|
if (event.type === 'agent_settled')
|
|
1496
1847
|
scheduleFaultRetry();
|
|
@@ -1526,6 +1877,9 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1526
1877
|
providerFallbackInFlight = null;
|
|
1527
1878
|
notFoundFallbackInFlight = null;
|
|
1528
1879
|
reloadAuthInFlight = null;
|
|
1880
|
+
openToolGroup = undefined;
|
|
1881
|
+
toolGroupsByCallId.clear();
|
|
1882
|
+
sawAssistantProse = false;
|
|
1529
1883
|
await candidateSession.bindExtensions({
|
|
1530
1884
|
uiContext,
|
|
1531
1885
|
// 'rpc' — NOT 'print'. Per pi docs/rpc.md:1078 this is exactly our shape:
|
|
@@ -1689,6 +2043,36 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1689
2043
|
failure = err;
|
|
1690
2044
|
sendFrame(client, { type: 'error', code: 'engine_error', message: String(err) });
|
|
1691
2045
|
};
|
|
2046
|
+
// `prompt { interrupt: true }` is one atomic viewer intention: end whatever
|
|
2047
|
+
// is producing now, then start this as a new turn. A normal mid-stream
|
|
2048
|
+
// prompt deliberately steers into pi's queue; the whip must not.
|
|
2049
|
+
if (frame.type === 'prompt' && frame.interrupt === true) {
|
|
2050
|
+
return (async () => {
|
|
2051
|
+
if (session.isBashRunning) {
|
|
2052
|
+
session.abortBash();
|
|
2053
|
+
}
|
|
2054
|
+
else {
|
|
2055
|
+
// A prompt can be in pi's async setup before isStreaming flips. Match
|
|
2056
|
+
// the correlated interrupt's guard so a whip cannot silently miss it.
|
|
2057
|
+
if (!session.isStreaming && pendingPromptDispatches > 0) {
|
|
2058
|
+
const deadline = Date.now() + 5_000;
|
|
2059
|
+
while (Date.now() < deadline && !session.isStreaming && pendingPromptDispatches > 0) {
|
|
2060
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
if (session.isStreaming)
|
|
2064
|
+
await session.abort();
|
|
2065
|
+
}
|
|
2066
|
+
return driveEngine(client, {
|
|
2067
|
+
type: 'prompt',
|
|
2068
|
+
text: frame.text,
|
|
2069
|
+
...(frame.images !== undefined ? { images: frame.images } : {}),
|
|
2070
|
+
});
|
|
2071
|
+
})().catch((err) => {
|
|
2072
|
+
relayError(err);
|
|
2073
|
+
return failure;
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
1692
2076
|
// Hidden `crtr-memory-refs` guidance rides ALONGSIDE the human frame's own
|
|
1693
2077
|
// engine call, never rewriting `frame.text` — see resolveFrameGuidance /
|
|
1694
2078
|
// chooseGuidanceDeliveryMode above. A failed SEND (as opposed to a failed
|
|
@@ -1710,38 +2094,65 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1710
2094
|
// before the caught promise resolves, so reading it after the chain is
|
|
1711
2095
|
// the call's verdict.
|
|
1712
2096
|
const settle = (p) => p.then(() => failure);
|
|
1713
|
-
const
|
|
1714
|
-
|
|
1715
|
-
|
|
2097
|
+
const dispatch = () => {
|
|
2098
|
+
const route = resolveEngineRoute(frame, session.isStreaming);
|
|
2099
|
+
const guidance = resolveFrameGuidance(frame.text);
|
|
2100
|
+
if (guidance === null) {
|
|
2101
|
+
if (route.call === 'prompt') {
|
|
2102
|
+
// A routed prompt STARTS a turn — including an idle follow_up
|
|
2103
|
+
// escalated by m-C — so it gets a fresh operation id. A true
|
|
2104
|
+
// (mid-stream) followUp rides the current turn's operation, like
|
|
2105
|
+
// steer.
|
|
2106
|
+
return settle(trackPromptDispatch(operationIdContext.fresh(() => session.prompt(frame.text, route.options).catch(relayError))));
|
|
2107
|
+
}
|
|
2108
|
+
// C1: pi's followUp() takes images as a POSITIONAL 2nd arg (`followUp(text, images?)`).
|
|
2109
|
+
return settle(session.followUp(frame.text, frame.images).catch(relayError));
|
|
2110
|
+
}
|
|
2111
|
+
const deliverAs = chooseGuidanceDeliveryMode(frame.type, route.call, session.isStreaming);
|
|
1716
2112
|
if (route.call === 'prompt') {
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
2113
|
+
if (deliverAs === 'nextTurn') {
|
|
2114
|
+
// The ONLY ordering where guidance precedes the human call — pi's
|
|
2115
|
+
// prompt() reads+clears its pending-nextTurn queue synchronously
|
|
2116
|
+
// at call time, injecting it into the SAME turn as the new user
|
|
2117
|
+
// message. promptWithNextTurnGuidance queues the guidance and then
|
|
2118
|
+
// ATOMICALLY clears its own pending entry if prompt() settles
|
|
2119
|
+
// without ever reaching that drain (an early throw/return leaves
|
|
2120
|
+
// nothing stranded to leak into a later, unrelated turn).
|
|
2121
|
+
return settle(trackPromptDispatch(promptWithNextTurnGuidance(session, guidance, () => operationIdContext.fresh(() => session.prompt(frame.text, route.options))).catch(relayError)));
|
|
2122
|
+
}
|
|
2123
|
+
return settle(trackPromptDispatch(operationIdContext.fresh(() => session.prompt(frame.text, route.options).catch(relayError).then(() => sendGuidance(guidance, deliverAs)))));
|
|
1722
2124
|
}
|
|
1723
|
-
//
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
2125
|
+
// deliverAs is always 'followUp' here per chooseGuidanceDeliveryMode's
|
|
2126
|
+
// contract (a followUp routeCall never yields 'nextTurn').
|
|
2127
|
+
return settle(session.followUp(frame.text, frame.images).catch(relayError).then(() => sendGuidance(guidance, deliverAs)));
|
|
2128
|
+
};
|
|
2129
|
+
// /login acknowledges before its asynchronous local refresh completes.
|
|
2130
|
+
// An idle turn must not overtake that refresh and launch on the stale
|
|
2131
|
+
// route; true mid-stream follow-ups keep their immediate queue behavior.
|
|
2132
|
+
const authReload = session.isStreaming ? undefined : reloadAuthInFlight?.promise;
|
|
2133
|
+
if (authReload === undefined)
|
|
2134
|
+
return dispatch();
|
|
2135
|
+
const waitingGeneration = installedGeneration;
|
|
2136
|
+
const waitingSession = session;
|
|
2137
|
+
return authReload.then(() => {
|
|
2138
|
+
if (installedGeneration !== waitingGeneration || session !== waitingSession) {
|
|
2139
|
+
relayError(new Error('session changed while prompt waited for authentication reload'));
|
|
2140
|
+
return failure;
|
|
1737
2141
|
}
|
|
1738
|
-
return
|
|
1739
|
-
}
|
|
1740
|
-
// deliverAs is always 'followUp' here per chooseGuidanceDeliveryMode's
|
|
1741
|
-
// contract (a followUp routeCall never yields 'nextTurn').
|
|
1742
|
-
return settle(session.followUp(frame.text, frame.images).catch(relayError).then(() => sendGuidance(guidance, deliverAs)));
|
|
2142
|
+
return dispatch();
|
|
2143
|
+
});
|
|
1743
2144
|
}
|
|
1744
2145
|
case 'steer': {
|
|
2146
|
+
// A viewer submits ordinary text as `steer` while the agent is running,
|
|
2147
|
+
// including a slash command injected by the tmux prefix menu. pi's
|
|
2148
|
+
// steer() deliberately rejects registered extension commands because
|
|
2149
|
+
// they cannot be queued; prompt() performs the command preflight first
|
|
2150
|
+
// and executes it immediately even while streaming. It also expands a
|
|
2151
|
+
// matching skill/template before applying the same steer behavior.
|
|
2152
|
+
if (resolveSteerCall(frame.text, session) === 'prompt') {
|
|
2153
|
+
void session.prompt(frame.text, { images: frame.images, streamingBehavior: 'steer' }).catch(relayError);
|
|
2154
|
+
break;
|
|
2155
|
+
}
|
|
1745
2156
|
const guidance = resolveFrameGuidance(frame.text);
|
|
1746
2157
|
// C1: pi's steer() takes images as a POSITIONAL 2nd arg (`steer(text, images?)`).
|
|
1747
2158
|
if (guidance === null) {
|
|
@@ -1810,6 +2221,9 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1810
2221
|
// Order before any held assistant message_update, so a `!` run mid-stream
|
|
1811
2222
|
// never renders ahead of the update it follows.
|
|
1812
2223
|
flushPendingUpdate();
|
|
2224
|
+
// The live bash component is a visible transcript boundary, even though
|
|
2225
|
+
// its persisted bashExecution message arrives later via the session.
|
|
2226
|
+
finishOpenToolGroup();
|
|
1813
2227
|
broadcast({ type: 'bash_start', command, excludeFromContext: exclude });
|
|
1814
2228
|
void session
|
|
1815
2229
|
.executeBash(command, (chunk) => broadcast({ type: 'bash_output', chunk }), {
|
|
@@ -1867,6 +2281,16 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
1867
2281
|
// pass no id (unchanged behavior).
|
|
1868
2282
|
...(id !== undefined ? { id } : {}),
|
|
1869
2283
|
});
|
|
2284
|
+
const isPreBoundaryReviewEntry = (entryId) => reviewCompanion &&
|
|
2285
|
+
!session.sessionManager.getBranch(entryId).some((entry) => entry.type === 'custom_message' && entry.customType === REVIEW_BOUNDARY_CUSTOM_TYPE);
|
|
2286
|
+
const reviewBoundaryRefusalTo = (client, targetId) => {
|
|
2287
|
+
const refusal = new ReviewBoundaryNavigationError(targetId);
|
|
2288
|
+
sendFrame(client, {
|
|
2289
|
+
type: 'error',
|
|
2290
|
+
code: refusal.code,
|
|
2291
|
+
message: refusal.message,
|
|
2292
|
+
});
|
|
2293
|
+
};
|
|
1870
2294
|
/** Resolve a `provider/id` model spec against the LIVE services registry (which
|
|
1871
2295
|
* carries any extension-registered providers). Pure: returns undefined on a
|
|
1872
2296
|
* malformed spec or an unknown model. */
|
|
@@ -2007,17 +2431,29 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
2007
2431
|
});
|
|
2008
2432
|
const buildGetTreeData = (id) => {
|
|
2009
2433
|
const sm = session.sessionManager;
|
|
2010
|
-
const
|
|
2434
|
+
const tree = reviewCompanion ? boundaryRootedTree(sm.getTree()) : sm.getTree();
|
|
2435
|
+
const visibleEntryIds = new Set();
|
|
2436
|
+
const collectEntryIds = (nodes) => {
|
|
2437
|
+
for (const node of nodes) {
|
|
2438
|
+
visibleEntryIds.add(node.entry.id);
|
|
2439
|
+
collectEntryIds(node.children);
|
|
2440
|
+
}
|
|
2441
|
+
};
|
|
2442
|
+
collectEntryIds(tree);
|
|
2443
|
+
const forkPoints = session.getUserMessagesForForking()
|
|
2444
|
+
.filter((u) => visibleEntryIds.has(u.entryId))
|
|
2445
|
+
.map((u) => ({
|
|
2011
2446
|
id: u.entryId,
|
|
2012
2447
|
text: u.text,
|
|
2013
2448
|
timestamp: sm.getEntry(u.entryId)?.timestamp,
|
|
2014
2449
|
}));
|
|
2450
|
+
const currentLeafId = sm.getLeafId();
|
|
2015
2451
|
return {
|
|
2016
2452
|
type: 'data',
|
|
2017
2453
|
id,
|
|
2018
2454
|
kind: 'get_tree',
|
|
2019
|
-
tree
|
|
2020
|
-
currentLeafId:
|
|
2455
|
+
tree,
|
|
2456
|
+
currentLeafId: currentLeafId !== null && visibleEntryIds.has(currentLeafId) ? currentLeafId : null,
|
|
2021
2457
|
forkPoints,
|
|
2022
2458
|
};
|
|
2023
2459
|
};
|
|
@@ -2365,7 +2801,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
2365
2801
|
.cycleModel(frame.direction)
|
|
2366
2802
|
.then(() => {
|
|
2367
2803
|
ackTo(client, 'cycle_model');
|
|
2368
|
-
broadcastModelChanged();
|
|
2804
|
+
broadcastModelChanged(true, undefined, true);
|
|
2369
2805
|
})
|
|
2370
2806
|
.catch(engineErrorTo(client));
|
|
2371
2807
|
break;
|
|
@@ -2414,7 +2850,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
2414
2850
|
if (nextSpec.thinkingLevel !== undefined)
|
|
2415
2851
|
session.setThinkingLevel(nextSpec.thinkingLevel);
|
|
2416
2852
|
ackTo(client, 'cycle_ladder');
|
|
2417
|
-
broadcastModelChanged();
|
|
2853
|
+
broadcastModelChanged(true, undefined, true);
|
|
2418
2854
|
})
|
|
2419
2855
|
.catch(engineErrorTo(client));
|
|
2420
2856
|
break;
|
|
@@ -2424,6 +2860,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
2424
2860
|
break;
|
|
2425
2861
|
try {
|
|
2426
2862
|
session.cycleThinkingLevel();
|
|
2863
|
+
persistModelChoice(undefined, true);
|
|
2427
2864
|
ackTo(client, 'cycle_thinking');
|
|
2428
2865
|
}
|
|
2429
2866
|
catch (err) {
|
|
@@ -2448,6 +2885,7 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
2448
2885
|
break;
|
|
2449
2886
|
try {
|
|
2450
2887
|
session.setThinkingLevel(frame.level);
|
|
2888
|
+
persistModelChoice(undefined, true);
|
|
2451
2889
|
ackTo(client, 'set_thinking_level');
|
|
2452
2890
|
}
|
|
2453
2891
|
catch (err) {
|
|
@@ -2602,6 +3040,10 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
2602
3040
|
case 'navigate_tree': {
|
|
2603
3041
|
if (notWritable(client, 'navigate the session tree'))
|
|
2604
3042
|
break;
|
|
3043
|
+
if (isPreBoundaryReviewEntry(frame.targetId)) {
|
|
3044
|
+
reviewBoundaryRefusalTo(client, frame.targetId);
|
|
3045
|
+
break;
|
|
3046
|
+
}
|
|
2605
3047
|
// navigateTree rewinds IN-PLACE (same session file, new leaf) and emits no
|
|
2606
3048
|
// relayed event, so every viewer must be re-snapshotted onto the rewound
|
|
2607
3049
|
// transcript — same reWelcomeAll the session-replacing ops use. The ack's
|
|
@@ -2672,6 +3114,10 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
2672
3114
|
case 'fork': {
|
|
2673
3115
|
if (notWritable(client, 'fork the session'))
|
|
2674
3116
|
break;
|
|
3117
|
+
if (isPreBoundaryReviewEntry(frame.entryId)) {
|
|
3118
|
+
reviewBoundaryRefusalTo(client, frame.entryId);
|
|
3119
|
+
break;
|
|
3120
|
+
}
|
|
2675
3121
|
runReplacement(client, 'fork', (rt) => rt.fork(frame.entryId));
|
|
2676
3122
|
break;
|
|
2677
3123
|
}
|
|
@@ -2724,58 +3170,61 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
2724
3170
|
}
|
|
2725
3171
|
case 'reload_auth': {
|
|
2726
3172
|
// Open to any client, observers included: reload_auth is an idempotent
|
|
2727
|
-
// local re-read of
|
|
2728
|
-
// does
|
|
2729
|
-
//
|
|
2730
|
-
// to every live broker) ride an observer connection.
|
|
3173
|
+
// local re-read of shared credentials + the runtime's availability
|
|
3174
|
+
// snapshot. It does not steer the conversation, so the daemon's
|
|
3175
|
+
// canvas-wide /login fan can ride an observer connection.
|
|
2731
3176
|
try {
|
|
2732
|
-
// pi 0.82: crouter's CredentialStore reads auth.json fresh under the
|
|
2733
|
-
// lock on every call, so there is no cached credential view left to
|
|
2734
|
-
// reload — only the runtime's models/availability snapshot has to be
|
|
2735
|
-
// recomputed, and that is asynchronous. Every consumer below awaits
|
|
2736
|
-
// the same promise so none of them reads a pre-login snapshot.
|
|
2737
|
-
const authRefreshed = registryOf(services).refresh().catch((err) => {
|
|
2738
|
-
emitEvent({ level: 'warn', event: 'broker.auth_reload.refresh_failed', error: err });
|
|
2739
|
-
});
|
|
2740
3177
|
ackTo(client, 'reload_auth');
|
|
2741
|
-
void authRefreshed.then(() => broadcastModelChanged());
|
|
2742
|
-
// Tier 3 re-drive: a model-less boot (no viable candidate at launch)
|
|
2743
|
-
// stays live so the user can /login; once auth actually changes,
|
|
2744
|
-
// re-run the SAME launch policy against the now-refreshed services
|
|
2745
|
-
// and, if a model now resolves, adopt it and fire the deferred
|
|
2746
|
-
// kickoff. handleFrame is not async (broker.ts) so this runs as a
|
|
2747
|
-
// detached IIFE, the same async-in-sync-switch pattern used above
|
|
2748
|
-
// for provider/not-found fallback.
|
|
2749
3178
|
const authGeneration = installedGeneration;
|
|
2750
|
-
|
|
3179
|
+
const existingAuthReload = reloadAuthInFlight;
|
|
3180
|
+
let authReloadPromise;
|
|
3181
|
+
if (existingAuthReload !== null && existingAuthReload.generation === authGeneration) {
|
|
3182
|
+
authReloadPromise = existingAuthReload.promise;
|
|
3183
|
+
}
|
|
3184
|
+
if (authGeneration !== undefined && authReloadPromise === undefined) {
|
|
2751
3185
|
const authSession = session;
|
|
2752
3186
|
const authServices = services;
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
3187
|
+
authReloadPromise = (async () => {
|
|
3188
|
+
// Credentials are read fresh by PoolCredentialStore; only the
|
|
3189
|
+
// ModelRuntime snapshot needs rebuilding. Authentication reloads
|
|
3190
|
+
// never fetch provider catalogs over the network.
|
|
3191
|
+
await authServices.modelRuntime.refresh({ allowNetwork: false });
|
|
3192
|
+
if (installedGeneration !== authGeneration || session !== authSession)
|
|
3193
|
+
return;
|
|
3194
|
+
const modelLess = authSession.model === undefined || isUnknownModel(authSession.model);
|
|
3195
|
+
const reconcilesLogicalRoute = cfg.modelExact !== true
|
|
3196
|
+
&& cfg.modelProviderPinned !== true
|
|
3197
|
+
&& (process.env['CRTR_MODEL_INTENT'] ?? '').trim() !== '';
|
|
3198
|
+
if (modelLess || reconcilesLogicalRoute) {
|
|
2757
3199
|
const resolved = await resolveLaunchModel(authServices, cfg);
|
|
3200
|
+
if (installedGeneration !== authGeneration || session !== authSession)
|
|
3201
|
+
return;
|
|
2758
3202
|
if (resolved !== undefined) {
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
3203
|
+
const currentSpec = formatModelSpec(authSession.model, authSession.thinkingLevel);
|
|
3204
|
+
const resolvedSpec = formatModelSpec(resolved.model, resolved.thinkingLevel);
|
|
3205
|
+
if (resolvedSpec !== null && resolvedSpec !== currentSpec) {
|
|
3206
|
+
await authSession.setModel(resolved.model);
|
|
3207
|
+
if (installedGeneration !== authGeneration || session !== authSession)
|
|
3208
|
+
return;
|
|
3209
|
+
authSession.setThinkingLevel((resolved.thinkingLevel ?? 'off'));
|
|
3210
|
+
}
|
|
2765
3211
|
}
|
|
2766
3212
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
3213
|
+
if (installedGeneration !== authGeneration || session !== authSession)
|
|
3214
|
+
return;
|
|
3215
|
+
// Persist an algorithmic route choice without clearing the logical
|
|
3216
|
+
// intent that makes future reconciliation possible.
|
|
3217
|
+
broadcastModelChanged();
|
|
3218
|
+
drivePendingKickoff();
|
|
3219
|
+
})().catch((err) => {
|
|
3220
|
+
emitEvent({ level: 'warn', event: 'broker.auth_reload.reconcile_failed', error: err });
|
|
3221
|
+
});
|
|
3222
|
+
const work = { generation: authGeneration, promise: authReloadPromise };
|
|
3223
|
+
reloadAuthInFlight = work;
|
|
3224
|
+
void authReloadPromise.finally(() => {
|
|
3225
|
+
if (reloadAuthInFlight === work)
|
|
3226
|
+
reloadAuthInFlight = null;
|
|
3227
|
+
});
|
|
2779
3228
|
}
|
|
2780
3229
|
const authFault = readFault(nodeId);
|
|
2781
3230
|
const authFaultGeneration = installedGeneration;
|
|
@@ -2787,6 +3236,10 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
2787
3236
|
authFaultRedriveInFlight = true;
|
|
2788
3237
|
void (async () => {
|
|
2789
3238
|
try {
|
|
3239
|
+
if (authReloadPromise !== undefined)
|
|
3240
|
+
await authReloadPromise;
|
|
3241
|
+
if (installedGeneration !== authFaultGeneration || session !== authFaultSession)
|
|
3242
|
+
return;
|
|
2790
3243
|
const anchorEntryId = authFault.anchorEntryId;
|
|
2791
3244
|
if (anchorEntryId !== undefined) {
|
|
2792
3245
|
try {
|
|
@@ -3000,12 +3453,13 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
3000
3453
|
// conversation instead of just the post-yield window; single-cycle sessions and
|
|
3001
3454
|
// the fake-engine fixture (no tree accessors) fall through to plain
|
|
3002
3455
|
// `buildSessionContext().messages` unchanged.
|
|
3003
|
-
export function snapshotMessages(session) {
|
|
3456
|
+
export function snapshotMessages(session, reviewCompanion = false) {
|
|
3004
3457
|
// Cast: the real SDK's `SessionEntry.data` is `unknown` (untyped custom-entry
|
|
3005
3458
|
// payload), narrower than `CycleSessionManagerLike`'s structural read of the
|
|
3006
3459
|
// `crtr-cycle` marker's `{cycle, fromLeaf}` shape — the fields we read off it
|
|
3007
3460
|
// are ones ONLY this module's own `appendCustomEntry` call (above) ever writes.
|
|
3008
|
-
|
|
3461
|
+
const messages = cycleAwareMessages(session.sessionManager);
|
|
3462
|
+
return visibleMessages(messages, { reviewCompanion });
|
|
3009
3463
|
}
|
|
3010
3464
|
// ---------------------------------------------------------------------------
|
|
3011
3465
|
// resolveLaunchModel — the SOLE launch model-selection policy, module-scoped
|
|
@@ -3018,48 +3472,26 @@ export function snapshotMessages(session) {
|
|
|
3018
3472
|
// alone on a failed re-drive attempt).
|
|
3019
3473
|
// ---------------------------------------------------------------------------
|
|
3020
3474
|
async function resolveLaunchModel(services, cfg) {
|
|
3021
|
-
// Route declarations own preference order; the raw registry remains only a
|
|
3022
|
-
// legacy tail for models a user did not declare.
|
|
3023
3475
|
const routeRequest = modelRequestFromConfig(cfg.model, process.env['CRTR_MODEL_INTENT'], cfg.modelExact === true);
|
|
3024
|
-
if (routeRequest
|
|
3025
|
-
const registry = registryOf(services);
|
|
3026
|
-
const routes = expandModelCandidates(routeRequest, process.env['CRTR_NODE_CWD'] ?? cfg.cwd, process.env['CRTR_PROFILE_ID']);
|
|
3027
|
-
const routeKeys = new Set(routes.map((candidate) => `${candidate.providerId}/${candidate.modelId}`));
|
|
3028
|
-
// A concrete request may advance only through its declared same-model
|
|
3029
|
-
// credential-source routes. The raw registry tail is logical-request legacy.
|
|
3030
|
-
const tail = routeRequest.kind === 'logical'
|
|
3031
|
-
? registry.getAll()
|
|
3032
|
-
.filter((model) => !routeKeys.has(`${model.provider}/${model.id}`))
|
|
3033
|
-
.map((model, index) => ({ routeId: `registry:${model.provider}/${model.id}`, credentialSource: 'registry', family: model.provider, strength: 'strong', providerId: model.provider, modelId: model.id, rank: routes.length + index }))
|
|
3034
|
-
: [];
|
|
3035
|
-
const hasManagedAuthenticated = [...routes, ...tail].some((candidate) => {
|
|
3036
|
-
const availability = probeRouteAvailability(candidate, registry);
|
|
3037
|
-
return isManagedProvider(candidate.providerId) && availability.registered && availability.authenticated;
|
|
3038
|
-
});
|
|
3039
|
-
const selection = resolveProviderCandidates([...routes, ...tail].map((candidate) => ({
|
|
3040
|
-
...candidate,
|
|
3041
|
-
...probeRouteAvailability(candidate, registry),
|
|
3042
|
-
automaticFallback: candidate.routeId.startsWith('registry:')
|
|
3043
|
-
? (isManagedProvider(candidate.providerId) || !hasManagedAuthenticated)
|
|
3044
|
-
: true,
|
|
3045
|
-
})), Date.now(), { pinnedToFirst: routeRequest.kind === 'exact' });
|
|
3046
|
-
const selected = selection.candidates[0];
|
|
3047
|
-
if (selected !== undefined) {
|
|
3048
|
-
const model = registry.find(selected.providerId, selected.modelId);
|
|
3049
|
-
if (model !== undefined) {
|
|
3050
|
-
if ((selected.rank ?? 0) > 0)
|
|
3051
|
-
emitEvent({ level: 'warn', event: 'broker.model.launch_fallback', fields: { requested: cfg.model, selected: `${selected.providerId}/${selected.modelId}`, route_id: selected.routeId, credential_source: selected.credentialSource, rank: selected.rank } });
|
|
3052
|
-
return { model, thinkingLevel: selected.thinkingLevel };
|
|
3053
|
-
}
|
|
3054
|
-
}
|
|
3055
|
-
if (cfg.modelExact === true) {
|
|
3056
|
-
const rejection = selection.rejected.map(({ candidate, reason }) => `'${candidate.providerId}/${candidate.modelId}' ${reason}`).join('; ');
|
|
3057
|
-
recordFaultForCurrentNode({ link: 'crtr→pi', op: 'model resolution at launch', kind: 'model-not-found', retry: { disposition: 'fatal' }, message: `exact-consult model '${cfg.model}' is unavailable — refusing to substitute (${rejection})` });
|
|
3058
|
-
throw new Error(`[broker] FATAL: exact-consult model '${cfg.model}' is unavailable`);
|
|
3059
|
-
}
|
|
3060
|
-
emitEvent({ level: 'warn', event: 'broker.model.none_viable', fields: { rejected_candidates: selection.rejected.slice(0, 16).map(({ candidate, reason }) => ({ provider: candidate.providerId, model: candidate.modelId, reason, route_id: candidate.routeId, credential_source: candidate.credentialSource })), rejected_candidates_omitted: Math.max(0, selection.rejected.length - 16) } });
|
|
3476
|
+
if (routeRequest === undefined)
|
|
3061
3477
|
return undefined;
|
|
3478
|
+
const selection = selectInitialModelRoute(routeRequest, registryOf(services), {
|
|
3479
|
+
cwd: process.env['CRTR_NODE_CWD'] ?? cfg.cwd,
|
|
3480
|
+
profileId: process.env['CRTR_PROFILE_ID'],
|
|
3481
|
+
});
|
|
3482
|
+
const selected = selection.selected;
|
|
3483
|
+
if (selected !== undefined) {
|
|
3484
|
+
const candidate = selected.candidate;
|
|
3485
|
+
if ((candidate.rank ?? 0) > 0)
|
|
3486
|
+
emitEvent({ level: 'warn', event: 'broker.model.launch_fallback', fields: { requested: cfg.model, selected: `${candidate.providerId}/${candidate.modelId}`, route_id: candidate.routeId, credential_source: candidate.credentialSource, rank: candidate.rank } });
|
|
3487
|
+
return { model: selected.model, thinkingLevel: candidate.thinkingLevel };
|
|
3488
|
+
}
|
|
3489
|
+
if (cfg.modelExact === true) {
|
|
3490
|
+
const rejection = selection.rejected.map(({ candidate, reason }) => `'${candidate.providerId}/${candidate.modelId}' ${reason}`).join('; ');
|
|
3491
|
+
recordFaultForCurrentNode({ link: 'crtr→pi', op: 'model resolution at launch', kind: 'model-not-found', retry: { disposition: 'fatal' }, message: `exact-consult model '${cfg.model}' is unavailable — refusing to substitute (${rejection})` });
|
|
3492
|
+
throw new Error(`[broker] FATAL: exact-consult model '${cfg.model}' is unavailable`);
|
|
3062
3493
|
}
|
|
3494
|
+
emitEvent({ level: 'warn', event: 'broker.model.none_viable', fields: { rejected_candidates: selection.rejected.slice(0, 16).map(({ candidate, reason }) => ({ provider: candidate.providerId, model: candidate.modelId, reason, route_id: candidate.routeId, credential_source: candidate.credentialSource })), rejected_candidates_omitted: Math.max(0, selection.rejected.length - 16) } });
|
|
3063
3495
|
return undefined;
|
|
3064
3496
|
}
|
|
3065
3497
|
/**
|