@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
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
// spawns pi and never opens the session — it only consumes the relayed
|
|
8
8
|
// `AgentSessionEvent` stream + the `welcome` snapshot over `view.sock`.
|
|
9
9
|
//
|
|
10
|
-
// Ownership boundary: ChatView owns the chat scroll `Container` it is handed
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
10
|
+
// Ownership boundary: ChatView owns the chat scroll `Container` it is handed.
|
|
11
|
+
// Its activity state (working spinner, compaction/retry indicators) is signaled
|
|
12
|
+
// to the viewer-owned fixed slot when one is supplied. It does NOT own the
|
|
13
|
+
// editor, the footer, or the terminal title — those are chrome laid out by the
|
|
14
|
+
// attach command (T7). Events that only affect that chrome
|
|
15
15
|
// (queue_update / session_info_changed / thinking_level_changed) are forwarded
|
|
16
16
|
// to the optional `onFooterEvent` sink instead of rendered here.
|
|
17
17
|
//
|
|
@@ -26,25 +26,29 @@ import { attachMarkdownTheme } from '../config.js';
|
|
|
26
26
|
import { ContextMessageComponent } from './context-message.js';
|
|
27
27
|
import { CRTR_OUTPUT_CUSTOM_TYPE, CrtrOutputMessageComponent, createCrtrBashToolDefinition, } from './crtr-output.js';
|
|
28
28
|
import { CRTR_CYCLE_DIVIDER_CUSTOM_TYPE } from '../../../core/runtime/session-cycles.js';
|
|
29
|
-
import { generatedContextPresentation } from '../../../shared/generated-context.js';
|
|
29
|
+
import { generatedContextExpandedBody, generatedContextPresentation, isRuntimeRestartContinuation, } from '../../../shared/generated-context.js';
|
|
30
|
+
import { assistantVisibleText, isTrueUserMessage, normalizeToolGroupSummary, normalizeToolGroupSummaryRecord, } from '../../../shared/tool-groups.js';
|
|
30
31
|
import { transformMessageDiagrams } from './diagram.js';
|
|
32
|
+
import { transformMessageHtml } from './html-markdown.js';
|
|
31
33
|
import { styleAttachMarkdownHeadings, styleAttachMessageMarkdown, styleAttachSummaryMarkdown, } from './markdown-headings.js';
|
|
32
34
|
import { createCrtrEditToolDefinition } from './edit-diff.js';
|
|
35
|
+
import { GroupActivityRecorder } from './group-activity.js';
|
|
36
|
+
import { GroupRecapComponent } from './group-recap.js';
|
|
33
37
|
import { FoldedToolCallController, createCrtrPlainBashToolDefinition, createCrtrReadToolDefinition, createCrtrWriteToolDefinition, } from './tool-calls.js';
|
|
34
38
|
import { ringCompletionBell } from '../chrome/completion-bell.js';
|
|
35
|
-
import { extractFilePaths } from '
|
|
39
|
+
import { extractFilePaths } from '#core/human/visible-paths';
|
|
36
40
|
import { keepWhenCondensed, liveRegionStart } from './condensed-history.js';
|
|
37
41
|
import { ViewerAssistantMessageComponent } from './assistant-message.js';
|
|
42
|
+
import { CompositeViewportSource, MeasuredContainer, } from './measured-container.js';
|
|
38
43
|
import { readConfig, updateRawConfigAtomically } from '../../../core/config.js';
|
|
39
44
|
import { DEFAULT_CONDENSED_HISTORY, DEFAULT_LIVE_CYCLES } from '../../../types.js';
|
|
45
|
+
import { DEFAULT_WORKING_ACTIVITY } from '../../../shared/working-activity.js';
|
|
40
46
|
/** Theme-derived chrome colors (spinner / dim / error) are supplied by the
|
|
41
47
|
* attach command via `ChatViewOptions.palette` (built from the live theme in
|
|
42
|
-
* config.ts). pi does not re-export the raw `theme` instance, so
|
|
43
|
-
*
|
|
44
|
-
* dim and ANSI red, matching the palette's own fallbacks. The message components
|
|
48
|
+
* config.ts). pi does not re-export the raw `theme` instance, so standalone
|
|
49
|
+
* ChatViews receive the same semantic ANSI fallback palette. The message components
|
|
45
50
|
* use the attach Markdown theme, which preserves the user's pi theme while making
|
|
46
51
|
* fenced code visually distinct. */
|
|
47
|
-
const defaultSpinnerStyle = (s) => `\x1b[2m${s}\x1b[22m`;
|
|
48
52
|
const defaultDimStyle = (s) => `\x1b[2m${s}\x1b[22m`;
|
|
49
53
|
/** How many catch-up messages `applySnapshot` builds between event-loop yields.
|
|
50
54
|
* Keeps large non-streaming snapshots from monopolizing the main thread so
|
|
@@ -85,6 +89,27 @@ function readFoldFinishedTools() {
|
|
|
85
89
|
return false;
|
|
86
90
|
}
|
|
87
91
|
}
|
|
92
|
+
/** Whether this attach should start folded tool groups on their broker-generated
|
|
93
|
+
* summaries. The setting is deliberately attach-local: the broker remains the
|
|
94
|
+
* single summary producer and every viewer independently chooses its display. */
|
|
95
|
+
function readSummarizeToolCalls() {
|
|
96
|
+
try {
|
|
97
|
+
return readConfig('user').summarize_tool_calls;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Whether folded tool recaps list their actual activity or use compact counts.
|
|
104
|
+
* The detailed form is the default; each attach reads its own presentation choice. */
|
|
105
|
+
function readDetailedToolRecaps() {
|
|
106
|
+
try {
|
|
107
|
+
return readConfig('user').detailed_tool_recaps;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
88
113
|
/** Persist the fold view setting to user config. Best-effort: a viewer must never
|
|
89
114
|
* die because the config file is unwritable — the in-viewer toggle still applies. */
|
|
90
115
|
function persistFoldFinishedTools(value) {
|
|
@@ -166,6 +191,8 @@ class FoldedToolSeparator extends Spacer {
|
|
|
166
191
|
}
|
|
167
192
|
}
|
|
168
193
|
class MinimizableToolComponent extends ToolExecutionComponent {
|
|
194
|
+
/** Bound by ChatView at append time; pi's base component does not expose it. */
|
|
195
|
+
activityCallId;
|
|
169
196
|
minimized = false;
|
|
170
197
|
expandedState = false;
|
|
171
198
|
foldController;
|
|
@@ -207,51 +234,35 @@ class MinimizableToolComponent extends ToolExecutionComponent {
|
|
|
207
234
|
}
|
|
208
235
|
}
|
|
209
236
|
const defaultErrorStyle = (s) => `\x1b[31m${s}\x1b[39m`;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
super.clear();
|
|
227
|
-
this.childCache.clear();
|
|
228
|
-
}
|
|
229
|
-
invalidate() {
|
|
230
|
-
super.invalidate();
|
|
231
|
-
this.childCache.clear();
|
|
232
|
-
}
|
|
233
|
-
render(width) {
|
|
234
|
-
const lines = [];
|
|
235
|
-
for (const child of this.children) {
|
|
236
|
-
let cached = this.childCache.get(child);
|
|
237
|
-
if (!cached || cached.width !== width) {
|
|
238
|
-
cached = { width, lines: child.render(width) };
|
|
239
|
-
this.childCache.set(child, cached);
|
|
240
|
-
}
|
|
241
|
-
lines.push(...cached.lines);
|
|
242
|
-
}
|
|
243
|
-
return lines;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
237
|
+
const defaultAttachPalette = {
|
|
238
|
+
accent: (s) => `\x1b[33m${s}\x1b[39m`,
|
|
239
|
+
active: (s) => `\x1b[36m${s}\x1b[39m`,
|
|
240
|
+
info: (s) => `\x1b[34m${s}\x1b[39m`,
|
|
241
|
+
muted: defaultDimStyle,
|
|
242
|
+
faint: defaultDimStyle,
|
|
243
|
+
border: (s) => s,
|
|
244
|
+
surface: (s) => s,
|
|
245
|
+
bold: (s) => `\x1b[1m${s}\x1b[22m`,
|
|
246
|
+
error: defaultErrorStyle,
|
|
247
|
+
warning: (s) => `\x1b[33m${s}\x1b[39m`,
|
|
248
|
+
diffAdded: (s) => `\x1b[32m${s}\x1b[39m`,
|
|
249
|
+
diffRemoved: (s) => `\x1b[91m${s}\x1b[39m`,
|
|
250
|
+
bashMode: (s) => `\x1b[32m${s}\x1b[39m`,
|
|
251
|
+
bashModeAlt: (s) => `\x1b[92m${s}\x1b[39m`,
|
|
252
|
+
};
|
|
246
253
|
export class ChatView {
|
|
247
254
|
tui;
|
|
248
255
|
/** The chat scroll area handed in by the attach command. */
|
|
249
256
|
container;
|
|
250
257
|
/** Settled history is cached independently from the live status area so picker
|
|
251
258
|
* navigation does not re-render a long conversation on every key repeat. */
|
|
252
|
-
historyContainer = new
|
|
253
|
-
/**
|
|
254
|
-
|
|
259
|
+
historyContainer = new MeasuredContainer();
|
|
260
|
+
/** Standalone ChatViews render activity inline; the attach viewer supplies
|
|
261
|
+
* `onActivityChange`, so its transcript excludes this container. */
|
|
262
|
+
statusContainer = new MeasuredContainer();
|
|
263
|
+
/** Optional banner, above history and status in the viewport's flat source. */
|
|
264
|
+
bannerContainer;
|
|
265
|
+
transcript;
|
|
255
266
|
/** Where `append()` puts a component. Normally `historyContainer`; retargeted to
|
|
256
267
|
* a nested container while condensed history is being built, so the same
|
|
257
268
|
* message-rendering path serves both regions. */
|
|
@@ -267,17 +278,37 @@ export class ChatView {
|
|
|
267
278
|
hiddenThinkingLabel;
|
|
268
279
|
/** Ctrl+O: pi's two-state tool-output expansion. */
|
|
269
280
|
toolOutputExpanded;
|
|
270
|
-
/** The view setting (Alt+C →
|
|
281
|
+
/** The view setting (Alt+C → z → f): fold every SETTLED tool call to its single
|
|
271
282
|
* call line. Independent of `toolOutputExpanded` — it survives an expand/collapse
|
|
272
283
|
* and only ever applies to tool calls that are done. */
|
|
273
284
|
foldSettledTools = readFoldFinishedTools();
|
|
285
|
+
/** Summary mode only changes Ctrl+O when both persistent settings are on. */
|
|
286
|
+
summarizeToolCalls = readSummarizeToolCalls();
|
|
287
|
+
detailedToolRecaps = readDetailedToolRecaps();
|
|
288
|
+
showGroupSummaries = this.summarizeToolCalls && this.foldSettledTools;
|
|
289
|
+
/** Groups exist only while building/rendering the full-fidelity live region. */
|
|
290
|
+
groups = [];
|
|
291
|
+
openGroup;
|
|
292
|
+
groupSummaries = new Map();
|
|
293
|
+
streamingWasGroupMember = false;
|
|
274
294
|
onFooterEvent;
|
|
295
|
+
/** Viewer hook that places activity in its fixed slot, when available. */
|
|
296
|
+
onActivityChange;
|
|
275
297
|
/** Theme-derived colorizers for the activity/status area. */
|
|
276
298
|
spinnerStyle;
|
|
277
299
|
dimStyle;
|
|
278
300
|
errorStyle;
|
|
301
|
+
/** Palette shared with recap rows that render outside pi tool callbacks. */
|
|
302
|
+
recapPalette;
|
|
279
303
|
/** Bold accent for the expanded crtr-context label. */
|
|
280
304
|
labelStyle = (s) => `\x1b[1m${s}\x1b[22m`;
|
|
305
|
+
/** Raw broker messages retained beside their viewer components so a local
|
|
306
|
+
* Ctrl+O disclosure toggle can re-project settled Markdown without touching
|
|
307
|
+
* persisted/copyable source. Includes condensed-history components. */
|
|
308
|
+
assistantSources = new Map();
|
|
309
|
+
/** Undefined honors each `<details open>` attribute until Ctrl+O establishes
|
|
310
|
+
* one viewer-wide disclosure state. */
|
|
311
|
+
detailsExpandedOverride;
|
|
281
312
|
/** The assistant message component currently being streamed (between
|
|
282
313
|
* message_start and message_end for an assistant turn). */
|
|
283
314
|
streamingComponent;
|
|
@@ -293,6 +324,10 @@ export class ChatView {
|
|
|
293
324
|
bashComponent;
|
|
294
325
|
/** The single active activity loader (working/compaction/retry), if any. */
|
|
295
326
|
activityLoader;
|
|
327
|
+
/** Broker-selected normal label for this turn; compaction/retry temporarily
|
|
328
|
+
* replace it and restore it when the run remains active. */
|
|
329
|
+
workingActivity = DEFAULT_WORKING_ACTIVITY;
|
|
330
|
+
runActive = false;
|
|
296
331
|
// -- Single-writer snapshot ordering -------------------------------------
|
|
297
332
|
// A non-streaming catch-up yields to the event loop mid-build (so queued stdin
|
|
298
333
|
// drains), which opens a window where later broker frames could otherwise
|
|
@@ -320,6 +355,12 @@ export class ChatView {
|
|
|
320
355
|
transcriptTexts = [];
|
|
321
356
|
/** Existing file paths found anywhere in the rendered transcript, newest first. */
|
|
322
357
|
transcriptFilePaths = [];
|
|
358
|
+
/** Canonical copyable transcript blocks, in their rendered visual order. */
|
|
359
|
+
copyBlockRecords = [];
|
|
360
|
+
copyIndex = new Map();
|
|
361
|
+
/** Registrations made while building condensed history are collapsed onto its
|
|
362
|
+
* outer container once that container becomes a top-level viewport block. */
|
|
363
|
+
condensedCopyBlocks = new WeakMap();
|
|
323
364
|
constructor(tui, container, opts = {}) {
|
|
324
365
|
this.tui = tui;
|
|
325
366
|
this.container = container;
|
|
@@ -332,21 +373,34 @@ export class ChatView {
|
|
|
332
373
|
this.liveCycles = opts.liveCycles ?? readLiveCycles();
|
|
333
374
|
this.condensedHistory = opts.condensedHistory ?? readCondensedHistory();
|
|
334
375
|
this.onFooterEvent = opts.onFooterEvent;
|
|
335
|
-
this.
|
|
336
|
-
this.
|
|
337
|
-
this.
|
|
376
|
+
this.onActivityChange = opts.onActivityChange;
|
|
377
|
+
this.recapPalette = opts.palette ?? defaultAttachPalette;
|
|
378
|
+
this.spinnerStyle = this.recapPalette.active;
|
|
379
|
+
this.dimStyle = this.recapPalette.muted;
|
|
380
|
+
this.errorStyle = this.recapPalette.error;
|
|
338
381
|
// Banner sits above history and OUTSIDE it, so resetChat() (which clears only
|
|
339
382
|
// historyContainer) never wipes it — it persists across every welcome snapshot.
|
|
340
383
|
if (opts.banner && opts.banner.length > 0) {
|
|
341
|
-
const
|
|
342
|
-
|
|
384
|
+
const banner = new MeasuredContainer();
|
|
385
|
+
banner.addChild(new Spacer(1));
|
|
343
386
|
for (const line of opts.banner)
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
this.
|
|
387
|
+
banner.addChild(new Text(line, 1, 0));
|
|
388
|
+
banner.addChild(new Spacer(1));
|
|
389
|
+
this.bannerContainer = banner;
|
|
390
|
+
this.container.addChild(banner);
|
|
347
391
|
}
|
|
348
392
|
this.container.addChild(this.historyContainer);
|
|
349
|
-
|
|
393
|
+
if (!this.onActivityChange)
|
|
394
|
+
this.container.addChild(this.statusContainer);
|
|
395
|
+
this.transcript = new CompositeViewportSource([
|
|
396
|
+
...(this.bannerContainer ? [this.bannerContainer] : []),
|
|
397
|
+
this.historyContainer,
|
|
398
|
+
...(this.onActivityChange ? [] : [this.statusContainer]),
|
|
399
|
+
]);
|
|
400
|
+
}
|
|
401
|
+
/** The transcript as one flat ordered block list for the viewport. */
|
|
402
|
+
transcriptSource() {
|
|
403
|
+
return this.transcript;
|
|
350
404
|
}
|
|
351
405
|
// -------------------------------------------------------------------------
|
|
352
406
|
// Catch-up: render the welcome snapshot, then live events resume.
|
|
@@ -365,6 +419,11 @@ export class ChatView {
|
|
|
365
419
|
this.applyingSnapshot = true;
|
|
366
420
|
this.deferredFrames.length = 0;
|
|
367
421
|
this.resetChat();
|
|
422
|
+
this.workingActivity = typeof snapshot.workingActivity === 'string' && snapshot.workingActivity.trim() !== ''
|
|
423
|
+
? snapshot.workingActivity
|
|
424
|
+
: DEFAULT_WORKING_ACTIVITY;
|
|
425
|
+
this.runActive = snapshot.state.isStreaming;
|
|
426
|
+
this.seedToolGroupSummaries(snapshot.toolGroupSummaries);
|
|
368
427
|
const renderedPendingTools = new Map();
|
|
369
428
|
const messages = snapshot.messages;
|
|
370
429
|
const lastIndex = messages.length - 1;
|
|
@@ -379,7 +438,7 @@ export class ChatView {
|
|
|
379
438
|
if (generation !== this.snapshotGeneration)
|
|
380
439
|
return;
|
|
381
440
|
if (condensed)
|
|
382
|
-
this.
|
|
441
|
+
this.appendCondensedBlock(condensed);
|
|
383
442
|
}
|
|
384
443
|
for (let i = liveStart; i < messages.length; i++) {
|
|
385
444
|
// Yield to the event loop every SNAPSHOT_YIELD_EVERY messages so queued
|
|
@@ -401,7 +460,7 @@ export class ChatView {
|
|
|
401
460
|
if (message.role === 'assistant') {
|
|
402
461
|
// Keep the `/copy` source current from the catch-up transcript too, not
|
|
403
462
|
// only the live stream (you usually attach AFTER the last reply landed).
|
|
404
|
-
const text =
|
|
463
|
+
const text = assistantVisibleText(message);
|
|
405
464
|
if (text.trim()) {
|
|
406
465
|
this.lastAssistantText = text;
|
|
407
466
|
this.pushTranscriptText(text);
|
|
@@ -416,42 +475,65 @@ export class ChatView {
|
|
|
416
475
|
snapshot.state.isStreaming &&
|
|
417
476
|
message.stopReason !== 'aborted' &&
|
|
418
477
|
message.stopReason !== 'error';
|
|
478
|
+
const hasProse = assistantVisibleText(message).trim() !== '';
|
|
479
|
+
const toolCalls = this.assistantToolCalls(message);
|
|
480
|
+
const endedWithFailure = message.stopReason === 'aborted' || message.stopReason === 'error';
|
|
481
|
+
const terminalErrorMessage = message.stopReason === 'aborted'
|
|
482
|
+
? 'Interrupted'
|
|
483
|
+
: (message.errorMessage ?? 'Error');
|
|
484
|
+
// A terminal assistant failure remains visible as its own boundary. Settle
|
|
485
|
+
// any interrupted calls so the completed span before it can still recap.
|
|
486
|
+
if (endedWithFailure) {
|
|
487
|
+
for (const component of renderedPendingTools.values()) {
|
|
488
|
+
const result = { content: [{ type: 'text', text: terminalErrorMessage }], isError: true };
|
|
489
|
+
component.updateResult(result);
|
|
490
|
+
this.registerCopyBlock(component, 'tool', terminalErrorMessage);
|
|
491
|
+
this.settleGroupTool(component, result, true);
|
|
492
|
+
}
|
|
493
|
+
renderedPendingTools.clear();
|
|
494
|
+
}
|
|
495
|
+
if (hasProse || endedWithFailure)
|
|
496
|
+
this.closeOpenGroup();
|
|
419
497
|
let separator;
|
|
420
498
|
if (isStreamingTail) {
|
|
421
499
|
this.streamingComponent = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel);
|
|
422
|
-
this.
|
|
423
|
-
this.streamingComponent
|
|
424
|
-
|
|
500
|
+
this.appendAssistantPart(this.streamingComponent, !hasProse);
|
|
501
|
+
this.registerCopyBlock(this.streamingComponent, 'assistant', '');
|
|
502
|
+
this.streamingWasGroupMember = !hasProse;
|
|
503
|
+
this.updateAssistantComponent(this.streamingComponent, message);
|
|
504
|
+
this.registerCopyBlock(this.streamingComponent, 'assistant', assistantVisibleText(message));
|
|
505
|
+
separator = this.streamingToolSeparator = this.appendAssistantSeparator(!hasProse);
|
|
425
506
|
}
|
|
426
507
|
else {
|
|
427
|
-
separator = this.appendAssistantMessage(message);
|
|
508
|
+
separator = this.appendAssistantMessage(message, !hasProse && !endedWithFailure);
|
|
428
509
|
}
|
|
429
|
-
const toolCalls = this.assistantToolCalls(message);
|
|
430
510
|
if (separator)
|
|
431
511
|
this.setAssistantSeparatorContent(separator, message);
|
|
432
512
|
for (const content of toolCalls) {
|
|
433
513
|
this.pushTranscriptValue(content.arguments);
|
|
434
|
-
const component = this.
|
|
435
|
-
this.append(component);
|
|
514
|
+
const component = this.appendToolComponent(content.name, content.id, content.arguments);
|
|
436
515
|
// (isStreamingTail is never aborted/error, so its tools fall through to
|
|
437
516
|
// renderedPendingTools → pendingTools, where live tool events find them.)
|
|
438
517
|
if (message.stopReason === 'aborted' || message.stopReason === 'error') {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
component.updateResult({ content: [{ type: 'text', text: errorMessage }], isError: true });
|
|
518
|
+
component.updateResult({ content: [{ type: 'text', text: terminalErrorMessage }], isError: true });
|
|
519
|
+
this.registerCopyBlock(component, 'tool', terminalErrorMessage);
|
|
520
|
+
this.settleGroupTool(component, { content: [{ type: 'text', text: terminalErrorMessage }], isError: true }, true);
|
|
443
521
|
}
|
|
444
522
|
else {
|
|
445
523
|
renderedPendingTools.set(content.id, component);
|
|
446
524
|
}
|
|
447
525
|
}
|
|
526
|
+
if (endedWithFailure)
|
|
527
|
+
this.closeOpenGroup();
|
|
448
528
|
}
|
|
449
529
|
else if (message.role === 'toolResult') {
|
|
450
530
|
this.pushTranscriptText(this.userMessageText(message));
|
|
451
531
|
const component = renderedPendingTools.get(message.toolCallId);
|
|
452
532
|
if (component) {
|
|
453
533
|
component.updateResult(message);
|
|
534
|
+
this.registerCopyBlock(component, 'tool', this.toolResultText(message));
|
|
454
535
|
this.historyContainer.markDirty(component);
|
|
536
|
+
this.settleGroupTool(component, message, message.isError === true);
|
|
455
537
|
renderedPendingTools.delete(message.toolCallId);
|
|
456
538
|
}
|
|
457
539
|
}
|
|
@@ -464,13 +546,12 @@ export class ChatView {
|
|
|
464
546
|
}
|
|
465
547
|
// A rebuild constructs fresh components, which are born unfolded; re-apply the
|
|
466
548
|
// setting so a reconnect/welcome does not silently unfold the transcript.
|
|
467
|
-
|
|
468
|
-
this.applyToolDisplay();
|
|
549
|
+
this.applyToolDisplay();
|
|
469
550
|
// If we attached mid-turn, the `agent_start` that spins up the working loader
|
|
470
551
|
// already fired before we connected — restore it from the snapshot's streaming
|
|
471
552
|
// state so an actively-working node shows the spinner right away.
|
|
472
553
|
if (snapshot.state.isStreaming)
|
|
473
|
-
this.setActivity(this.makeLoader(
|
|
554
|
+
this.setActivity(this.makeLoader(this.workingActivity));
|
|
474
555
|
this.tui.requestRender();
|
|
475
556
|
// Build complete and still current (a mismatch would have returned above):
|
|
476
557
|
// open the gate and replay, in wire order, any live frames that arrived
|
|
@@ -492,7 +573,9 @@ export class ChatView {
|
|
|
492
573
|
async buildCondensedBlock(messages, generation) {
|
|
493
574
|
const condensed = new Container();
|
|
494
575
|
const previousTarget = this.appendTarget;
|
|
576
|
+
const copyBlocks = [];
|
|
495
577
|
this.appendTarget = condensed;
|
|
578
|
+
this.condensedCopyBlocks.set(condensed, copyBlocks);
|
|
496
579
|
try {
|
|
497
580
|
let built = 0;
|
|
498
581
|
for (const message of messages) {
|
|
@@ -513,8 +596,9 @@ export class ChatView {
|
|
|
513
596
|
}
|
|
514
597
|
finally {
|
|
515
598
|
this.appendTarget = previousTarget;
|
|
599
|
+
this.condensedCopyBlocks.delete(condensed);
|
|
516
600
|
}
|
|
517
|
-
return condensed.children.length > 0 ? condensed : undefined;
|
|
601
|
+
return condensed.children.length > 0 ? { component: condensed, copyBlocks } : undefined;
|
|
518
602
|
}
|
|
519
603
|
/** Defer a live broker frame when a snapshot build is in flight. Returns true
|
|
520
604
|
* (and enqueues the replay closure) while building so the caller returns
|
|
@@ -533,14 +617,26 @@ export class ChatView {
|
|
|
533
617
|
for (const replay of frames)
|
|
534
618
|
replay();
|
|
535
619
|
}
|
|
620
|
+
/** Accept the broker's per-turn random choice before its following agent_start. */
|
|
621
|
+
setWorkingActivity(activity) {
|
|
622
|
+
if (this.deferIfBuilding(() => this.setWorkingActivity(activity)))
|
|
623
|
+
return;
|
|
624
|
+
this.workingActivity = activity.trim() === '' ? DEFAULT_WORKING_ACTIVITY : activity;
|
|
625
|
+
}
|
|
536
626
|
/** Clear live turn state after the broker reports completion or the viewer observes dormancy. */
|
|
537
627
|
settleRun() {
|
|
628
|
+
this.runActive = false;
|
|
629
|
+
for (const component of this.pendingTools.values())
|
|
630
|
+
this.settleGroupTool(component);
|
|
538
631
|
if (this.streamingComponent) {
|
|
539
632
|
this.historyContainer.removeChild(this.streamingComponent);
|
|
633
|
+
this.unregisterCopyBlock(this.streamingComponent);
|
|
634
|
+
this.assistantSources.delete(this.streamingComponent);
|
|
540
635
|
this.streamingComponent = undefined;
|
|
541
636
|
}
|
|
542
637
|
this.streamingToolSeparator = undefined;
|
|
543
638
|
this.pendingTools.clear();
|
|
639
|
+
this.applyToolDisplay();
|
|
544
640
|
this.setActivity(undefined);
|
|
545
641
|
this.settleBashComponent();
|
|
546
642
|
}
|
|
@@ -555,36 +651,54 @@ export class ChatView {
|
|
|
555
651
|
// Pure client-side render state — no broker round-trip — ported from pi's
|
|
556
652
|
// `setToolsExpanded` / `toggleThinkingBlockVisibility`.
|
|
557
653
|
// -------------------------------------------------------------------------
|
|
558
|
-
/** Ctrl+O
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
* they are unaffected by design. */
|
|
654
|
+
/** Ctrl+O prefers summaries when the user enabled both folding and summary
|
|
655
|
+
* generation. The same disclosure state drives assistant-authored `<details>`
|
|
656
|
+
* sections, so the transcript has one reveal-more action rather than two. */
|
|
562
657
|
toggleToolsExpanded() {
|
|
563
|
-
|
|
658
|
+
let result;
|
|
659
|
+
if (this.summarizeToolCalls && this.foldSettledTools) {
|
|
660
|
+
this.showGroupSummaries = !this.showGroupSummaries;
|
|
661
|
+
this.toolOutputExpanded = !this.showGroupSummaries;
|
|
662
|
+
result = { kind: 'summaries', shown: this.showGroupSummaries };
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
this.toolOutputExpanded = !this.toolOutputExpanded;
|
|
666
|
+
result = { kind: 'output', expanded: this.toolOutputExpanded };
|
|
667
|
+
}
|
|
668
|
+
this.detailsExpandedOverride = this.toolOutputExpanded;
|
|
669
|
+
this.rerenderAssistantMessages();
|
|
564
670
|
this.applyToolDisplay();
|
|
565
|
-
return
|
|
671
|
+
return result;
|
|
566
672
|
}
|
|
567
|
-
/** The `fold finished tool calls` view setting (`/fold-tools`, Alt+C →
|
|
673
|
+
/** The `fold finished tool calls` view setting (`/fold-tools`, Alt+C → z → f).
|
|
568
674
|
* Returns the new state so the caller can surface a notice. */
|
|
569
675
|
toggleFoldSettledTools() {
|
|
570
676
|
this.foldSettledTools = !this.foldSettledTools;
|
|
571
677
|
persistFoldFinishedTools(this.foldSettledTools);
|
|
678
|
+
// Turning folding back on restores the configured default (summaries),
|
|
679
|
+
// rather than preserving a stale Ctrl+O choice from an unfolded view.
|
|
680
|
+
if (this.summarizeToolCalls && this.foldSettledTools)
|
|
681
|
+
this.showGroupSummaries = true;
|
|
572
682
|
this.applyToolDisplay();
|
|
573
683
|
return this.foldSettledTools;
|
|
574
684
|
}
|
|
575
685
|
/** Push both display settings onto every rendered component. A tool call still in
|
|
576
|
-
*
|
|
577
|
-
*
|
|
686
|
+
* flight is never hidden or minimized. Only a settled group with a broker-known
|
|
687
|
+
* summary collapses; incomplete/failed generation leaves its folded call lines. */
|
|
578
688
|
applyToolDisplay() {
|
|
579
689
|
const inFlight = new Set(this.pendingTools.values());
|
|
580
|
-
this.
|
|
690
|
+
for (const group of this.groups)
|
|
691
|
+
for (const tool of group.inFlight)
|
|
692
|
+
inFlight.add(tool);
|
|
581
693
|
for (const child of this.historyContainer.children) {
|
|
582
|
-
if (child instanceof MinimizableToolComponent)
|
|
694
|
+
if (child instanceof MinimizableToolComponent)
|
|
583
695
|
child.setMinimized(this.foldSettledTools && !inFlight.has(child));
|
|
584
|
-
}
|
|
585
696
|
if (isExpandable(child))
|
|
586
697
|
child.setExpanded(this.toolOutputExpanded);
|
|
587
698
|
}
|
|
699
|
+
for (const group of this.groups)
|
|
700
|
+
this.applyGroupDisplay(group);
|
|
701
|
+
this.updateFoldedToolSeparators(inFlight);
|
|
588
702
|
this.historyContainer.markDirty();
|
|
589
703
|
this.tui.requestRender();
|
|
590
704
|
}
|
|
@@ -615,8 +729,11 @@ export class ChatView {
|
|
|
615
729
|
return;
|
|
616
730
|
switch (event.type) {
|
|
617
731
|
case 'agent_start': {
|
|
732
|
+
this.runActive = true;
|
|
733
|
+
for (const component of this.pendingTools.values())
|
|
734
|
+
this.settleGroupTool(component);
|
|
618
735
|
this.pendingTools.clear();
|
|
619
|
-
this.setActivity(this.makeLoader(
|
|
736
|
+
this.setActivity(this.makeLoader(this.workingActivity));
|
|
620
737
|
break;
|
|
621
738
|
}
|
|
622
739
|
case 'agent_end': {
|
|
@@ -644,23 +761,28 @@ export class ChatView {
|
|
|
644
761
|
this.addMessageToChat(event.message);
|
|
645
762
|
}
|
|
646
763
|
else if (event.message.role === 'assistant') {
|
|
764
|
+
const hasProse = assistantVisibleText(event.message).trim() !== '';
|
|
765
|
+
if (hasProse)
|
|
766
|
+
this.closeOpenGroup();
|
|
647
767
|
this.streamingComponent = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel);
|
|
648
|
-
this.
|
|
649
|
-
this.
|
|
650
|
-
this.
|
|
768
|
+
this.streamingWasGroupMember = !hasProse;
|
|
769
|
+
this.appendAssistantPart(this.streamingComponent, !hasProse);
|
|
770
|
+
this.registerCopyBlock(this.streamingComponent, 'assistant', '');
|
|
771
|
+
this.updateAssistantComponent(this.streamingComponent, event.message);
|
|
772
|
+
this.registerCopyBlock(this.streamingComponent, 'assistant', assistantVisibleText(event.message));
|
|
773
|
+
this.streamingToolSeparator = this.appendAssistantSeparator(!hasProse);
|
|
651
774
|
this.setAssistantSeparatorContent(this.streamingToolSeparator, event.message);
|
|
652
775
|
}
|
|
653
776
|
break;
|
|
654
777
|
}
|
|
655
778
|
case 'message_update': {
|
|
656
779
|
if (this.streamingComponent && event.message.role === 'assistant') {
|
|
657
|
-
|
|
658
|
-
//
|
|
659
|
-
//
|
|
660
|
-
//
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
this.streamingComponent.updateContent(updated);
|
|
780
|
+
this.promoteStreamingAssistantToBoundary(event.message);
|
|
781
|
+
// Upgrade complete rich-Markdown constructs mid-stream. Incomplete
|
|
782
|
+
// Mermaid fences and HTML pairs remain literal until their closing
|
|
783
|
+
// marker arrives, then this same settled-message pipeline projects them.
|
|
784
|
+
this.updateAssistantComponent(this.streamingComponent, event.message);
|
|
785
|
+
this.registerCopyBlock(this.streamingComponent, 'assistant', assistantVisibleText(event.message));
|
|
664
786
|
this.historyContainer.markDirty(this.streamingComponent);
|
|
665
787
|
const toolCalls = this.assistantToolCalls(event.message);
|
|
666
788
|
if (this.streamingToolSeparator)
|
|
@@ -670,11 +792,11 @@ export class ChatView {
|
|
|
670
792
|
const existing = this.pendingTools.get(content.id);
|
|
671
793
|
if (existing) {
|
|
672
794
|
existing.updateArgs(content.arguments);
|
|
795
|
+
this.noteGroupTool(existing, content.name, content.arguments);
|
|
673
796
|
this.historyContainer.markDirty(existing);
|
|
674
797
|
}
|
|
675
798
|
else {
|
|
676
|
-
const component = this.
|
|
677
|
-
this.append(component);
|
|
799
|
+
const component = this.appendToolComponent(content.name, content.id, content.arguments);
|
|
678
800
|
this.pendingTools.set(content.id, component);
|
|
679
801
|
}
|
|
680
802
|
}
|
|
@@ -684,10 +806,16 @@ export class ChatView {
|
|
|
684
806
|
case 'message_end': {
|
|
685
807
|
if (event.message.role !== 'assistant')
|
|
686
808
|
break;
|
|
809
|
+
const endedWithFailure = event.message.stopReason === 'aborted' || event.message.stopReason === 'error';
|
|
810
|
+
// A terminal failure is always its own visible boundary, including an
|
|
811
|
+
// empty abort created by yield or a daemon restart.
|
|
812
|
+
this.promoteStreamingAssistantToBoundary(event.message, endedWithFailure);
|
|
687
813
|
// Stash the completed reply's text for `/copy` (skip aborted/error turns,
|
|
688
814
|
// whose text is partial or absent).
|
|
815
|
+
if (this.streamingComponent)
|
|
816
|
+
this.registerCopyBlock(this.streamingComponent, 'assistant', assistantVisibleText(event.message));
|
|
689
817
|
if (event.message.stopReason !== 'aborted' && event.message.stopReason !== 'error') {
|
|
690
|
-
const text =
|
|
818
|
+
const text = assistantVisibleText(event.message);
|
|
691
819
|
if (text.trim()) {
|
|
692
820
|
this.lastAssistantText = text;
|
|
693
821
|
this.pushTranscriptText(text);
|
|
@@ -696,10 +824,9 @@ export class ChatView {
|
|
|
696
824
|
if (this.streamingComponent) {
|
|
697
825
|
const stopReason = event.message.stopReason;
|
|
698
826
|
let errorMessage = event.message.errorMessage;
|
|
699
|
-
// An aborted turn
|
|
700
|
-
// attach its own generic text ("Request was
|
|
701
|
-
//
|
|
702
|
-
// plain, human label.
|
|
827
|
+
// An aborted turn may be a user interrupt or a lifecycle interruption.
|
|
828
|
+
// The provider tends to attach its own generic text ("Request was
|
|
829
|
+
// aborted."), so override it outright with one plain, human label.
|
|
703
830
|
if (stopReason === 'aborted') {
|
|
704
831
|
errorMessage = 'Interrupted';
|
|
705
832
|
// Surface the abort on the assistant bubble itself, mirroring pi
|
|
@@ -707,14 +834,14 @@ export class ChatView {
|
|
|
707
834
|
// updateContent) so the rendered message shows the annotation.
|
|
708
835
|
event.message.errorMessage = errorMessage;
|
|
709
836
|
}
|
|
710
|
-
//
|
|
711
|
-
|
|
712
|
-
const finalMessage = styleAttachMessageMarkdown(transformMessageDiagrams(event.message, this.renderWidth()));
|
|
713
|
-
this.streamingComponent.updateContent(finalMessage);
|
|
837
|
+
// Project every supported rich-Markdown construct in the settled reply.
|
|
838
|
+
this.updateAssistantComponent(this.streamingComponent, event.message);
|
|
714
839
|
if (stopReason === 'aborted' || stopReason === 'error') {
|
|
715
840
|
const text = errorMessage ?? 'Error';
|
|
716
841
|
for (const component of this.pendingTools.values()) {
|
|
717
842
|
component.updateResult({ content: [{ type: 'text', text }], isError: true });
|
|
843
|
+
this.registerCopyBlock(component, 'tool', text);
|
|
844
|
+
this.settleGroupTool(component, { content: [{ type: 'text', text }], isError: true }, true);
|
|
718
845
|
this.historyContainer.markDirty(component);
|
|
719
846
|
}
|
|
720
847
|
this.pendingTools.clear();
|
|
@@ -730,16 +857,24 @@ export class ChatView {
|
|
|
730
857
|
this.streamingComponent = undefined;
|
|
731
858
|
this.streamingToolSeparator = undefined;
|
|
732
859
|
}
|
|
860
|
+
if (endedWithFailure) {
|
|
861
|
+
this.closeOpenGroup();
|
|
862
|
+
// A summary normally follows this terminal frame, but re-evaluate now
|
|
863
|
+
// too so an early recap immediately folds the tools before this seam.
|
|
864
|
+
this.applyToolDisplay();
|
|
865
|
+
}
|
|
733
866
|
break;
|
|
734
867
|
}
|
|
735
868
|
case 'tool_execution_start': {
|
|
736
869
|
this.pushTranscriptValue(event.args);
|
|
737
870
|
let component = this.pendingTools.get(event.toolCallId);
|
|
738
871
|
if (!component) {
|
|
739
|
-
component = this.
|
|
740
|
-
this.append(component);
|
|
872
|
+
component = this.appendToolComponent(event.toolName, event.toolCallId, event.args);
|
|
741
873
|
this.pendingTools.set(event.toolCallId, component);
|
|
742
874
|
}
|
|
875
|
+
else {
|
|
876
|
+
this.noteGroupTool(component, event.toolName, event.args);
|
|
877
|
+
}
|
|
743
878
|
component.markExecutionStarted();
|
|
744
879
|
this.historyContainer.markDirty(component);
|
|
745
880
|
break;
|
|
@@ -749,6 +884,7 @@ export class ChatView {
|
|
|
749
884
|
const component = this.pendingTools.get(event.toolCallId);
|
|
750
885
|
if (component) {
|
|
751
886
|
component.updateResult({ ...event.partialResult, isError: false }, true);
|
|
887
|
+
this.registerCopyBlock(component, 'tool', this.toolResultText(event.partialResult));
|
|
752
888
|
this.historyContainer.markDirty(component);
|
|
753
889
|
}
|
|
754
890
|
break;
|
|
@@ -758,11 +894,10 @@ export class ChatView {
|
|
|
758
894
|
const component = this.pendingTools.get(event.toolCallId);
|
|
759
895
|
if (component) {
|
|
760
896
|
component.updateResult({ ...event.result, isError: event.isError });
|
|
897
|
+
this.registerCopyBlock(component, 'tool', this.toolResultText(event.result));
|
|
761
898
|
this.pendingTools.delete(event.toolCallId);
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
component.setMinimized(true);
|
|
765
|
-
this.updateFoldedToolSeparators();
|
|
899
|
+
this.settleGroupTool(component, event.result, event.isError);
|
|
900
|
+
this.applyToolDisplay();
|
|
766
901
|
this.historyContainer.markDirty(component);
|
|
767
902
|
}
|
|
768
903
|
break;
|
|
@@ -773,7 +908,7 @@ export class ChatView {
|
|
|
773
908
|
break;
|
|
774
909
|
}
|
|
775
910
|
case 'compaction_end': {
|
|
776
|
-
this.setActivity(undefined);
|
|
911
|
+
this.setActivity(this.runActive ? this.makeLoader(this.workingActivity) : undefined);
|
|
777
912
|
if (event.aborted) {
|
|
778
913
|
this.showStatus(event.reason === 'manual' ? 'Compaction cancelled' : 'Auto-compaction cancelled');
|
|
779
914
|
}
|
|
@@ -804,7 +939,7 @@ export class ChatView {
|
|
|
804
939
|
break;
|
|
805
940
|
}
|
|
806
941
|
case 'auto_retry_end': {
|
|
807
|
-
this.setActivity(undefined);
|
|
942
|
+
this.setActivity(this.runActive ? this.makeLoader(this.workingActivity) : undefined);
|
|
808
943
|
if (!event.success) {
|
|
809
944
|
this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError ?? 'Unknown error'}`);
|
|
810
945
|
}
|
|
@@ -822,7 +957,7 @@ export class ChatView {
|
|
|
822
957
|
break;
|
|
823
958
|
}
|
|
824
959
|
// No unconditional markDirty here: appends/removes invalidate through
|
|
825
|
-
//
|
|
960
|
+
// MeasuredContainer, and branches that mutate a child in place explicitly
|
|
826
961
|
// mark it dirty. Activity-only events leave settled history cached while the
|
|
827
962
|
// independent status loader animates.
|
|
828
963
|
this.tui.requestRender();
|
|
@@ -836,6 +971,7 @@ export class ChatView {
|
|
|
836
971
|
bashStart(command, excludeFromContext) {
|
|
837
972
|
if (this.deferIfBuilding(() => this.bashStart(command, excludeFromContext)))
|
|
838
973
|
return;
|
|
974
|
+
this.closeOpenGroup();
|
|
839
975
|
this.settleBashComponent();
|
|
840
976
|
let component;
|
|
841
977
|
component = new BashExecutionComponent(command, this.componentTui(() => component), excludeFromContext);
|
|
@@ -872,11 +1008,27 @@ export class ChatView {
|
|
|
872
1008
|
if (generated !== null &&
|
|
873
1009
|
(message.role !== 'custom' || message.display === true)) {
|
|
874
1010
|
this.pushTranscriptText(generated.body);
|
|
875
|
-
|
|
1011
|
+
const component = new ContextMessageComponent(generatedContextExpandedBody(generated), this.toolOutputExpanded, this.dimStyle, (s) => this.spinnerStyle(this.labelStyle(s)), generated.label, ` ${generated.summary} — ctrl+o to expand`, generated.label === 'crtr context' || generated.kind === 'revive' || generated.kind === 'inbox');
|
|
1012
|
+
// A successor broker's exact restart continuation is a visible seam, not
|
|
1013
|
+
// a group member: the preceding span keeps its own recap and the next tool
|
|
1014
|
+
// call begins a broker-aligned fresh group.
|
|
1015
|
+
if (isRuntimeRestartContinuation(message)) {
|
|
1016
|
+
this.closeOpenGroup();
|
|
1017
|
+
this.append(component);
|
|
1018
|
+
// Bearings and revive kickoffs only belong to an already-open run; they
|
|
1019
|
+
// never create a summary block by themselves. Other injected context may.
|
|
1020
|
+
}
|
|
1021
|
+
else if (generated.kind === 'bearings' || generated.kind === 'revive') {
|
|
1022
|
+
this.appendExistingGroupMember(component);
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
this.appendGroupMember(component);
|
|
1026
|
+
}
|
|
876
1027
|
return;
|
|
877
1028
|
}
|
|
878
1029
|
switch (message.role) {
|
|
879
1030
|
case 'bashExecution': {
|
|
1031
|
+
this.closeOpenGroup();
|
|
880
1032
|
this.pushTranscriptText(message.output ?? '');
|
|
881
1033
|
const component = new BashExecutionComponent(message.command, this.tui, message.excludeFromContext);
|
|
882
1034
|
if (message.output)
|
|
@@ -894,7 +1046,7 @@ export class ChatView {
|
|
|
894
1046
|
if (message.display) {
|
|
895
1047
|
this.pushTranscriptText(this.userMessageText(message));
|
|
896
1048
|
if (message.customType === CRTR_OUTPUT_CUSTOM_TYPE) {
|
|
897
|
-
this.
|
|
1049
|
+
this.appendGroupMember(new CrtrOutputMessageComponent(this.userMessageText(message), this.toolOutputExpanded, this.crtrOutputTheme()));
|
|
898
1050
|
break;
|
|
899
1051
|
}
|
|
900
1052
|
// A refresh-yield cycle boundary (session-cycles.ts, synthetic — never
|
|
@@ -902,6 +1054,7 @@ export class ChatView {
|
|
|
902
1054
|
// message card, so scrolling through a multi-cycle node's full history
|
|
903
1055
|
// reads as one conversation with clear seams between cycles.
|
|
904
1056
|
if (message.customType === CRTR_CYCLE_DIVIDER_CUSTOM_TYPE) {
|
|
1057
|
+
this.closeOpenGroup();
|
|
905
1058
|
this.append(new Spacer(1));
|
|
906
1059
|
this.append(new Text(this.dimStyle(`── ${this.userMessageText(message)} ──`), 1, 0));
|
|
907
1060
|
this.append(new Spacer(1));
|
|
@@ -909,11 +1062,12 @@ export class ChatView {
|
|
|
909
1062
|
}
|
|
910
1063
|
const component = new CustomMessageComponent(styleAttachMessageMarkdown(message), undefined, attachMarkdownTheme());
|
|
911
1064
|
component.setExpanded(this.toolOutputExpanded);
|
|
912
|
-
this.
|
|
1065
|
+
this.appendGroupMember(component);
|
|
913
1066
|
}
|
|
914
1067
|
break;
|
|
915
1068
|
}
|
|
916
1069
|
case 'compactionSummary': {
|
|
1070
|
+
this.closeOpenGroup();
|
|
917
1071
|
this.append(new Spacer(1));
|
|
918
1072
|
const component = new CompactionSummaryMessageComponent(styleAttachSummaryMarkdown(message), attachMarkdownTheme());
|
|
919
1073
|
component.setExpanded(this.toolOutputExpanded);
|
|
@@ -921,35 +1075,47 @@ export class ChatView {
|
|
|
921
1075
|
break;
|
|
922
1076
|
}
|
|
923
1077
|
case 'branchSummary': {
|
|
1078
|
+
this.closeOpenGroup();
|
|
924
1079
|
this.append(new Spacer(1));
|
|
925
1080
|
const component = new BranchSummaryMessageComponent(styleAttachSummaryMarkdown(message), attachMarkdownTheme());
|
|
926
1081
|
component.setExpanded(this.toolOutputExpanded);
|
|
927
1082
|
this.append(component);
|
|
1083
|
+
this.registerCopyBlock(component, 'assistant', message.summary);
|
|
928
1084
|
break;
|
|
929
1085
|
}
|
|
930
1086
|
case 'user': {
|
|
1087
|
+
if (isTrueUserMessage(message))
|
|
1088
|
+
this.closeOpenGroup();
|
|
931
1089
|
const textContent = this.userMessageText(message);
|
|
932
1090
|
if (!textContent)
|
|
933
1091
|
break;
|
|
934
1092
|
this.pushTranscriptText(textContent);
|
|
935
1093
|
if (this.chatChildCount() > 0)
|
|
936
|
-
this.
|
|
1094
|
+
this.appendUserPart(new Spacer(1), !isTrueUserMessage(message));
|
|
937
1095
|
const skillBlock = parseSkillBlock(textContent);
|
|
938
1096
|
if (skillBlock) {
|
|
939
1097
|
const component = new SkillInvocationMessageComponent({ ...skillBlock, content: styleAttachMarkdownHeadings(skillBlock.content) }, attachMarkdownTheme());
|
|
940
1098
|
component.setExpanded(this.toolOutputExpanded);
|
|
941
|
-
this.
|
|
1099
|
+
this.appendUserPart(component, !isTrueUserMessage(message));
|
|
1100
|
+
this.registerCopyBlock(component, 'user', textContent);
|
|
942
1101
|
if (skillBlock.userMessage) {
|
|
943
|
-
|
|
1102
|
+
const userMessage = new UserMessageComponent(styleAttachMarkdownHeadings(skillBlock.userMessage), attachMarkdownTheme());
|
|
1103
|
+
this.appendUserPart(userMessage, !isTrueUserMessage(message));
|
|
1104
|
+
this.registerCopyBlock(userMessage, 'user', skillBlock.userMessage);
|
|
944
1105
|
}
|
|
945
1106
|
}
|
|
946
1107
|
else {
|
|
947
|
-
|
|
1108
|
+
const component = new UserMessageComponent(styleAttachMarkdownHeadings(textContent), attachMarkdownTheme());
|
|
1109
|
+
this.appendUserPart(component, !isTrueUserMessage(message));
|
|
1110
|
+
this.registerCopyBlock(component, 'user', textContent);
|
|
948
1111
|
}
|
|
949
1112
|
break;
|
|
950
1113
|
}
|
|
951
1114
|
case 'assistant': {
|
|
952
|
-
|
|
1115
|
+
const hasProse = assistantVisibleText(message).trim() !== '';
|
|
1116
|
+
if (hasProse)
|
|
1117
|
+
this.closeOpenGroup();
|
|
1118
|
+
this.appendAssistantMessage(message, !hasProse);
|
|
953
1119
|
break;
|
|
954
1120
|
}
|
|
955
1121
|
// toolResult is rendered inline with its tool call (handled in applySnapshot).
|
|
@@ -961,19 +1127,176 @@ export class ChatView {
|
|
|
961
1127
|
// Helpers
|
|
962
1128
|
// -------------------------------------------------------------------------
|
|
963
1129
|
/** Render an assistant reply plus its initially-hidden boundary separator. */
|
|
964
|
-
appendAssistantMessage(message) {
|
|
965
|
-
|
|
966
|
-
|
|
1130
|
+
appendAssistantMessage(message, groupMember = false) {
|
|
1131
|
+
const component = new ViewerAssistantMessageComponent(undefined, this.hideThinking, attachMarkdownTheme(), this.hiddenThinkingLabel);
|
|
1132
|
+
this.updateAssistantComponent(component, message);
|
|
1133
|
+
this.appendAssistantPart(component, groupMember);
|
|
1134
|
+
this.registerCopyBlock(component, 'assistant', assistantVisibleText(message));
|
|
1135
|
+
const separator = this.appendAssistantSeparator(groupMember);
|
|
967
1136
|
this.setAssistantSeparatorContent(separator, message);
|
|
968
1137
|
return separator;
|
|
969
1138
|
}
|
|
970
1139
|
/** Add a separator in transcript order now; it renders only after a following
|
|
971
1140
|
* tool settles in the folded view. */
|
|
972
|
-
|
|
1141
|
+
appendAssistantSeparator(groupMember = false) {
|
|
973
1142
|
const separator = new FoldedToolSeparator();
|
|
974
|
-
|
|
1143
|
+
if (groupMember)
|
|
1144
|
+
this.appendGroupMember(separator);
|
|
1145
|
+
else
|
|
1146
|
+
this.append(separator);
|
|
975
1147
|
return separator;
|
|
976
1148
|
}
|
|
1149
|
+
appendAssistantPart(component, groupMember) {
|
|
1150
|
+
if (groupMember)
|
|
1151
|
+
this.appendGroupMember(component);
|
|
1152
|
+
else
|
|
1153
|
+
this.append(component);
|
|
1154
|
+
}
|
|
1155
|
+
appendUserPart(component, groupMember) {
|
|
1156
|
+
if (groupMember)
|
|
1157
|
+
this.appendGroupMember(component);
|
|
1158
|
+
else
|
|
1159
|
+
this.append(component);
|
|
1160
|
+
}
|
|
1161
|
+
/** Only the full-fidelity trailing region gets summary groups. Condensed
|
|
1162
|
+
* history intentionally drops tool traffic, so grouping there would create
|
|
1163
|
+
* phantom summary rows and cross the live/condensed boundary. */
|
|
1164
|
+
isLiveGroupRegion() {
|
|
1165
|
+
return this.appendTarget === this.historyContainer;
|
|
1166
|
+
}
|
|
1167
|
+
ensureGroup() {
|
|
1168
|
+
if (!this.isLiveGroupRegion())
|
|
1169
|
+
return undefined;
|
|
1170
|
+
if (this.openGroup)
|
|
1171
|
+
return this.openGroup;
|
|
1172
|
+
const recap = new GroupRecapComponent();
|
|
1173
|
+
const group = {
|
|
1174
|
+
members: [],
|
|
1175
|
+
recap,
|
|
1176
|
+
activity: new GroupActivityRecorder(this.cwd),
|
|
1177
|
+
toolCount: 0,
|
|
1178
|
+
inFlight: new Set(),
|
|
1179
|
+
};
|
|
1180
|
+
this.append(recap);
|
|
1181
|
+
// The recap is a direct history child for cache identity, but zero-row and
|
|
1182
|
+
// hidden until a settled broker summary makes this group collapsible.
|
|
1183
|
+
this.historyContainer.setHidden(recap, true);
|
|
1184
|
+
this.groups.push(group);
|
|
1185
|
+
this.openGroup = group;
|
|
1186
|
+
return group;
|
|
1187
|
+
}
|
|
1188
|
+
closeOpenGroup() {
|
|
1189
|
+
this.openGroup = undefined;
|
|
1190
|
+
}
|
|
1191
|
+
appendGroupMember(component) {
|
|
1192
|
+
const group = this.ensureGroup();
|
|
1193
|
+
this.append(component);
|
|
1194
|
+
if (group)
|
|
1195
|
+
group.members.push(component);
|
|
1196
|
+
}
|
|
1197
|
+
/** Used for context that may join an existing run but must not create one. */
|
|
1198
|
+
appendExistingGroupMember(component) {
|
|
1199
|
+
this.append(component);
|
|
1200
|
+
if (this.isLiveGroupRegion() && this.openGroup)
|
|
1201
|
+
this.openGroup.members.push(component);
|
|
1202
|
+
}
|
|
1203
|
+
removeGroupMember(component) {
|
|
1204
|
+
for (const group of this.groups) {
|
|
1205
|
+
const index = group.members.indexOf(component);
|
|
1206
|
+
if (index !== -1)
|
|
1207
|
+
group.members.splice(index, 1);
|
|
1208
|
+
}
|
|
1209
|
+
this.historyContainer.setHidden(component, false);
|
|
1210
|
+
}
|
|
1211
|
+
/** Add one tool as a group member, moving the assistant's boundary separator
|
|
1212
|
+
* behind the new summary row without changing the separator's identity. */
|
|
1213
|
+
appendToolComponent(name, id, args) {
|
|
1214
|
+
const trailingSeparator = this.isLiveGroupRegion() &&
|
|
1215
|
+
!this.openGroup &&
|
|
1216
|
+
this.historyContainer.children.at(-1) instanceof FoldedToolSeparator
|
|
1217
|
+
? this.historyContainer.children.at(-1)
|
|
1218
|
+
: undefined;
|
|
1219
|
+
if (trailingSeparator)
|
|
1220
|
+
this.historyContainer.removeChild(trailingSeparator);
|
|
1221
|
+
const group = this.ensureGroup();
|
|
1222
|
+
if (trailingSeparator) {
|
|
1223
|
+
this.append(trailingSeparator);
|
|
1224
|
+
group?.members.push(trailingSeparator);
|
|
1225
|
+
}
|
|
1226
|
+
const component = this.makeToolComponent(name, id, args);
|
|
1227
|
+
component.activityCallId = id;
|
|
1228
|
+
this.appendGroupMember(component);
|
|
1229
|
+
this.registerCopyBlock(component, 'tool', '');
|
|
1230
|
+
if (group) {
|
|
1231
|
+
group.key ??= id;
|
|
1232
|
+
group.toolCount++;
|
|
1233
|
+
group.inFlight.add(component);
|
|
1234
|
+
group.activity.noteCall(id, name, args);
|
|
1235
|
+
}
|
|
1236
|
+
return component;
|
|
1237
|
+
}
|
|
1238
|
+
noteGroupTool(component, name, args) {
|
|
1239
|
+
if (!component.activityCallId)
|
|
1240
|
+
return;
|
|
1241
|
+
for (const group of this.groups) {
|
|
1242
|
+
if (group.members.includes(component))
|
|
1243
|
+
group.activity.noteCall(component.activityCallId, name, args);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
settleGroupTool(component, result, isError = false) {
|
|
1247
|
+
for (const group of this.groups) {
|
|
1248
|
+
group.inFlight.delete(component);
|
|
1249
|
+
if (!component.activityCallId || !group.members.includes(component))
|
|
1250
|
+
continue;
|
|
1251
|
+
if (result === undefined)
|
|
1252
|
+
group.activity.resolveWithoutResult(component.activityCallId);
|
|
1253
|
+
else
|
|
1254
|
+
group.activity.settle(component.activityCallId, result, isError);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
/** An assistant starts as a non-boundary while its text is empty. Visible prose
|
|
1258
|
+
* or a terminal failure turns it into a boundary without recreating either
|
|
1259
|
+
* pi-owned component (their direct-child cache identities remain intact). */
|
|
1260
|
+
promoteStreamingAssistantToBoundary(message, force = false) {
|
|
1261
|
+
if (!this.streamingWasGroupMember || (!force && assistantVisibleText(message).trim() === ''))
|
|
1262
|
+
return;
|
|
1263
|
+
if (this.streamingComponent)
|
|
1264
|
+
this.removeGroupMember(this.streamingComponent);
|
|
1265
|
+
if (this.streamingToolSeparator)
|
|
1266
|
+
this.removeGroupMember(this.streamingToolSeparator);
|
|
1267
|
+
this.streamingWasGroupMember = false;
|
|
1268
|
+
this.closeOpenGroup();
|
|
1269
|
+
}
|
|
1270
|
+
seedToolGroupSummaries(summaries) {
|
|
1271
|
+
this.groupSummaries.clear();
|
|
1272
|
+
for (const [key, summary] of Object.entries(normalizeToolGroupSummaryRecord(summaries))) {
|
|
1273
|
+
this.groupSummaries.set(key, summary);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
/** Broker frames can arrive before or after their group's events, and snapshot
|
|
1277
|
+
* rebuilding defers them through the single-writer gate, so this stores first
|
|
1278
|
+
* and applies to every currently-rendered matching group. */
|
|
1279
|
+
applyToolGroupSummary(key, summary) {
|
|
1280
|
+
if (this.deferIfBuilding(() => this.applyToolGroupSummary(key, summary)))
|
|
1281
|
+
return;
|
|
1282
|
+
const normalized = normalizeToolGroupSummary(summary);
|
|
1283
|
+
if (key.trim() === '' || normalized === null)
|
|
1284
|
+
return;
|
|
1285
|
+
this.groupSummaries.set(key, normalized);
|
|
1286
|
+
this.applyToolDisplay();
|
|
1287
|
+
}
|
|
1288
|
+
applyGroupDisplay(group) {
|
|
1289
|
+
const summary = group.key === undefined ? undefined : this.groupSummaries.get(group.key);
|
|
1290
|
+
const collapsed = this.foldSettledTools &&
|
|
1291
|
+
this.showGroupSummaries &&
|
|
1292
|
+
group.toolCount > 0 &&
|
|
1293
|
+
group.inFlight.size === 0 &&
|
|
1294
|
+
summary !== undefined;
|
|
1295
|
+
group.recap.setRecap(collapsed ? summary : undefined, group.toolCount, group.activity.activity(), this.detailedToolRecaps, this.recapPalette);
|
|
1296
|
+
this.historyContainer.setHidden(group.recap, !collapsed);
|
|
1297
|
+
for (const member of group.members)
|
|
1298
|
+
this.historyContainer.setHidden(member, collapsed);
|
|
1299
|
+
}
|
|
977
1300
|
/** Make each separator visible only if its immediately-following tool call is
|
|
978
1301
|
* actually folded; in-flight calls remain flush with their live output. */
|
|
979
1302
|
updateFoldedToolSeparators(inFlight = new Set(this.pendingTools.values())) {
|
|
@@ -984,7 +1307,9 @@ export class ChatView {
|
|
|
984
1307
|
continue;
|
|
985
1308
|
const followingTool = children[i + 1];
|
|
986
1309
|
separator.setFolded(this.foldSettledTools &&
|
|
1310
|
+
!this.historyContainer.hiddenChildren.has(separator) &&
|
|
987
1311
|
followingTool instanceof MinimizableToolComponent &&
|
|
1312
|
+
!this.historyContainer.hiddenChildren.has(followingTool) &&
|
|
988
1313
|
!inFlight.has(followingTool));
|
|
989
1314
|
this.historyContainer.markDirty(separator);
|
|
990
1315
|
}
|
|
@@ -1037,6 +1362,33 @@ export class ChatView {
|
|
|
1037
1362
|
append(child) {
|
|
1038
1363
|
this.appendTarget.addChild(child);
|
|
1039
1364
|
}
|
|
1365
|
+
/** Project one raw assistant message through every attach-local Markdown
|
|
1366
|
+
* transform. This is the sole pipeline used by snapshots, streaming, settled
|
|
1367
|
+
* messages, and Ctrl+O re-renders. */
|
|
1368
|
+
renderAssistantMessage(message) {
|
|
1369
|
+
const withDiagrams = transformMessageDiagrams(message, this.renderWidth());
|
|
1370
|
+
const withHtml = transformMessageHtml(withDiagrams, {
|
|
1371
|
+
detailsExpanded: this.detailsExpandedOverride,
|
|
1372
|
+
styles: {
|
|
1373
|
+
kbd: (text) => this.recapPalette.active(this.recapPalette.bold(`‹${text}›`)),
|
|
1374
|
+
mark: (text) => `\x1b[7m${this.recapPalette.bold(this.recapPalette.accent(text))}\x1b[27m`,
|
|
1375
|
+
hint: this.dimStyle,
|
|
1376
|
+
},
|
|
1377
|
+
});
|
|
1378
|
+
return styleAttachMessageMarkdown(withHtml);
|
|
1379
|
+
}
|
|
1380
|
+
/** Retain canonical source and update one stable transcript component with its
|
|
1381
|
+
* viewer-local projection. */
|
|
1382
|
+
updateAssistantComponent(component, message) {
|
|
1383
|
+
this.assistantSources.set(component, message);
|
|
1384
|
+
component.updateContent(this.renderAssistantMessage(message));
|
|
1385
|
+
}
|
|
1386
|
+
/** Re-project every assistant component in place after a disclosure toggle. */
|
|
1387
|
+
rerenderAssistantMessages() {
|
|
1388
|
+
for (const [component, source] of this.assistantSources) {
|
|
1389
|
+
component.updateContent(this.renderAssistantMessage(source));
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1040
1392
|
/** Approximate render width for inline mermaid diagrams. Only affects
|
|
1041
1393
|
* termrender's trailing padding (stripped in the transform), so the terminal
|
|
1042
1394
|
* column count is close enough without threading the live pane width here. */
|
|
@@ -1054,15 +1406,34 @@ export class ChatView {
|
|
|
1054
1406
|
this.settleBashComponent();
|
|
1055
1407
|
this.streamingComponent = undefined;
|
|
1056
1408
|
this.streamingToolSeparator = undefined;
|
|
1409
|
+
this.streamingWasGroupMember = false;
|
|
1057
1410
|
this.foldedToolSeparatorContents.clear();
|
|
1411
|
+
this.assistantSources.clear();
|
|
1412
|
+
this.groups.length = 0;
|
|
1413
|
+
this.openGroup = undefined;
|
|
1414
|
+
this.groupSummaries.clear();
|
|
1058
1415
|
this.pendingTools.clear();
|
|
1059
1416
|
this.lastAssistantText = undefined;
|
|
1417
|
+
this.copyBlockRecords.length = 0;
|
|
1418
|
+
this.copyIndex.clear();
|
|
1060
1419
|
this.transcriptTexts.length = 0;
|
|
1061
1420
|
this.transcriptFilePaths.length = 0;
|
|
1062
1421
|
this.historyContainer.clear();
|
|
1063
1422
|
}
|
|
1064
1423
|
makeLoader(message) {
|
|
1065
|
-
|
|
1424
|
+
// Loader updates itself on an interval. Its TUI callback has to evict the
|
|
1425
|
+
// status block's cached frame before requesting the next render.
|
|
1426
|
+
let loader;
|
|
1427
|
+
const statusTui = {
|
|
1428
|
+
requestRender: (force) => {
|
|
1429
|
+
if (loader && !this.onActivityChange)
|
|
1430
|
+
this.statusContainer.markDirty(loader);
|
|
1431
|
+
this.tui.requestRender(force);
|
|
1432
|
+
},
|
|
1433
|
+
terminal: this.tui.terminal,
|
|
1434
|
+
};
|
|
1435
|
+
loader = new Loader(statusTui, this.spinnerStyle, this.dimStyle, message);
|
|
1436
|
+
return loader;
|
|
1066
1437
|
}
|
|
1067
1438
|
/** Let a self-updating history child invalidate only its own cached lines. */
|
|
1068
1439
|
componentTui(component) {
|
|
@@ -1086,16 +1457,22 @@ export class ChatView {
|
|
|
1086
1457
|
/** Swap the single active activity indicator (working/compaction/retry). */
|
|
1087
1458
|
setActivity(loader) {
|
|
1088
1459
|
this.activityLoader?.stop();
|
|
1089
|
-
this.statusContainer.clear();
|
|
1090
1460
|
this.activityLoader = loader;
|
|
1461
|
+
if (this.onActivityChange) {
|
|
1462
|
+
this.onActivityChange(loader);
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
this.statusContainer.clear();
|
|
1091
1466
|
if (loader)
|
|
1092
1467
|
this.statusContainer.addChild(loader);
|
|
1093
1468
|
}
|
|
1094
1469
|
showStatus(message) {
|
|
1470
|
+
this.closeOpenGroup();
|
|
1095
1471
|
this.append(new Spacer(1));
|
|
1096
1472
|
this.append(new Text(this.dimStyle(message), 1, 0));
|
|
1097
1473
|
}
|
|
1098
1474
|
showError(message) {
|
|
1475
|
+
this.closeOpenGroup();
|
|
1099
1476
|
this.append(new Spacer(1));
|
|
1100
1477
|
this.append(new Text(this.errorStyle(message), 1, 0));
|
|
1101
1478
|
}
|
|
@@ -1131,6 +1508,98 @@ export class ChatView {
|
|
|
1131
1508
|
const t = this.lastAssistantText?.trim();
|
|
1132
1509
|
return t ? t : undefined;
|
|
1133
1510
|
}
|
|
1511
|
+
/** Copyable source text for a transcript block, if it has any. */
|
|
1512
|
+
blockText(block) {
|
|
1513
|
+
const index = this.copyIndex.get(block);
|
|
1514
|
+
return index === undefined ? undefined : this.copyBlockRecords[index]?.text;
|
|
1515
|
+
}
|
|
1516
|
+
/** The canonical ordered copy record, oldest → newest. */
|
|
1517
|
+
copyBlocks() {
|
|
1518
|
+
return this.copyBlockRecords;
|
|
1519
|
+
}
|
|
1520
|
+
/** Newest tool call's result text (`/copy-tool`). */
|
|
1521
|
+
getLastToolOutputText() {
|
|
1522
|
+
for (let i = this.copyBlockRecords.length - 1; i >= 0; i--) {
|
|
1523
|
+
const copyBlock = this.copyBlockRecords[i];
|
|
1524
|
+
if (copyBlock.kind !== 'tool')
|
|
1525
|
+
continue;
|
|
1526
|
+
return copyBlock.text.trim() === '' ? undefined : copyBlock.text;
|
|
1527
|
+
}
|
|
1528
|
+
return undefined;
|
|
1529
|
+
}
|
|
1530
|
+
/** Every copyable block's text, oldest → newest, joined by blank lines. */
|
|
1531
|
+
getTranscriptPlainText() {
|
|
1532
|
+
return this.copyBlockRecords
|
|
1533
|
+
.map((copyBlock) => copyBlock.text)
|
|
1534
|
+
.filter((text) => text.trim() !== '')
|
|
1535
|
+
.join('\n\n');
|
|
1536
|
+
}
|
|
1537
|
+
registerCopyBlock(block, kind, text) {
|
|
1538
|
+
const condensedCopyBlocks = this.condensedCopyBlocks.get(this.appendTarget);
|
|
1539
|
+
if (condensedCopyBlocks) {
|
|
1540
|
+
const index = condensedCopyBlocks.findIndex((copyBlock) => copyBlock.block === block);
|
|
1541
|
+
if (index === -1)
|
|
1542
|
+
condensedCopyBlocks.push({ block, kind, text });
|
|
1543
|
+
else
|
|
1544
|
+
condensedCopyBlocks[index].text = text;
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
const index = this.copyIndex.get(block);
|
|
1548
|
+
if (index === undefined) {
|
|
1549
|
+
this.copyIndex.set(block, this.copyBlockRecords.length);
|
|
1550
|
+
this.copyBlockRecords.push({ block, kind, text });
|
|
1551
|
+
}
|
|
1552
|
+
else {
|
|
1553
|
+
this.copyBlockRecords[index].text = text;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
unregisterCopyBlock(block) {
|
|
1557
|
+
const condensedCopyBlocks = this.condensedCopyBlocks.get(this.appendTarget);
|
|
1558
|
+
if (condensedCopyBlocks) {
|
|
1559
|
+
const index = condensedCopyBlocks.findIndex((copyBlock) => copyBlock.block === block);
|
|
1560
|
+
if (index !== -1)
|
|
1561
|
+
condensedCopyBlocks.splice(index, 1);
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
const index = this.copyIndex.get(block);
|
|
1565
|
+
if (index === undefined)
|
|
1566
|
+
return;
|
|
1567
|
+
this.copyBlockRecords.splice(index, 1);
|
|
1568
|
+
this.copyIndex.delete(block);
|
|
1569
|
+
for (let i = index; i < this.copyBlockRecords.length; i++) {
|
|
1570
|
+
this.copyIndex.set(this.copyBlockRecords[i].block, i);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
appendCondensedBlock({ component, copyBlocks }) {
|
|
1574
|
+
this.append(component);
|
|
1575
|
+
this.registerCopyBlock(component, 'condensed', copyBlocks.map((copyBlock) => copyBlock.text).filter((text) => text.trim() !== '').join('\n\n'));
|
|
1576
|
+
}
|
|
1577
|
+
/** Flatten arbitrary tool result payloads into the visible text copy target. */
|
|
1578
|
+
toolResultText(result) {
|
|
1579
|
+
if (typeof result === 'string')
|
|
1580
|
+
return result;
|
|
1581
|
+
if (typeof result === 'object' && result !== null && 'content' in result) {
|
|
1582
|
+
const content = result.content;
|
|
1583
|
+
if (typeof content === 'string')
|
|
1584
|
+
return content;
|
|
1585
|
+
if (Array.isArray(content)) {
|
|
1586
|
+
return content
|
|
1587
|
+
.filter((part) => typeof part === 'object' &&
|
|
1588
|
+
part !== null &&
|
|
1589
|
+
part.type === 'text' &&
|
|
1590
|
+
typeof part.text === 'string')
|
|
1591
|
+
.map((part) => part.text)
|
|
1592
|
+
.join('');
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
try {
|
|
1596
|
+
const text = JSON.stringify(result);
|
|
1597
|
+
return typeof text === 'string' ? text : '';
|
|
1598
|
+
}
|
|
1599
|
+
catch {
|
|
1600
|
+
return '';
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1134
1603
|
pushTranscriptText(text) {
|
|
1135
1604
|
const trimmed = text.trim();
|
|
1136
1605
|
if (trimmed === '')
|