@pellux/goodvibes-tui 2.0.21 → 2.1.0
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/CHANGELOG.md +58 -0
- package/README.md +16 -16
- package/docs/README.md +4 -4
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +16 -6
- package/src/cli/management.ts +5 -2
- package/src/cli/tui-startup.ts +34 -39
- package/src/core/agent-view-render.ts +157 -0
- package/src/core/approval-alert.ts +49 -11
- package/src/core/bookmark-navigation.ts +7 -1
- package/src/core/budget-breach-notifier.ts +27 -8
- package/src/core/compaction-receipt.ts +1 -1
- package/src/core/context-usage.ts +1 -1
- package/src/core/conversation-compaction-render.ts +3 -3
- package/src/core/conversation-display-only.ts +86 -0
- package/src/core/conversation-event-navigation.ts +27 -0
- package/src/core/conversation-line-cache.ts +216 -447
- package/src/core/conversation-render-context.ts +20 -27
- package/src/core/conversation-rendering.ts +157 -575
- package/src/core/conversation-splash-state.ts +6 -1
- package/src/core/conversation-types.ts +35 -0
- package/src/core/conversation-usage.ts +12 -0
- package/src/core/conversation.ts +130 -131
- package/src/core/long-task-notifier.ts +100 -31
- package/src/core/notices.ts +75 -0
- package/src/core/pairing-tailscale-gateway.ts +1 -1
- package/src/core/process-output-log.ts +143 -0
- package/src/core/rewind-receipt.ts +1 -1
- package/src/core/session-focus.ts +190 -0
- package/src/core/session-resume-core.ts +23 -81
- package/src/core/stream-event-wiring.ts +31 -5
- package/src/core/system-message-noise.ts +2 -2
- package/src/core/system-message-router.ts +16 -18
- package/src/core/turn-event-wiring.ts +272 -33
- package/src/core/work-tree-focus.ts +290 -0
- package/src/core/work-tree-fold-store.ts +143 -0
- package/src/core/work-tree-model.ts +799 -0
- package/src/core/work-tree-render.ts +384 -0
- package/src/core/work-tree-sources.ts +184 -0
- package/src/core/work-tree-wiring.ts +176 -0
- package/src/core/workstream-notification.ts +36 -6
- package/src/core/wrfc-notice-titles.ts +37 -0
- package/src/input/agents-modal-text.ts +119 -0
- package/src/input/agents-modal.ts +699 -0
- package/src/input/bookmark-modal.ts +33 -9
- package/src/input/changes-git.ts +251 -0
- package/src/input/changes-modal.ts +629 -0
- package/src/input/changes-model.ts +125 -0
- package/src/input/command-palette-types.ts +41 -0
- package/src/input/command-palette.ts +272 -0
- package/src/input/command-registry.ts +77 -35
- package/src/input/commands/acp-runtime.ts +19 -9
- package/src/input/commands/channel-runtime.ts +2 -2
- package/src/input/commands/ci-runtime.ts +1 -1
- package/src/input/commands/codebase-runtime.ts +1 -1
- package/src/input/commands/config.ts +7 -1
- package/src/input/commands/context-window.ts +25 -19
- package/src/input/commands/control-room-runtime.ts +18 -18
- package/src/input/commands/conversation-runtime.ts +0 -1
- package/src/input/commands/cost-runtime.ts +29 -20
- package/src/input/commands/diff-runtime.ts +35 -181
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/experience-runtime.ts +3 -7
- package/src/input/commands/fresh-conversation.ts +86 -0
- package/src/input/commands/git-runtime.ts +5 -22
- package/src/input/commands/graph-runtime.ts +1 -1
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +8 -9
- package/src/input/commands/hooks-runtime.ts +2 -2
- package/src/input/commands/hosted-runtime.ts +3 -3
- package/src/input/commands/incident-runtime.ts +3 -3
- package/src/input/commands/intelligence-runtime.ts +6 -9
- package/src/input/commands/knowledge.ts +2 -2
- package/src/input/commands/legacy-panel-command.ts +57 -0
- package/src/input/commands/local-auth-runtime.ts +8 -9
- package/src/input/commands/local-provider-runtime.ts +7 -4
- package/src/input/commands/local-runtime.ts +12 -15
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/local-setup.ts +13 -19
- package/src/input/commands/memory.ts +1 -1
- package/src/input/commands/notify-runtime.ts +9 -0
- package/src/input/commands/operator-runtime.ts +10 -12
- package/src/input/commands/permissions-provenance.ts +3 -3
- package/src/input/commands/planning-runtime.ts +7 -7
- package/src/input/commands/platform-sandbox-runtime.ts +2 -2
- package/src/input/commands/platform-services-runtime.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +2 -2
- package/src/input/commands/policy.ts +3 -3
- package/src/input/commands/provider.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/review-runtime.ts +39 -94
- package/src/input/commands/rewind-runtime.ts +81 -108
- package/src/input/commands/runtime-services.ts +15 -17
- package/src/input/commands/services-runtime.ts +2 -2
- package/src/input/commands/session-content.ts +21 -5
- package/src/input/commands/session-workflow.ts +13 -38
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/shell-core.ts +19 -4
- package/src/input/commands/skills-runtime.ts +2 -2
- package/src/input/commands/status-runtime.ts +13 -0
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/tasks-runtime.ts +2 -8
- package/src/input/commands/work-plan-runtime.ts +4 -4
- package/src/input/commands/workstream-attempts.ts +24 -47
- package/src/input/commands/workstream-runtime.ts +2 -2
- package/src/input/commands/worktree-runtime.ts +3 -3
- package/src/input/commands.ts +3 -1
- package/src/input/composer-editor.ts +44 -0
- package/src/input/concealed-input.ts +1 -1
- package/src/input/config-modal-types.ts +20 -8
- package/src/input/config-modal.ts +106 -105
- package/src/input/confirm-dialog-types.ts +28 -0
- package/src/input/confirm-dialog.ts +70 -0
- package/src/input/delete-key-policy.ts +3 -3
- package/src/input/feed-context-factory.ts +15 -16
- package/src/input/handler-command-route.ts +7 -42
- package/src/input/handler-content-actions.ts +16 -15
- package/src/input/handler-feed-routes.ts +26 -211
- package/src/input/handler-feed.ts +68 -95
- package/src/input/handler-interactions.ts +5 -3
- package/src/input/handler-modal-routes.ts +215 -216
- package/src/input/handler-modal-stack.ts +81 -11
- package/src/input/handler-modal-token-routes.ts +4 -0
- package/src/input/handler-onboarding.ts +0 -1
- package/src/input/handler-picker-routes.ts +65 -107
- package/src/input/handler-session-view-route.ts +149 -0
- package/src/input/handler-shortcuts.ts +16 -154
- package/src/input/handler-types.ts +4 -2
- package/src/input/handler-ui-state.ts +45 -15
- package/src/input/handler-work-tree-route.ts +123 -0
- package/src/input/handler.ts +25 -70
- package/src/input/keybindings.ts +16 -71
- package/src/input/masked-entry-modal.ts +117 -0
- package/src/input/mcp-workspace.ts +48 -17
- package/src/input/model-picker-filter.ts +8 -0
- package/src/input/model-picker-open.ts +99 -0
- package/src/input/model-picker-types.ts +1 -1
- package/src/input/model-picker.ts +92 -9
- package/src/input/notifications-modal.ts +123 -0
- package/src/input/overlay-filter.ts +26 -0
- package/src/input/profile-picker-modal.ts +37 -8
- package/src/input/search.ts +3 -3
- package/src/input/selection-modal-queue.ts +2 -0
- package/src/input/selection-modal.ts +28 -16
- package/src/input/session-picker-modal.ts +33 -8
- package/src/input/settings-modal-activation.ts +7 -2
- package/src/input/settings-modal-data.ts +11 -6
- package/src/input/settings-modal-types.ts +1 -1
- package/src/input/settings-modal.ts +20 -20
- package/src/input/submission-intent.ts +1 -1
- package/src/input/submission-router.ts +4 -9
- package/src/input/surface-modal-host.ts +110 -0
- package/src/input/theme-settings-actions.ts +82 -0
- package/src/input/usage-modal.ts +183 -0
- package/src/input/views.ts +97 -0
- package/src/main.ts +175 -165
- package/src/permissions/hunk-selection.ts +1 -1
- package/src/permissions/prompt-card.ts +511 -0
- package/src/permissions/prompt.ts +59 -622
- package/src/renderer/agents-modal.ts +310 -0
- package/src/renderer/autocomplete-overlay.ts +42 -220
- package/src/renderer/back-to-bottom.ts +80 -0
- package/src/renderer/block-actions-overlay.ts +30 -48
- package/src/renderer/bookmark-modal.ts +48 -79
- package/src/renderer/changes-modal.ts +325 -0
- package/src/renderer/code-block.ts +184 -106
- package/src/renderer/command-palette.ts +93 -0
- package/src/renderer/composer-fragments.ts +4 -4
- package/src/renderer/composer.ts +146 -0
- package/src/renderer/compositor.ts +26 -160
- package/src/renderer/config-modal.ts +185 -81
- package/src/renderer/confirm-dialog.ts +50 -0
- package/src/renderer/context-inspector.ts +116 -154
- package/src/renderer/context-status-hint.ts +4 -4
- package/src/renderer/conversation-layout.ts +20 -0
- package/src/renderer/conversation-overlays.ts +94 -84
- package/src/renderer/conversation-surface.ts +19 -38
- package/src/renderer/diff-tint.ts +98 -0
- package/src/renderer/file-picker-overlay.ts +63 -90
- package/src/renderer/footer-targets.ts +38 -0
- package/src/renderer/git-status.ts +3 -4
- package/src/renderer/header-line.ts +140 -117
- package/src/renderer/help-overlay.ts +133 -219
- package/src/renderer/history-search-overlay.ts +15 -12
- package/src/renderer/lane-graph/bead.ts +800 -0
- package/src/renderer/lane-graph/glyphs.ts +134 -0
- package/src/renderer/lane-graph/layout.ts +248 -0
- package/src/renderer/lane-graph/paint.ts +537 -0
- package/src/renderer/lane-graph/semantic-memo.ts +77 -0
- package/src/renderer/layout-engine.ts +2 -57
- package/src/renderer/layout.ts +7 -13
- package/src/renderer/markdown-inline.ts +2 -1
- package/src/renderer/markdown-table.ts +17 -12
- package/src/renderer/markdown.ts +92 -34
- package/src/renderer/masked-entry-modal.ts +78 -0
- package/src/renderer/mcp-workspace.ts +166 -203
- package/src/renderer/model-workspace.ts +381 -436
- package/src/renderer/notifications-modal.ts +116 -0
- package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
- package/src/renderer/process-view.ts +192 -0
- package/src/renderer/profile-picker-modal.ts +58 -110
- package/src/renderer/search-overlay.ts +6 -3
- package/src/renderer/selection-modal-overlay.ts +87 -327
- package/src/renderer/semantic-diff.ts +2 -2
- package/src/renderer/session-chips.ts +102 -0
- package/src/renderer/session-picker-modal.ts +177 -297
- package/src/renderer/settings-modal-connections.ts +14 -23
- package/src/renderer/settings-modal-context.ts +367 -0
- package/src/renderer/settings-modal-helpers.ts +6 -11
- package/src/renderer/settings-modal.ts +322 -726
- package/src/renderer/shell-surface.ts +277 -175
- package/src/renderer/shortcuts-overlay.ts +217 -0
- package/src/renderer/status-line.ts +475 -0
- package/src/renderer/surface-compose.ts +174 -0
- package/src/renderer/surface-kit-extra.ts +303 -0
- package/src/renderer/surface-kit-list.ts +366 -0
- package/src/renderer/surface-kit-parts.ts +360 -0
- package/src/renderer/surface-kit.ts +568 -0
- package/src/renderer/syntax-highlighter.ts +280 -197
- package/src/renderer/syntax-theme.ts +51 -0
- package/src/renderer/system-message.ts +0 -1
- package/src/renderer/term-caps.ts +42 -1
- package/src/renderer/terminal-bg-probe.ts +116 -19
- package/src/renderer/terminal-palette-probe.ts +322 -0
- package/src/renderer/terminal-palette-reader.ts +183 -0
- package/src/renderer/terminal-palette.ts +58 -0
- package/src/renderer/theme-mode-config.ts +16 -1
- package/src/renderer/theme.ts +234 -207
- package/src/renderer/thinking.ts +0 -1
- package/src/renderer/throbber.ts +188 -0
- package/src/renderer/toast-center.ts +119 -0
- package/src/renderer/ui-factory.ts +39 -602
- package/src/renderer/ui-primitives.ts +8 -25
- package/src/renderer/usage-modal.ts +353 -0
- package/src/renderer/user-message.ts +58 -0
- package/src/renderer/voice-capture-chip.ts +53 -0
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +38 -89
- package/src/runtime/bootstrap-core.ts +13 -15
- package/src/runtime/bootstrap-hook-bridge.ts +12 -21
- package/src/runtime/bootstrap-notifier-sync.ts +17 -1
- package/src/runtime/bootstrap-shell.ts +16 -50
- package/src/runtime/bootstrap.ts +14 -14
- package/src/runtime/client/fleet-union.ts +5 -5
- package/src/runtime/client/hosted-exit.ts +2 -2
- package/src/runtime/client/hosted-sessions.ts +1 -1
- package/src/runtime/client/spine-adoption.ts +1 -1
- package/src/runtime/context-accounting-source.ts +5 -4
- package/src/runtime/index.ts +1 -0
- package/src/runtime/notification-dispatch.ts +102 -29
- package/src/runtime/recovery-offer-wiring.ts +4 -1
- package/src/runtime/recovery-prompt.ts +31 -15
- package/src/runtime/runtime-services-types.ts +10 -6
- package/src/runtime/services.ts +17 -12
- package/src/runtime/session-continuity-hints.ts +2 -5
- package/src/runtime/session-inbound-inputs.ts +1 -1
- package/src/runtime/session-resume-liveness-confirm.ts +14 -2
- package/src/runtime/startup-typeahead-gate.ts +103 -0
- package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
- package/src/runtime/store/domains/index.ts +0 -8
- package/src/runtime/store/selectors/index.ts +0 -28
- package/src/runtime/store/state.ts +0 -8
- package/src/runtime/ui-services.ts +4 -6
- package/src/runtime/usage-tracker.ts +348 -0
- package/src/runtime/wrfc-persistence.ts +4 -4
- package/src/shell/blocking-input.ts +92 -2
- package/src/shell/recovery-input-helpers.ts +1 -1
- package/src/shell/session-views.ts +539 -0
- package/src/shell/status-report.ts +86 -0
- package/src/shell/transcript-scroll.ts +68 -0
- package/src/shell/ui-openers.ts +84 -172
- package/src/shell/view-openers.ts +205 -0
- package/src/tools/wrfc-agent-guard.ts +22 -2
- package/src/utils/format-duration.ts +4 -8
- package/src/utils/splash-lines.ts +9 -1
- package/src/utils/terminal-width.ts +56 -0
- package/src/verification/verification-ledger.ts +18 -16
- package/src/{panels → views}/agent-inspector-shared.ts +2 -2
- package/src/{panels → views}/builtin-modals.ts +22 -21
- package/src/views/builtin-views.ts +135 -0
- package/src/{panels → views}/confirm-state.ts +9 -9
- package/src/{panels → views}/diff-review-model.ts +10 -3
- package/src/{panels → views}/eval-registry.ts +1 -1
- package/src/{panels → views}/fleet-acts.ts +49 -85
- package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
- package/src/{panels → views}/fleet-gateway.ts +5 -5
- package/src/{panels → views}/fleet-observed-render.ts +16 -16
- package/src/{panels → views}/fleet-read-model.ts +9 -9
- package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
- package/src/{panels → views}/fleet-spawn.ts +33 -49
- package/src/{panels → views}/fleet-steer.ts +12 -12
- package/src/{panels → views}/fleet-stop.ts +4 -4
- package/src/{panels → views}/fleet-transcript.ts +26 -27
- package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
- package/src/{panels → views}/hosted-session-feed.ts +6 -6
- package/src/views/modal-surface-registry.ts +41 -0
- package/src/{panels → views}/modals/devices-modal.ts +2 -2
- package/src/{panels → views}/modals/hooks-modal.ts +13 -13
- package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
- package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
- package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
- package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
- package/src/{panels → views}/modals/memory-modal.ts +8 -8
- package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
- package/src/{panels → views}/modals/pairing-modal.ts +9 -9
- package/src/{panels → views}/modals/planning-modal.ts +15 -20
- package/src/{panels → views}/modals/plugins-modal.ts +6 -6
- package/src/{panels → views}/modals/policy-modal.ts +13 -5
- package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
- package/src/{panels → views}/modals/remote-modal.ts +3 -3
- package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
- package/src/{panels → views}/modals/security-modal.ts +22 -16
- package/src/{panels → views}/modals/services-modal.ts +3 -3
- package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
- package/src/{panels → views}/modals/skills-modal.ts +9 -9
- package/src/{panels → views}/modals/subscription-modal.ts +3 -3
- package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
- package/src/views/notifications-feed.ts +314 -0
- package/src/{panels → views}/polish-core.ts +38 -36
- package/src/{panels → views}/polish-tables.ts +35 -26
- package/src/{panels → views}/polish.ts +101 -95
- package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
- package/src/{panels → views}/provider-health-routes.ts +14 -14
- package/src/{panels → views}/search-focus.ts +8 -8
- package/src/{panels → views}/session-maintenance.ts +11 -11
- package/src/views/skills-discovery.ts +159 -0
- package/src/views/view-deps.ts +80 -0
- package/src/views/view-panel-adapter.ts +53 -0
- package/src/{panels → views}/workstream-graph-render.ts +1 -1
- package/src/core/conversation-fold.ts +0 -101
- package/src/core/conversation-turn-structure.ts +0 -464
- package/src/core/conversation-user-receipts.ts +0 -42
- package/src/input/commands/operator-panel-runtime.ts +0 -186
- package/src/input/panel-integration-actions.ts +0 -33
- package/src/input/panel-mouse-geometry.ts +0 -97
- package/src/input/panel-paste-flood-guard.ts +0 -86
- package/src/panels/base-panel.ts +0 -301
- package/src/panels/builtin/agent.ts +0 -27
- package/src/panels/builtin/development.ts +0 -92
- package/src/panels/builtin/knowledge.ts +0 -12
- package/src/panels/builtin/operations.ts +0 -177
- package/src/panels/builtin/session.ts +0 -81
- package/src/panels/builtin/shared.ts +0 -357
- package/src/panels/builtin-panels.ts +0 -28
- package/src/panels/cost-tracker-panel.ts +0 -746
- package/src/panels/diff-panel.ts +0 -797
- package/src/panels/diff-review-panel.ts +0 -395
- package/src/panels/expandable-list-panel.ts +0 -189
- package/src/panels/fleet-deep-link.ts +0 -31
- package/src/panels/fleet-panel.ts +0 -799
- package/src/panels/git-panel.ts +0 -800
- package/src/panels/hosted-session-panel.ts +0 -221
- package/src/panels/index.ts +0 -21
- package/src/panels/local-auth-panel.ts +0 -478
- package/src/panels/notifications-feed.ts +0 -127
- package/src/panels/notifications-panel.ts +0 -160
- package/src/panels/panel-confirm-overlay.ts +0 -72
- package/src/panels/panel-manager.ts +0 -791
- package/src/panels/plugins-panel.ts +0 -371
- package/src/panels/scrollable-list-panel.ts +0 -498
- package/src/panels/skills-panel.ts +0 -454
- package/src/panels/token-budget-panel.ts +0 -688
- package/src/panels/types.ts +0 -191
- package/src/panels/wrfc-panel-format.ts +0 -133
- package/src/renderer/diff-view.ts +0 -134
- package/src/renderer/fleet-tab-strip.ts +0 -56
- package/src/renderer/footer-tips.ts +0 -53
- package/src/renderer/fullscreen-primitives.ts +0 -146
- package/src/renderer/fullscreen-workspace.ts +0 -194
- package/src/renderer/hint-grammar.ts +0 -52
- package/src/renderer/modal-factory.ts +0 -480
- package/src/renderer/overlay-box.ts +0 -154
- package/src/renderer/overlay-viewport.ts +0 -104
- package/src/renderer/panel-composite.ts +0 -145
- package/src/renderer/panel-workspace-bar.ts +0 -54
- package/src/renderer/process-indicator.ts +0 -163
- package/src/renderer/tab-strip.ts +0 -222
- package/src/renderer/text-layout.ts +0 -31
- package/src/renderer/tool-call.ts +0 -276
- package/src/renderer/tool-result-expanded-lines.ts +0 -48
- package/src/renderer/tool-result-summary.ts +0 -219
- package/src/runtime/diagnostics/panels/index.ts +0 -21
- package/src/runtime/diagnostics/panels/ops.ts +0 -156
- package/src/runtime/perf/panel-contracts.ts +0 -32
- package/src/runtime/store/domains/panels.ts +0 -117
- /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
- /package/src/{panels → views}/modals/modal-theme.ts +0 -0
- /package/src/{panels → views}/provider-health-domains.ts +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Two pieces of state, kept out of ConversationManager (core/conversation.ts,
|
|
6
6
|
* already at its line-count gate) and out of `suppressSplash`, which is a
|
|
7
|
-
* per-frame posture the
|
|
7
|
+
* per-frame posture the modal layer sets both ways on every render:
|
|
8
8
|
*
|
|
9
9
|
* - `dismissed`, sticky for the run. Owner rule: the splash yields to ANY
|
|
10
10
|
* submission, a chat message or a slash command alike. A slash command
|
|
@@ -37,6 +37,11 @@ export class SplashGateState {
|
|
|
37
37
|
return true;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/** True while the last rebuild drew the splash (the shell centers it). */
|
|
41
|
+
get showing(): boolean {
|
|
42
|
+
return this.onScreen;
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
/** Record that this rebuild drew the splash. */
|
|
41
46
|
enter(): void {
|
|
42
47
|
this.onScreen = true;
|
|
@@ -47,4 +47,39 @@ export type BlockMeta = Omit<SdkBlockMeta, 'type'> & {
|
|
|
47
47
|
* standalone tool results with no recorded name.
|
|
48
48
|
*/
|
|
49
49
|
toolName?: string;
|
|
50
|
+
/**
|
|
51
|
+
* The collapse key that reveals each entry of groupMemberIndexes (same
|
|
52
|
+
* order): the bead body the result sits behind (work-tree-model.ts
|
|
53
|
+
* beadKeyOf). /expand opens them in the same pass that unfolds the turn,
|
|
54
|
+
* and search reveals a hit by opening exactly its bead.
|
|
55
|
+
*/
|
|
56
|
+
groupMemberKeys?: readonly string[];
|
|
57
|
+
/**
|
|
58
|
+
* The work-tree row this block is, when it is one: 'turn' (a turn header),
|
|
59
|
+
* 'bead' (a tool call) or 'lane' (an agent lane's spawn or folded row). The
|
|
60
|
+
* keyboard moves between these rows (see ConversationManager focus methods).
|
|
61
|
+
*/
|
|
62
|
+
workTree?: {
|
|
63
|
+
readonly kind: 'turn' | 'bead' | 'lane';
|
|
64
|
+
/** Focus identity, stable across rebuilds. */
|
|
65
|
+
readonly id: string;
|
|
66
|
+
/** The bead has a body to open (then ▸ shows). */
|
|
67
|
+
readonly hasBody: boolean;
|
|
68
|
+
/** The body is open. */
|
|
69
|
+
readonly open: boolean;
|
|
70
|
+
/** The body shows its first rows only ("… N more"). */
|
|
71
|
+
readonly capped: boolean;
|
|
72
|
+
/** Collapse key of the body's "… N more" (bead only). */
|
|
73
|
+
readonly moreKey?: string;
|
|
74
|
+
/** Whether the lane has finished (lane only). */
|
|
75
|
+
readonly finished?: boolean;
|
|
76
|
+
/** The lane this row sits on (bead rows inside an agent lane carry that lane's collapse key). */
|
|
77
|
+
readonly laneKey?: string;
|
|
78
|
+
/** The agent a lane row draws (Enter opens it full screen). */
|
|
79
|
+
readonly agentId?: string;
|
|
80
|
+
/** A lane row's color index (lane-graph/paint.ts laneColor): the agent's own color. */
|
|
81
|
+
readonly colorIndex?: number;
|
|
82
|
+
/** The background process a ▶ bead started (Enter opens its output). */
|
|
83
|
+
readonly processId?: string;
|
|
84
|
+
};
|
|
50
85
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* conversation-usage.ts, usage totals folded from a transcript's assistant
|
|
3
|
+
* messages (re-exported by conversation.ts).
|
|
4
|
+
*
|
|
5
|
+
* The fold is the SDK's (platform/core sumConversationUsage): a resumed
|
|
6
|
+
* session hydrates its token counters and its context figure from it. The
|
|
7
|
+
* context figure is the latest real turn request's; a follow-up
|
|
8
|
+
* acknowledgement (sent without tool definitions) counts toward the totals
|
|
9
|
+
* only, so the meter never drops to that smaller request's size.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export { sumConversationUsage } from '@pellux/goodvibes-sdk/platform/core';
|
package/src/core/conversation.ts
CHANGED
|
@@ -12,22 +12,25 @@ import {
|
|
|
12
12
|
type BlockMeta as SdkBlockMeta,
|
|
13
13
|
} from '@pellux/goodvibes-sdk/platform/core';
|
|
14
14
|
import type { BlockMeta } from './conversation-types.ts';
|
|
15
|
+
import type { NoticeSink } from './notices.ts';
|
|
15
16
|
import { MessageLineCache } from './conversation-line-cache.ts';
|
|
16
|
-
import { UserReceiptIndices } from './conversation-user-receipts.ts';
|
|
17
17
|
import { SplashGateState } from './conversation-splash-state.ts';
|
|
18
|
-
import { resolveTranscriptEventLine } from './conversation-event-navigation.ts';
|
|
18
|
+
import { nearestBlock, resolveTranscriptEventLine, wrapToLine } from './conversation-event-navigation.ts';
|
|
19
|
+
import { DisplayOnlyOutput } from './conversation-display-only.ts';
|
|
19
20
|
import { SearchExpansionTracker } from './conversation-search-expansion.ts';
|
|
20
21
|
import {
|
|
21
22
|
addConversationSplashScreen,
|
|
22
23
|
conversationTextToLines,
|
|
23
24
|
logConversationText,
|
|
24
25
|
logConversationToolResult,
|
|
25
|
-
renderConversationAssistantMessage,
|
|
26
|
-
renderConversationSystemMessage,
|
|
27
|
-
renderConversationToolMessage,
|
|
28
|
-
renderConversationUserMessage,
|
|
29
26
|
} from './conversation-rendering.ts';
|
|
30
27
|
import { renderMarkdown } from '../renderer/markdown.ts';
|
|
28
|
+
import type { WorkTreeSources } from './work-tree-sources.ts';
|
|
29
|
+
import { renderStreamingContinuation } from './work-tree-render.ts';
|
|
30
|
+
import { WorkTreeController } from './work-tree-focus.ts';
|
|
31
|
+
import { isWorkTreeFoldKey } from './work-tree-fold-store.ts';
|
|
32
|
+
import { resolveTreeGlyphSet, type TreeGlyphSetName } from '../renderer/lane-graph/glyphs.ts';
|
|
33
|
+
import { probeUnicodeSupport } from '../renderer/term-caps.ts';
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
36
|
* ConversationManager - TUI subclass of the SDK's ConversationManager.
|
|
@@ -62,39 +65,12 @@ type Message = ConversationMessageSnapshot;
|
|
|
62
65
|
import type { OrchestratorUsageTotals } from '@pellux/goodvibes-sdk/platform/core';
|
|
63
66
|
export type { OrchestratorUsageTotals };
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
* sumConversationUsage - Fold every assistant message's per-turn usage into
|
|
67
|
-
* running totals, plus the *last* assistant message's own input-token figure
|
|
68
|
-
* (context-window occupancy, not a running sum).
|
|
69
|
-
*
|
|
70
|
-
* TUI-side counterpart to SDK Orchestrator.usage: after a session resume
|
|
71
|
-
* replays historical messages into a freshly-constructed Orchestrator (whose
|
|
72
|
-
* `usage` starts at all zeros, bootstrap.ts always constructs a fresh
|
|
73
|
-
* instance, see), this lets the caller hydrate the footer's token
|
|
74
|
-
* counters from messages that already carry real usage data instead of
|
|
75
|
-
* waiting for the first new turn to populate them.
|
|
76
|
-
*/
|
|
77
|
-
export function sumConversationUsage(
|
|
78
|
-
messages: readonly ConversationMessageSnapshot[],
|
|
79
|
-
): { usage: OrchestratorUsageTotals; lastInputTokens: number } {
|
|
80
|
-
const usage: OrchestratorUsageTotals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
81
|
-
let lastInputTokens = 0;
|
|
82
|
-
for (const message of messages) {
|
|
83
|
-
if (message.role !== 'assistant' || !message.usage) continue;
|
|
84
|
-
usage.input += message.usage.inputTokens;
|
|
85
|
-
usage.output += message.usage.outputTokens;
|
|
86
|
-
usage.cacheRead += message.usage.cacheReadTokens ?? 0;
|
|
87
|
-
usage.cacheWrite += message.usage.cacheWriteTokens ?? 0;
|
|
88
|
-
// Context-window occupancy tracks the most recent turn only, not a sum.
|
|
89
|
-
lastInputTokens = message.usage.inputTokens
|
|
90
|
-
+ (message.usage.cacheReadTokens ?? 0)
|
|
91
|
-
+ (message.usage.cacheWriteTokens ?? 0);
|
|
92
|
-
}
|
|
93
|
-
return { usage, lastInputTokens };
|
|
94
|
-
}
|
|
68
|
+
export { sumConversationUsage } from './conversation-usage.ts';
|
|
95
69
|
|
|
96
70
|
export class ConversationManager extends SdkConversationManager {
|
|
97
71
|
public history = new InfiniteBuffer();
|
|
72
|
+
/** Display-only output kept for redraw (conversation-display-only.ts). */
|
|
73
|
+
private readonly displayOnly = new DisplayOnlyOutput();
|
|
98
74
|
private _getWidth: () => number;
|
|
99
75
|
/** Tracks the rendered width; a change invalidates the full history. */
|
|
100
76
|
private lastRenderedWidth = 0;
|
|
@@ -143,8 +119,6 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
143
119
|
private errorLineRegistry: number[] = [];
|
|
144
120
|
/** Maps message index → SystemMessageKind for system-role messages. */
|
|
145
121
|
private messageKindRegistry: Map<number, SystemMessageKind> = new Map();
|
|
146
|
-
/** See conversation-user-receipts.ts. */
|
|
147
|
-
private userReceiptIndices = new UserReceiptIndices();
|
|
148
122
|
/** Streaming block start line in history buffer (for incremental streaming update). */
|
|
149
123
|
private streamingStartLine = -1;
|
|
150
124
|
/**
|
|
@@ -169,9 +143,24 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
169
143
|
private _displayFromMessageIndex = 0;
|
|
170
144
|
|
|
171
145
|
public suppressSplash: boolean = false;
|
|
146
|
+
private noticeSink: NoticeSink | null = null;
|
|
172
147
|
public splashOptions: SplashOptions = {};
|
|
173
148
|
/** Run-sticky dismissal + the splash→transcript edge (conversation-splash-state.ts). */
|
|
174
149
|
private readonly splashGate = new SplashGateState();
|
|
150
|
+
/** Live facts the work tree reads (timings, agent lanes, waiting calls); see work-tree-sources.ts. */
|
|
151
|
+
private workTreeSources: WorkTreeSources = {};
|
|
152
|
+
/** Turn activity at the last build: a change forces a rebuild (a finished turn's header must drop "working"). */
|
|
153
|
+
private builtWhileActive = false;
|
|
154
|
+
/** Whether the terminal draws unicode (term-caps.ts); ascii work-tree glyphs when not. */
|
|
155
|
+
private unicodeCapable = probeUnicodeSupport();
|
|
156
|
+
/** Keyboard focus, live repaint and fold state of the work tree (work-tree-focus.ts). */
|
|
157
|
+
public readonly workTree: WorkTreeController = new WorkTreeController({
|
|
158
|
+
blocks: () => { this.flushHistory(); return this.blockRegistry; },
|
|
159
|
+
collapseState: () => this.collapseState,
|
|
160
|
+
markDirty: () => this.markDirty(),
|
|
161
|
+
noteUserTouch: (key) => this.searchExpansion.noteUserTouch(key),
|
|
162
|
+
drewLive: () => this.lineCache.live,
|
|
163
|
+
});
|
|
175
164
|
|
|
176
165
|
constructor(
|
|
177
166
|
getWidth: () => number = () => process.stdout.columns || 80,
|
|
@@ -229,23 +218,27 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
229
218
|
// stale kind (e.g. 'operational') and silently mis-classify the new message.
|
|
230
219
|
const nextIndex = this.getMessageSnapshot().length;
|
|
231
220
|
this.messageKindRegistry.delete(nextIndex);
|
|
232
|
-
this.userReceiptIndices.delete(nextIndex);
|
|
233
221
|
super.addSystemMessage(content);
|
|
234
222
|
this.markDirty();
|
|
223
|
+
this.noticeSink?.(content, { restored: false });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Where system notices go (core/notices.ts); the transcript draws none. Absent in bare test conversations. */
|
|
227
|
+
public setNoticeSink(sink: NoticeSink | null): void {
|
|
228
|
+
this.noticeSink = sink;
|
|
235
229
|
}
|
|
236
230
|
|
|
237
231
|
/**
|
|
238
232
|
* addTypedSystemMessage - System message with an explicit kind tag, stored
|
|
239
|
-
* in messageKindRegistry.
|
|
240
|
-
*
|
|
241
|
-
* direct receipt to an explicit user action, never ambient boot chatter.
|
|
233
|
+
* in messageKindRegistry. Like every system message it is a notice: the
|
|
234
|
+
* notice sink shows it, the transcript does not.
|
|
242
235
|
*/
|
|
243
|
-
public addTypedSystemMessage(content: string, kind: SystemMessageKind
|
|
236
|
+
public addTypedSystemMessage(content: string, kind: SystemMessageKind): void {
|
|
244
237
|
const nextIndex = this.getMessageSnapshot().length;
|
|
245
238
|
this.messageKindRegistry.set(nextIndex, kind);
|
|
246
|
-
if (opts?.isUserReceipt) this.userReceiptIndices.add(nextIndex); else this.userReceiptIndices.delete(nextIndex);
|
|
247
239
|
super.addSystemMessage(content);
|
|
248
240
|
this.markDirty();
|
|
241
|
+
this.noticeSink?.(content, { restored: false });
|
|
249
242
|
}
|
|
250
243
|
|
|
251
244
|
public override undo(): boolean {
|
|
@@ -260,7 +253,6 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
260
253
|
for (const key of this.messageKindRegistry.keys()) {
|
|
261
254
|
if (key >= postUndoCount) this.messageKindRegistry.delete(key);
|
|
262
255
|
}
|
|
263
|
-
this.userReceiptIndices.purgeFrom(postUndoCount);
|
|
264
256
|
this.markDirty();
|
|
265
257
|
}
|
|
266
258
|
return result;
|
|
@@ -295,8 +287,13 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
295
287
|
this.markDirty();
|
|
296
288
|
// Record the line where the streaming block starts so updates can be incremental.
|
|
297
289
|
// Reset the render throttle so the first delta always renders immediately.
|
|
290
|
+
// The rebuild already treats the placeholder as streaming (its turn ends
|
|
291
|
+
// without a blank row, the text continues under its spine), the same as a
|
|
292
|
+
// rebuild later in the stream does.
|
|
293
|
+
this.streamingStartLine = 0;
|
|
298
294
|
this.flushHistory();
|
|
299
|
-
|
|
295
|
+
// The streamed text starts above any display-only draws kept below it.
|
|
296
|
+
this.streamingStartLine = this.history.getLineCount() - this.displayOnly.trailingLineCount;
|
|
300
297
|
this._streamWidth = this._getWidth();
|
|
301
298
|
this._lastStreamRenderMs = 0;
|
|
302
299
|
}
|
|
@@ -324,8 +321,8 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
324
321
|
this._lastStreamRenderMs = now;
|
|
325
322
|
const width = this._getWidth();
|
|
326
323
|
this.history.truncateToLine(this.streamingStartLine);
|
|
327
|
-
|
|
328
|
-
this.history.
|
|
324
|
+
this.history.addLines(this.streamingLines(content, width));
|
|
325
|
+
this.displayOnly.drawTrailing(width, () => this.history.getLineCount());
|
|
329
326
|
}
|
|
330
327
|
}
|
|
331
328
|
}
|
|
@@ -337,6 +334,7 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
337
334
|
public override finalizeStreamingBlock(): void {
|
|
338
335
|
super.finalizeStreamingBlock();
|
|
339
336
|
this.streamingStartLine = -1;
|
|
337
|
+
this.displayOnly.resetTrailing();
|
|
340
338
|
this._streamWidth = -1;
|
|
341
339
|
this._lastStreamRenderMs = 0;
|
|
342
340
|
this.markDirty();
|
|
@@ -362,18 +360,13 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
362
360
|
this.messageLineRegistry = [];
|
|
363
361
|
this.errorLineRegistry = [];
|
|
364
362
|
this.messageKindRegistry = new Map();
|
|
365
|
-
this.userReceiptIndices.clear();
|
|
366
363
|
this.streamingStartLine = -1;
|
|
364
|
+
this.displayOnly.drop();
|
|
365
|
+
this.workTree.reset();
|
|
367
366
|
this._displayFromMessageIndex = 0; // full reset, show everything on next render
|
|
368
367
|
}
|
|
369
368
|
|
|
370
|
-
/**
|
|
371
|
-
* replaceMessagesForLLM - Replace the conversation's LLM-visible messages with a new set.
|
|
372
|
-
* Used by small-window compaction to swap in truncated messages without an LLM call.
|
|
373
|
-
* System messages are always preserved at the front.
|
|
374
|
-
*
|
|
375
|
-
* @param newMessages - Replacement ProviderMessage array (user/assistant/tool roles only)
|
|
376
|
-
*/
|
|
369
|
+
/** Compaction's replace: the SDK keeps every kept message whole (tool calls, model, reasoning); the display rebuilds. */
|
|
377
370
|
public override replaceMessagesForLLM(newMessages: ProviderMessage[]): void {
|
|
378
371
|
super.replaceMessagesForLLM(newMessages);
|
|
379
372
|
this.history.clear();
|
|
@@ -415,6 +408,10 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
415
408
|
titleSource?: import('@pellux/goodvibes-sdk/platform/core').ConversationTitleSource;
|
|
416
409
|
}): void {
|
|
417
410
|
super.fromJSON(data);
|
|
411
|
+
// A restored session's notices go back into the history (not toasted).
|
|
412
|
+
for (const message of data.messages) if (message.role === 'system') this.noticeSink?.(message.content, { restored: true });
|
|
413
|
+
// Output printed over the conversation this one replaces is not about it.
|
|
414
|
+
this.displayOnly.drop();
|
|
418
415
|
this.history.clear();
|
|
419
416
|
this.lineCache.clear();
|
|
420
417
|
this.appendedUpTo = 0;
|
|
@@ -466,6 +463,8 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
466
463
|
const width = this._getWidth();
|
|
467
464
|
this.lastRenderedWidth = width;
|
|
468
465
|
this.dirty = false;
|
|
466
|
+
this.builtWhileActive = this.workTreeSources.turnActive?.() ?? false;
|
|
467
|
+
this.displayOnly.resetTrailing();
|
|
469
468
|
|
|
470
469
|
const snapshot = this.getMessageSnapshot();
|
|
471
470
|
// During streaming, the in-progress placeholder (always the last message) is
|
|
@@ -484,14 +483,9 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
484
483
|
const displayStart = this._displayFromMessageIndex;
|
|
485
484
|
const visibleSnapshot = displayStart > 0 ? renderSnapshot.slice(displayStart) : renderSnapshot;
|
|
486
485
|
|
|
487
|
-
// Tool
|
|
488
|
-
//
|
|
489
|
-
|
|
490
|
-
const displayMessages = visibleSnapshot.filter((m, i) => {
|
|
491
|
-
if (m.role === 'tool') return false;
|
|
492
|
-
if (m.role === 'system') return this.userReceiptIndices.has(displayStart + i);
|
|
493
|
-
return true;
|
|
494
|
-
});
|
|
486
|
+
// Tool and system messages are not transcript content: a system message is
|
|
487
|
+
// a notice (a toast and the notification history, see core/notices.ts).
|
|
488
|
+
const displayMessages = visibleSnapshot.filter((m) => m.role !== 'tool' && m.role !== 'system');
|
|
495
489
|
|
|
496
490
|
if (displayMessages.length === 0 && displayStart === 0 && !this.suppressSplash && !this.splashGate.dismissed) {
|
|
497
491
|
this.addSplashScreen(width);
|
|
@@ -504,8 +498,14 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
504
498
|
// renderSnapshot) is left uncached: its content mutates in place per delta and
|
|
505
499
|
// the incremental streaming path (updateStreamingBlock) owns it.
|
|
506
500
|
const streamingPlaceholderAbsIdx = isStreaming ? snapshot.length - 1 : -1;
|
|
507
|
-
|
|
501
|
+
// Display-only draws go back where they were printed: after the unit that
|
|
502
|
+
// holds the last message that existed then. The turn still streaming is
|
|
503
|
+
// last and its text continues below it, so what follows it waits.
|
|
504
|
+
const kept = this.displayOnly.cursor(width);
|
|
505
|
+
kept.through(displayStart - 1);
|
|
506
|
+
this.appendMessages(visibleSnapshot, width, displayStart, streamingPlaceholderAbsIdx, (last, streaming) => { if (!streaming) kept.through(last); });
|
|
508
507
|
this.appendedUpTo = snapshot.length;
|
|
508
|
+
if (!isStreaming) kept.through(Number.POSITIVE_INFINITY);
|
|
509
509
|
|
|
510
510
|
if (isStreaming) {
|
|
511
511
|
// Re-anchor the streaming block to the freshly rebuilt buffer and re-render
|
|
@@ -515,17 +515,31 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
515
515
|
this._lastStreamRenderMs = 0;
|
|
516
516
|
const streamingContent = lastMsg?.content;
|
|
517
517
|
if (typeof streamingContent === 'string' && streamingContent.length > 0) {
|
|
518
|
-
this.history.addLines(
|
|
518
|
+
this.history.addLines(this.streamingLines(streamingContent, width));
|
|
519
519
|
}
|
|
520
|
+
kept.trailStreamed(() => this.history.getLineCount()); // printed this turn: below the streamed text
|
|
520
521
|
}
|
|
521
522
|
}
|
|
522
523
|
|
|
524
|
+
/** Keep a display-only draw and draw it now (below streamed text while streaming; over the splash, it retires the splash). */
|
|
525
|
+
private recordDisplayOnly(draw: (width: number) => void): void {
|
|
526
|
+
const seq = this.displayOnly.keep(this.getMessageSnapshot().length, draw);
|
|
527
|
+
if (this.splashGate.showing && this.splashGate.dismiss()) return this.rebuildHistory();
|
|
528
|
+
const before = this.history.getLineCount();
|
|
529
|
+
draw(this._getWidth());
|
|
530
|
+
this.displayOnly.noteDrawn(seq, this.history.getLineCount() - before, this.streamingStartLine >= 0);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/** Display-only draws kept for redraw (bounded; for memory-hygiene assertions). */
|
|
534
|
+
public getDisplayOnlyCount(): number { return this.displayOnly.count; }
|
|
535
|
+
|
|
523
536
|
/**
|
|
524
537
|
* flushHistory - Incremental update. Appends only newly added messages.
|
|
525
538
|
* Falls back to a full rebuild when the terminal width has changed.
|
|
526
539
|
*/
|
|
527
540
|
public flushHistory(): void {
|
|
528
541
|
const currentWidth = this._getWidth();
|
|
542
|
+
if ((this.workTreeSources.turnActive?.() ?? false) !== this.builtWhileActive) this.dirty = true;
|
|
529
543
|
if (!this.dirty && currentWidth === this.lastRenderedWidth) return;
|
|
530
544
|
this.rebuildHistory();
|
|
531
545
|
}
|
|
@@ -543,29 +557,24 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
543
557
|
messageKindRegistry: this.messageKindRegistry as ReadonlyMap<number, SystemMessageKind>,
|
|
544
558
|
configManager: this._configManager,
|
|
545
559
|
splashOptions: this.splashOptions,
|
|
560
|
+
systemNotices: 'elsewhere' as const,
|
|
561
|
+
workTreeSources: this.workTreeSources,
|
|
562
|
+
treeGlyphSet: this.treeGlyphSet(),
|
|
563
|
+
focusId: this.workTree.focus,
|
|
564
|
+
frame: this.workTree.frame,
|
|
546
565
|
};
|
|
547
566
|
}
|
|
548
567
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
private renderAssistantMessage(
|
|
554
|
-
message: Extract<Message, { role: 'assistant' }>,
|
|
555
|
-
width: number,
|
|
556
|
-
lineNumberMode: 'all' | 'code' | 'off',
|
|
557
|
-
collapseThreshold: number,
|
|
558
|
-
msgIdx: number,
|
|
559
|
-
): void {
|
|
560
|
-
renderConversationAssistantMessage(this.renderingContext(), message, width, lineNumberMode, collapseThreshold, msgIdx);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
private renderSystemMessage(message: Extract<Message, { role: 'system' }>, width: number, msgIdx: number): void {
|
|
564
|
-
renderConversationSystemMessage(this.renderingContext(), message, width, msgIdx);
|
|
568
|
+
/** The work tree's glyph set: display.treeGlyphs, ascii on a terminal without unicode. */
|
|
569
|
+
private treeGlyphSet(): TreeGlyphSetName {
|
|
570
|
+
return resolveTreeGlyphSet(this._configManager?.get('display.treeGlyphs'), this.unicodeCapable);
|
|
565
571
|
}
|
|
566
572
|
|
|
567
|
-
|
|
568
|
-
|
|
573
|
+
/** Streaming text: under the spine of the turn it belongs to when there is one, else full width. */
|
|
574
|
+
private streamingLines(content: string, width: number): Line[] {
|
|
575
|
+
const tail = this.lineCache.lastTail;
|
|
576
|
+
if (!tail) return renderMarkdown(content, width, { isStreaming: true });
|
|
577
|
+
return renderStreamingContinuation(content, width, tail, this.treeGlyphSet());
|
|
569
578
|
}
|
|
570
579
|
|
|
571
580
|
/**
|
|
@@ -581,6 +590,7 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
581
590
|
width: number,
|
|
582
591
|
msgIndexOffset = 0,
|
|
583
592
|
streamingPlaceholderAbsIdx = -1,
|
|
593
|
+
afterUnit?: (lastIndex: number, streaming: boolean) => void,
|
|
584
594
|
): void {
|
|
585
595
|
this.lineCache.renderInto(
|
|
586
596
|
this.renderingContext(),
|
|
@@ -589,34 +599,35 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
589
599
|
this.messageLineRegistry,
|
|
590
600
|
msgIndexOffset,
|
|
591
601
|
streamingPlaceholderAbsIdx,
|
|
602
|
+
afterUnit,
|
|
592
603
|
);
|
|
593
604
|
}
|
|
594
605
|
|
|
606
|
+
// -------------------------------------------------------------------------
|
|
607
|
+
// Work tree (see work-tree-model.ts / work-tree-focus.ts)
|
|
608
|
+
// -------------------------------------------------------------------------
|
|
609
|
+
|
|
610
|
+
/** Live facts the work tree draws: timings, agent lanes, the call a prompt is holding. */
|
|
611
|
+
public setWorkTreeSources(sources: WorkTreeSources): void { this.workTreeSources = sources; this.markDirty(); }
|
|
612
|
+
// An agent view (core/agent-view-render.ts) draws with main's sources, glyphs and the agent's lane color.
|
|
613
|
+
public getWorkTreeSources(): WorkTreeSources { return this.workTreeSources; }
|
|
614
|
+
public getTreeGlyphSet(): TreeGlyphSetName { return this.treeGlyphSet(); }
|
|
615
|
+
public laneColorOf(agentId: string): number | undefined { return this.getBlockRegistry().find((b) => b.workTree?.kind === 'lane' && b.workTree.agentId === agentId)?.workTree?.colorIndex; }
|
|
616
|
+
/** Override the unicode probe (tests, and a terminal the caller knows better). */
|
|
617
|
+
public setUnicodeCapable(capable: boolean): void { this.unicodeCapable = capable; this.markDirty(); }
|
|
595
618
|
/** Find the nearest block to a given line index, optionally filtered by type. */
|
|
596
|
-
public findNearestBlock(lineIndex: number, typeFilter?: string): BlockMeta | null {
|
|
597
|
-
let nearest: BlockMeta | null = null;
|
|
598
|
-
let nearestDist = Infinity;
|
|
599
|
-
for (const block of this.blockRegistry) {
|
|
600
|
-
if (typeFilter !== undefined && block.type !== typeFilter) continue;
|
|
601
|
-
if (lineIndex >= block.startLine && lineIndex < block.startLine + block.lineCount) {
|
|
602
|
-
return block;
|
|
603
|
-
}
|
|
604
|
-
const dist = Math.abs(block.startLine - lineIndex);
|
|
605
|
-
if (dist < nearestDist) {
|
|
606
|
-
nearestDist = dist;
|
|
607
|
-
nearest = block;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
return nearest;
|
|
611
|
-
}
|
|
619
|
+
public findNearestBlock(lineIndex: number, typeFilter?: string): BlockMeta | null { return nearestBlock(this.blockRegistry, lineIndex, typeFilter); }
|
|
612
620
|
|
|
613
|
-
/**
|
|
614
|
-
* isCollapsed - Returns whether the block at blockIndex is collapsed.
|
|
615
|
-
*/
|
|
621
|
+
/** isCollapsed - Returns whether the block at blockIndex is collapsed. */
|
|
616
622
|
public isCollapsed(blockIndex: number): boolean {
|
|
617
623
|
const block = this.blockRegistry[blockIndex];
|
|
618
|
-
|
|
619
|
-
|
|
624
|
+
return block ? this.collapsedNow(block) : false;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** Current collapse of a block: an unset bead key is a closed bead; everything else defaults open. */
|
|
628
|
+
private collapsedNow(block: BlockMeta): boolean {
|
|
629
|
+
const stored = this.collapseState.get(block.collapseKey);
|
|
630
|
+
return block.workTree?.kind === 'bead' ? stored !== false : stored ?? false;
|
|
620
631
|
}
|
|
621
632
|
|
|
622
633
|
/**
|
|
@@ -634,11 +645,7 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
634
645
|
public getDiffAtLine(lineIndex: number): { filePath: string; original: string; updated: string } | null {
|
|
635
646
|
const nearest = this.findNearestBlock(lineIndex, 'diff');
|
|
636
647
|
if (!nearest || !nearest.filePath) return null;
|
|
637
|
-
return {
|
|
638
|
-
filePath: nearest.filePath,
|
|
639
|
-
original: nearest.diffOriginal ?? '',
|
|
640
|
-
updated: nearest.diffUpdated ?? '',
|
|
641
|
-
};
|
|
648
|
+
return { filePath: nearest.filePath, original: nearest.diffOriginal ?? '', updated: nearest.diffUpdated ?? '' };
|
|
642
649
|
}
|
|
643
650
|
|
|
644
651
|
/**
|
|
@@ -648,10 +655,11 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
648
655
|
public toggleCollapseAtLine(lineIndex: number): number {
|
|
649
656
|
const nearest = this.findNearestBlock(lineIndex);
|
|
650
657
|
if (!nearest) return -1;
|
|
651
|
-
const current = this.
|
|
658
|
+
const current = this.collapsedNow(nearest);
|
|
652
659
|
this.collapseState.set(nearest.collapseKey, !current);
|
|
653
660
|
this.searchExpansion.noteUserTouch(nearest.collapseKey);
|
|
654
661
|
this.markDirty();
|
|
662
|
+
if (isWorkTreeFoldKey(nearest.collapseKey)) this.workTree.notifyFoldChange();
|
|
655
663
|
return nearest.blockIndex;
|
|
656
664
|
}
|
|
657
665
|
|
|
@@ -692,23 +700,13 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
692
700
|
* nextErrorLine - Find the next error line after currentLine (wraps around).
|
|
693
701
|
* Returns -1 if there are no error lines.
|
|
694
702
|
*/
|
|
695
|
-
public nextErrorLine(currentLine: number): number {
|
|
696
|
-
const errors = this.getErrorLines();
|
|
697
|
-
if (errors.length === 0) return -1;
|
|
698
|
-
const after = errors.find(l => l > currentLine);
|
|
699
|
-
return after ?? errors[0];
|
|
700
|
-
}
|
|
703
|
+
public nextErrorLine(currentLine: number): number { return wrapToLine(this.getErrorLines(), currentLine, 'next'); }
|
|
701
704
|
|
|
702
705
|
/**
|
|
703
706
|
* prevErrorLine - Find the previous error line before currentLine (wraps around).
|
|
704
707
|
* Returns -1 if there are no error lines.
|
|
705
708
|
*/
|
|
706
|
-
public prevErrorLine(currentLine: number): number {
|
|
707
|
-
const errors = this.getErrorLines();
|
|
708
|
-
if (errors.length === 0) return -1;
|
|
709
|
-
const before = [...errors].reverse().find(l => l < currentLine);
|
|
710
|
-
return before ?? errors[errors.length - 1];
|
|
711
|
-
}
|
|
709
|
+
public prevErrorLine(currentLine: number): number { return wrapToLine(this.getErrorLines(), currentLine, 'prev'); }
|
|
712
710
|
|
|
713
711
|
public nextTranscriptEventLine(currentLine: number, kind: TranscriptEventKind | 'all' = 'all'): number {
|
|
714
712
|
this.flushHistory();
|
|
@@ -727,14 +725,13 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
727
725
|
}
|
|
728
726
|
|
|
729
727
|
/** Retire the splash for the run, any submission does it (SplashGateState). */
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
}
|
|
728
|
+
/** Retire the splash; rebuilt now (not marked dirty) so a command's output logged in the same tick survives the next frame. */
|
|
729
|
+
public dismissSplash(): void { if (this.splashGate.dismiss() && this.splashGate.showing) this.rebuildHistory(); }
|
|
733
730
|
|
|
731
|
+
/** True while the conversation area is showing the splash (the shell centers it). */
|
|
732
|
+
public isSplashShowing(): boolean { return this.splashGate.showing; }
|
|
734
733
|
/** True once, on the frame the splash gives way to transcript content. */
|
|
735
|
-
public consumeSplashTransition(): boolean {
|
|
736
|
-
return this.splashGate.consumeTransition();
|
|
737
|
-
}
|
|
734
|
+
public consumeSplashTransition(): boolean { return this.splashGate.consumeTransition(); }
|
|
738
735
|
|
|
739
736
|
private addSplashScreen(width: number): void {
|
|
740
737
|
addConversationSplashScreen(this.renderingContext(), width);
|
|
@@ -744,8 +741,9 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
744
741
|
return conversationTextToLines(text, width, style);
|
|
745
742
|
}
|
|
746
743
|
|
|
744
|
+
/** Print display-only text into the transcript (never a message); it survives every rebuild. */
|
|
747
745
|
public log(text: string, style: Partial<Cell> = {}, indent = ' '): void {
|
|
748
|
-
|
|
746
|
+
this.recordDisplayOnly((width) => logConversationText(this.renderingContext(), width, text, style, indent));
|
|
749
747
|
}
|
|
750
748
|
|
|
751
749
|
/**
|
|
@@ -761,7 +759,7 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
761
759
|
durationMs: number,
|
|
762
760
|
errorMsg?: string,
|
|
763
761
|
): void {
|
|
764
|
-
|
|
762
|
+
this.recordDisplayOnly((width) => logConversationToolResult(this.renderingContext(), width, toolCall, status, resultSummary, durationMs, errorMsg));
|
|
765
763
|
this.markDirty();
|
|
766
764
|
}
|
|
767
765
|
|
|
@@ -778,6 +776,7 @@ export class ConversationManager extends SdkConversationManager {
|
|
|
778
776
|
*/
|
|
779
777
|
public clearDisplay(): void {
|
|
780
778
|
this.history.clear();
|
|
779
|
+
this.displayOnly.drop();
|
|
781
780
|
this.blockRegistry = [];
|
|
782
781
|
this.messageLineRegistry = [];
|
|
783
782
|
this.errorLineRegistry = [];
|