@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
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,64 @@ All notable changes to GoodVibes TUI.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [2.1.0] - 2026-09-30
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Themes.** display.theme picks goodvibes (the default), goodvibes-neon,
|
|
12
|
+
catppuccin, tokyonight, dracula, nord, gruvbox, one-dark, rosepine,
|
|
13
|
+
solarized, github, or system, which follows the terminal's own palette,
|
|
14
|
+
under tmux and over ssh too. Settings previews themes live. A theme you
|
|
15
|
+
already chose is kept.
|
|
16
|
+
- **Modals replace panes.** Usage, Changes, Agents and Notifications open as
|
|
17
|
+
large dimmed modals on one surface kit with a live search row and one-level
|
|
18
|
+
Esc; ctrl+p opens the command palette.
|
|
19
|
+
- **A calmer base screen.** A one-row header, an empty row under it,
|
|
20
|
+
full-width user messages, an input area that holds only input, and a status
|
|
21
|
+
line with the context bar and its compaction tick.
|
|
22
|
+
- **The throbber is back** on its own row above the input area, naming what
|
|
23
|
+
the session is doing with elapsed time, with half-row gaps between output,
|
|
24
|
+
throbber, input area and status line.
|
|
25
|
+
- **A lane-graph work tree** with status on each bead, agents as lanes,
|
|
26
|
+
folding, and a display.treeGlyphs choice of rounded, square or ascii.
|
|
27
|
+
- **Full-screen agent and process views** with session chips. Esc never
|
|
28
|
+
stops them and ctrl+x asks before stopping.
|
|
29
|
+
- **A back-to-bottom pill** shows while the output is scrolled back. Esc
|
|
30
|
+
returns to the live output and never interrupts the turn.
|
|
31
|
+
- **Notifications name the work** on the desktop, in the terminal, on
|
|
32
|
+
webhooks and for approvals, budgets, agents and workstreams. The
|
|
33
|
+
behavior.notificationsMetadataOnly setting sends metadata only and is off
|
|
34
|
+
by default.
|
|
35
|
+
- **System notices are toasts** kept in a /notifications history with plain
|
|
36
|
+
names, one entry per event.
|
|
37
|
+
- /context window and /status say where the context window came from.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- **WRFC chains run in their own git worktree.** A passed chain commits only
|
|
42
|
+
its own changes plus edits GoodVibes' own tools made, runs your git hooks,
|
|
43
|
+
and leaves your other uncommitted edits in place. A failed chain stops its
|
|
44
|
+
remaining fix tasks.
|
|
45
|
+
- A remote model with no real window takes it from the models.dev catalog,
|
|
46
|
+
so abacusai route-llm reads 128.0k instead of a guessed 8.2k.
|
|
47
|
+
- The pins move to sdk 2.1.0 and daemon 1.29.0.
|
|
48
|
+
- Every open dependency advisory is closed: undici, brace-expansion, the
|
|
49
|
+
Anthropic client (0.92), qs, body-parser, ip-address, fflate, fast-uri,
|
|
50
|
+
js-yaml and sharp move to patched versions.
|
|
51
|
+
|
|
52
|
+
### Fixed
|
|
53
|
+
|
|
54
|
+
- Read and command bodies keep their indentation; wrapped prose no longer
|
|
55
|
+
starts one column right; long code lines wrap.
|
|
56
|
+
- A finished turn stops saying working; ended processes stop counting as
|
|
57
|
+
running.
|
|
58
|
+
- The recovery offer opens before the first frame and never takes keys typed
|
|
59
|
+
before it appeared.
|
|
60
|
+
- Instructions meant for the model no longer show as notices, and notices
|
|
61
|
+
keep their full text.
|
|
62
|
+
- Printed command output survives resizes.
|
|
63
|
+
|
|
64
|
+
|
|
7
65
|
## [2.0.21] - 2026-08-23
|
|
8
66
|
|
|
9
67
|
### Changes
|
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://github.com/mgd34msu/goodvibes-tui)
|
|
6
6
|
|
|
7
7
|
GoodVibes is a terminal console for coding and operations work with an AI model. You run `goodvibes` in a project directory and get a full-screen terminal app where you talk to a model that can read and edit your files, run shell commands, search the web, and hand work off to background agents, asking your permission before anything that writes or executes.
|
|
8
8
|
|
|
9
|
-
It talks to many model providers (OpenAI, Anthropic, Gemini, Bedrock, Copilot, OpenRouter and other OpenAI-compatible gateways, plus local servers like Ollama and LM Studio that `/scan` finds on your machine and LAN), keeps its settings, sessions, and secrets on your own machine, and shows you the token count and running cost of every turn.
|
|
9
|
+
It talks to many model providers (OpenAI, Anthropic, Gemini, Bedrock, Copilot, OpenRouter and other OpenAI-compatible gateways, plus local servers like Ollama and LM Studio that `/scan` finds on your machine and LAN), keeps its settings, sessions, and secrets on your own machine, and shows you the token count and running cost of every turn. Background work opens in large modals over the conversation: running agents, changed files and diffs, tokens and cost.
|
|
10
10
|
|
|
11
11
|
<img src="docs/assets/splash.png" alt="GoodVibes starting up in a terminal: a glitch-art GOODVIBES wordmark in cyan-to-magenta gradient, the version and tagline beneath it, the active model and tool count, the working directory, a line offering to resume the last session, and a hint line reading Ctrl+P panels / ? help / F2 fleet. A status footer shows mode, token usage, context usage, and the daemon and web listener addresses." width="900">
|
|
12
12
|
|
|
@@ -80,11 +80,11 @@ Models come from a live catalog, so the picker lists far more than a hardcoded s
|
|
|
80
80
|
|
|
81
81
|
The `synthetic` provider groups the same model across every backend that serves it into one entry. Pick it and requests route to whichever backend is healthy, failing over on rate limits and transient errors without changing the model you chose, and without ever crossing the free, paid, and subscription boundaries. Any OpenAI-compatible API can be added as a custom provider by dropping a JSON file in `~/.goodvibes/tui/providers/`. It is hot-reloaded. See [docs/providers-and-routing.md](docs/providers-and-routing.md).
|
|
82
82
|
|
|
83
|
-
### The
|
|
83
|
+
### The Agents control room
|
|
84
84
|
|
|
85
85
|
<img src="docs/assets/fleet-panel.png" alt="The Fleet panel open beside the conversation. It lists background work as rows with a kind, a name, elapsed time, token count, and cost: a CI watch poller and a runtime heartbeat watcher at 4 minutes 23 seconds each, and an idle code indexer. The detail region below shows the selected watcher's id, state idle, elapsed time, model, tokens, cost, a headline reading CI watch poller, healthy, an activity phase, and an approvals line. The action hints read j/k navigate, Enter attach, K kill, A archive finished, v archived, n host agent." width="900">
|
|
86
86
|
|
|
87
|
-
Work that is not conversation
|
|
87
|
+
Work that is not conversation opens in modals instead of scrolling past in the transcript. Agents, reached with `F2`, is the live control room for agents, WRFC chains, workstreams, watchers, scheduled jobs and hosted sessions: what is running, for how long, at what token cost, and what it is waiting on. You can open a running agent to watch its live tail or steer it, back out with `Esc` without stopping it, stop it with `Ctrl+X` (it asks first), pause and resume, and archive finished work. Usage (`/usage`) shows context pressure, tokens and cost; Changes (`/changes`) shows changed files with a tinted diff, a semantic summary, hunk staging and review comments; Notifications (`/notifications`) keeps the history. The screenshot above predates the modal redesign.
|
|
88
88
|
|
|
89
89
|
### Everything else is a command
|
|
90
90
|
|
|
@@ -99,14 +99,14 @@ Press `?` for a searchable, categorized list of every slash command with its arg
|
|
|
99
99
|
| `Enter` / `Shift+Enter` | Send the message / insert a newline |
|
|
100
100
|
| `?` | Help and command picker (on an empty prompt) |
|
|
101
101
|
| `@` | File picker: insert a path into the prompt |
|
|
102
|
-
| `Tab` |
|
|
102
|
+
| `Tab` | Complete a path, or toggle collapse on the nearest block. With the session chips showing and an empty composer, switch to the next session |
|
|
103
103
|
| `Ctrl+F` | Search the conversation |
|
|
104
104
|
| `Ctrl+Y` / `Ctrl+S` | Copy / save the nearest block |
|
|
105
|
-
| `Ctrl+P` |
|
|
106
|
-
| `F2`
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
105
|
+
| `Ctrl+P` / `Ctrl+K` | Command palette: search and run any command, view or setting |
|
|
106
|
+
| `F2` / `Ctrl+O` | Open or close Agents |
|
|
107
|
+
| `Shift+Tab` | Cycle the session permission mode. With the session chips showing and an empty composer, switch to the previous session |
|
|
108
|
+
| `Esc` | Close the top modal (one level), clear the composer, leave an agent or process view (never stops it), or interrupt main's turn |
|
|
109
|
+
| `Ctrl+X` | Inside an agent or process view: stop it (press twice to confirm) |
|
|
110
110
|
| `Ctrl+C` | Clear input, cancel a running turn. Press twice to quit |
|
|
111
111
|
|
|
112
112
|
Most bindings are customizable in `~/.goodvibes/tui/keybindings.json`, and `/keybindings` shows what is currently bound. Five keys are fixed and stay out of that file: `F2` (Fleet), `Shift+Tab` (permission-mode cycle), `Esc` (leave the current mode), `?` (help), and `@` (file picker). The full reference is in [docs/tools-and-commands.md](docs/tools-and-commands.md).
|
|
@@ -129,7 +129,7 @@ Each row links to the page that documents it. The product's own `?` overlay and
|
|
|
129
129
|
| CLI flags | Session lifecycle (`--continue`, `--resume`, `--fork`), non-interactive mode, output formats, host selection | [cli-flags.md](docs/cli-flags.md) |
|
|
130
130
|
| Knowledge and memory | Session and durable memory, a structured knowledge store with connectors and extractors, embeddings and retrieval, artifacts, multimodal analysis | [knowledge-artifacts-and-multimodal.md](docs/knowledge-artifacts-and-multimodal.md) |
|
|
131
131
|
| Session durability | Post-turn snapshots plus an fsync-per-record transcript journal replayed at every resume | [session-durability.md](docs/session-durability.md) |
|
|
132
|
-
| Planning | Conversational planning loop, project-scoped knowledge spaces, readiness evaluation, the Planning
|
|
132
|
+
| Planning | Conversational planning loop, project-scoped knowledge spaces, readiness evaluation, the Planning modal | [project-planning.md](docs/project-planning.md) |
|
|
133
133
|
| Sharing and export | `/share` to HTML, JSON, or Markdown with redaction, upload, and clipboard options | [share-command.md](docs/share-command.md) |
|
|
134
134
|
| Daemon and services | Connecting to the standalone GoodVibes daemon, browser operator surface, background service and autostart, inbound TLS, outbound trust | [deployment-and-services.md](docs/deployment-and-services.md) |
|
|
135
135
|
| Remote access | A worked home-server setup: always-on daemon, browser access, TUI over SSH, reachability and TLS | [remote-access.md](docs/remote-access.md) |
|
|
@@ -137,7 +137,7 @@ Each row links to the page that documents it. The product's own `?` overlay and
|
|
|
137
137
|
| Voice | Live `/tts` playback, TTS and STT providers, streaming voice API | [voice-and-live-tts.md](docs/voice-and-live-tts.md) |
|
|
138
138
|
| Sandboxing | Bounded eval and isolated MCP execution, with a QEMU-backed VM path | [qemu-sandbox.md](docs/qemu-sandbox.md) |
|
|
139
139
|
| Integrations | Home Assistant surface, Cloudflare Workers/Queues batch, GitHub Action | [homeassistant-surface.md](docs/homeassistant-surface.md) · [cloudflare-batch.md](docs/cloudflare-batch.md) · [github-action.md](docs/github-action.md) |
|
|
140
|
-
| Contributing surfaces |
|
|
140
|
+
| Contributing surfaces | The checked-in operator/peer contracts and knowledge schemas | [foundation-artifacts](docs/foundation-artifacts/README.md) |
|
|
141
141
|
|
|
142
142
|
Full index: [docs/README.md](docs/README.md).
|
|
143
143
|
|
|
@@ -218,12 +218,12 @@ bun run dev
|
|
|
218
218
|
|
|
219
219
|
The compiled binary is the TUI entrypoint. With `daemon.enabled` on (the default) it adopts a running standalone GoodVibes daemon over loopback, and when a daemon is installed as a service but stopped, it starts that service once and waits for it to come online. It never embeds or constructs a daemon of its own. The control plane, HTTP listener (`danger.httpListener`), and web surface are all hosted by the daemon; the TUI configures them and reports their bindings.
|
|
220
220
|
|
|
221
|
-
Tests live under `src/test/`, mirroring the source tree, and cover contract, security, release-gate, runtime, renderer,
|
|
221
|
+
Tests live under `src/test/`, mirroring the source tree, and cover contract, security, release-gate, runtime, renderer, view, integration, and anti-regression cases. Several gates run alongside them in CI: byte-exact golden renderer frames, performance budgets for startup and frame composition and line production (`scripts/perf-baseline.json`), and architecture rules for import cycles, layer boundaries, source-file size, and unused renderer exports (`scripts/check-architecture.ts`).
|
|
222
222
|
|
|
223
223
|
Some decisions worth knowing before you read the source:
|
|
224
224
|
|
|
225
225
|
- **Bun runtime.** Native TypeScript execution, fast startup, built-in test runner.
|
|
226
|
-
- **Raw ANSI renderer.** It writes the UI straight to the alternate screen buffer, giving direct control over every byte sent to the terminal. Conversation,
|
|
226
|
+
- **Raw ANSI renderer.** It writes the UI straight to the alternate screen buffer, giving direct control over every byte sent to the terminal. Conversation, modals, overlays, and the footer all share that one renderer.
|
|
227
227
|
- **In-process agents.** Agents run in the same process rather than over IPC, staying isolated through scoped tool registries and namespaced state.
|
|
228
228
|
- **Typed runtime store.** A plain `zustand/vanilla` store with typed selectors and dispatch paths, reachable from agents, tools, renderer, hooks, channels, and daemon surfaces alike.
|
|
229
229
|
- **Tree-sitter and bundled language servers.** Grammars for structural analysis, outlines, and AST-level edits, several embedded as WASM for instant startup. TypeScript, Python, Bash, CSS, HTML, and JSON language servers ship as dependencies, while `rust-analyzer` and `gopls` are fetched on first use with checksum verification.
|
|
@@ -233,13 +233,13 @@ Some decisions worth knowing before you read the source:
|
|
|
233
233
|
|
|
234
234
|
The TUI consumes the published `@pellux/goodvibes-sdk` platform layer for shared contracts, daemon routes, and transports, and keeps the terminal UI, host wiring, and product composition here. The dependency is pinned in `package.json`. Reference consumers of those surfaces live under [`examples/`](examples/reference-operator-client/README.md).
|
|
235
235
|
|
|
236
|
-
Source layout, in brief:
|
|
236
|
+
Source layout, in brief (the layer rules and the operator API bridge are in [docs/architecture.md](docs/architecture.md)):
|
|
237
237
|
|
|
238
238
|
```text
|
|
239
239
|
src/
|
|
240
240
|
├── main.ts, core/ terminal entrypoint, orchestrator, conversation and transcript state
|
|
241
241
|
├── renderer/ raw ANSI compositor, overlays, modals, fullscreen workspaces
|
|
242
|
-
├──
|
|
242
|
+
├── views/ modal surfaces, the fleet read model and acts behind Agents, view wiring
|
|
243
243
|
├── input/ slash commands, keybindings, composer, pickers, settings modals
|
|
244
244
|
├── runtime/ bootstrap wiring, typed store, service composition, session recovery
|
|
245
245
|
├── shell/ shell-level modal openers, blocking input, retry affordances
|
package/docs/README.md
CHANGED
|
@@ -32,7 +32,7 @@ This directory contains the current product documentation for `goodvibes-tui`.
|
|
|
32
32
|
Session memory, durable memory, structured knowledge, connectors, extractors, embeddings, artifacts, and multimodal analysis.
|
|
33
33
|
|
|
34
34
|
- [Project planning](project-planning.md)
|
|
35
|
-
TUI-owned conversational planning loop, passive SDK planning artifacts, project-scoped knowledge spaces, readiness evaluation, and the Planning
|
|
35
|
+
TUI-owned conversational planning loop, passive SDK planning artifacts, project-scoped knowledge spaces, readiness evaluation, and the Planning modal.
|
|
36
36
|
|
|
37
37
|
- [Channels, remote runtime, and API](channels-remote-and-api.md)
|
|
38
38
|
Omnichannel surfaces, reply routing, remote peers, node-host contracts, and the daemon/control-plane HTTP + streaming surfaces.
|
|
@@ -46,6 +46,9 @@ This directory contains the current product documentation for `goodvibes-tui`.
|
|
|
46
46
|
- [CLI flags](cli-flags.md)
|
|
47
47
|
Global flags reference covering session lifecycle (`--continue`, `--resume`, `--fork`), the `-y`/`--non-interactive` commit gate for plugin bundle installs, output format consolidation, the `--host` alias, and all other startup and command-specific flags.
|
|
48
48
|
|
|
49
|
+
- [Architecture](architecture.md)
|
|
50
|
+
Source layout, the import-direction rules `bun run architecture:check` enforces, and the operator API bridge that answers `panels.list` / `panels.open` with the modal views.
|
|
51
|
+
|
|
49
52
|
- [Commands reference](commands-reference.md)
|
|
50
53
|
The generated, categorized list of every slash command, its arguments, and its description. It is the same list `?` renders in the product.
|
|
51
54
|
|
|
@@ -64,9 +67,6 @@ This directory contains the current product documentation for `goodvibes-tui`.
|
|
|
64
67
|
- [Local verification](verification/local-verification.md)
|
|
65
68
|
Inventory coverage, GoodVibes home audits, compiled CLI probes, authenticated daemon probes, and release-oriented local gates.
|
|
66
69
|
|
|
67
|
-
- [Panel authoring](panel-authoring.md)
|
|
68
|
-
Class hierarchy, canonical example, palette convention, rendering utilities, input handling, performance instrumentation, and contract test registration for contributors building new TUI panels.
|
|
69
|
-
|
|
70
70
|
- [Changelog](../CHANGELOG.md)
|
|
71
71
|
Current release history. Older per-version release-note files remain under `docs/releases/` for historical releases, but `CHANGELOG.md` is the canonical current stream.
|
|
72
72
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-tui",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Terminal-native GoodVibes client for coding, operations, knowledge and channel workflows; a pure client of the GoodVibes daemon.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.ts",
|
|
@@ -96,11 +96,11 @@
|
|
|
96
96
|
"@anthropic-ai/bedrock-sdk": "^0.29.1",
|
|
97
97
|
"@anthropic-ai/sdk": "^0.92.0",
|
|
98
98
|
"@ast-grep/napi": "^0.42.0",
|
|
99
|
-
"@pellux/goodvibes-sdk": "2.0
|
|
100
|
-
"@pellux/goodvibes-terminal-shell": "2.0
|
|
99
|
+
"@pellux/goodvibes-sdk": "2.1.0",
|
|
100
|
+
"@pellux/goodvibes-terminal-shell": "2.1.0",
|
|
101
101
|
"bash-language-server": "^5.6.0",
|
|
102
102
|
"fuse.js": "^7.1.0",
|
|
103
|
-
"@pellux/goodvibes-daemon": "1.
|
|
103
|
+
"@pellux/goodvibes-daemon": "1.29.0",
|
|
104
104
|
"graphql": "^16.13.2",
|
|
105
105
|
"jszip": "^3.10.1",
|
|
106
106
|
"node-edge-tts": "^1.2.10",
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
"zustand": "^5.0.12"
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
|
-
"@pellux/goodvibes-toolchain": "2.0
|
|
124
|
+
"@pellux/goodvibes-toolchain": "2.1.0",
|
|
125
125
|
"@types/bun": "^1.3.10",
|
|
126
126
|
"typescript": "^5.9.3"
|
|
127
127
|
},
|
|
@@ -133,6 +133,16 @@
|
|
|
133
133
|
},
|
|
134
134
|
"trustedDependencies": [],
|
|
135
135
|
"overrides": {
|
|
136
|
-
"minimatch": "^10.2.5"
|
|
136
|
+
"minimatch": "^10.2.5",
|
|
137
|
+
"undici": "^7.29.1",
|
|
138
|
+
"brace-expansion": "^5.0.11",
|
|
139
|
+
"@anthropic-ai/sdk": "^0.92.0",
|
|
140
|
+
"qs": "^6.16.0",
|
|
141
|
+
"body-parser": "^1.20.8",
|
|
142
|
+
"ip-address": "^10.7.2",
|
|
143
|
+
"fflate": "^0.8.3",
|
|
144
|
+
"fast-uri": "^3.1.6",
|
|
145
|
+
"js-yaml": "^4.3.2",
|
|
146
|
+
"sharp": "^0.35.4"
|
|
137
147
|
}
|
|
138
148
|
}
|
package/src/cli/management.ts
CHANGED
|
@@ -232,6 +232,8 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
|
|
|
232
232
|
id: model.id,
|
|
233
233
|
displayName: model.displayName,
|
|
234
234
|
contextWindow: services.providerRegistry.getContextWindowForModel(model),
|
|
235
|
+
// False when nothing states the window (a guess, or disproven by a larger accepted request).
|
|
236
|
+
contextWindowKnown: services.providerRegistry.getKnownContextWindowForModel(model) !== null,
|
|
235
237
|
providerConfigured: providerSnapshot?.runtime.auth?.configured ?? true,
|
|
236
238
|
setup,
|
|
237
239
|
};
|
|
@@ -241,7 +243,7 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
|
|
|
241
243
|
` provider: ${model.provider}`,
|
|
242
244
|
` setup: ${setup.setupLabel}`,
|
|
243
245
|
` provider configured: ${yesNo(value.providerConfigured)}`,
|
|
244
|
-
` context: ${value.contextWindow.toLocaleString()}`,
|
|
246
|
+
` context: ${value.contextWindowKnown ? value.contextWindow.toLocaleString() : 'unknown'}`,
|
|
245
247
|
].join('\n'));
|
|
246
248
|
}
|
|
247
249
|
if (subOrFilter === 'use' || subOrFilter === 'set') {
|
|
@@ -316,6 +318,7 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
|
|
|
316
318
|
id: model.id,
|
|
317
319
|
displayName: model.displayName,
|
|
318
320
|
contextWindow: services.providerRegistry.getContextWindowForModel(model),
|
|
321
|
+
contextWindowKnown: services.providerRegistry.getKnownContextWindowForModel(model) !== null,
|
|
319
322
|
current: model.registryKey === current,
|
|
320
323
|
...(synthInfo !== null ? {
|
|
321
324
|
isSynthetic: true,
|
|
@@ -331,7 +334,7 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
|
|
|
331
334
|
const synthLabel = model.isSynthetic
|
|
332
335
|
? ` [synthetic ${model.syntheticConfiguredBackends}/${model.syntheticBackends}p]`
|
|
333
336
|
: '';
|
|
334
|
-
return ` ${model.current ? '*' : ' '} ${model.registryKey.padEnd(42)} setup=${model.setupClass} ctx=${model.contextWindow.toLocaleString()}${synthLabel} ${model.displayName}`;
|
|
337
|
+
return ` ${model.current ? '*' : ' '} ${model.registryKey.padEnd(42)} setup=${model.setupClass} ctx=${model.contextWindowKnown ? model.contextWindow.toLocaleString() : 'unknown'}${synthLabel} ${model.displayName}`;
|
|
335
338
|
}),
|
|
336
339
|
].join('\n'));
|
|
337
340
|
});
|
package/src/cli/tui-startup.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { startOnboardingFastPath } from '../runtime/onboarding/fast-path.ts';
|
|
|
7
7
|
import { checkRecoveryForSession, readLastSessionPointer, type SessionSurface } from '@/runtime/index.ts';
|
|
8
8
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
9
9
|
import { writeFatalLine } from '@pellux/goodvibes-sdk/platform/daemon';
|
|
10
|
-
import { offerRecoverySnapshot } from '../runtime/recovery-prompt.ts';
|
|
10
|
+
import { offerRecoverySnapshot, type RecoveryTypeaheadHooks } from '../runtime/recovery-prompt.ts';
|
|
11
11
|
import { buildRecoveryOfferWiring } from '../runtime/recovery-offer-wiring.ts';
|
|
12
12
|
import type { ConversationManager } from '../core/conversation.ts';
|
|
13
13
|
import type { SessionManager } from '@pellux/goodvibes-sdk/platform/sessions';
|
|
@@ -34,6 +34,8 @@ export interface TuiStartupRecoveryDeps {
|
|
|
34
34
|
readonly conversation: ConversationManager;
|
|
35
35
|
readonly writeLastSessionPointer: (sessionId: string) => void;
|
|
36
36
|
readonly receipt: (line: string) => void;
|
|
37
|
+
/** Holds typeahead out of the offer modal (see RecoveryPromptDeps.typeahead). */
|
|
38
|
+
readonly typeahead?: RecoveryTypeaheadHooks;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/**
|
|
@@ -59,11 +61,12 @@ export interface TuiStartupRecoveryDeps {
|
|
|
59
61
|
* plain store resume, after the follow-up Keep/Remove question the
|
|
60
62
|
* established flow always asks on decline.
|
|
61
63
|
*
|
|
62
|
-
* The modal
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
64
|
+
* The modal opens synchronously, before the shell's first frame, like the
|
|
65
|
+
* general boot offer (startRecoveryOffer in runtime/recovery-prompt.ts): the
|
|
66
|
+
* first frame already carries the question, so the composer is never live
|
|
67
|
+
* underneath it, and keys typed before it was painted are held out of it by
|
|
68
|
+
* the typeahead gate (recovery.typeahead). This runs before the general
|
|
69
|
+
* offer, so it still asks first.
|
|
67
70
|
*/
|
|
68
71
|
async function resumeNamedSessionWithRecoveryCheck(options: {
|
|
69
72
|
readonly sessionId: string;
|
|
@@ -88,39 +91,31 @@ async function resumeNamedSessionWithRecoveryCheck(options: {
|
|
|
88
91
|
return;
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
117
|
-
} finally {
|
|
118
|
-
resolve();
|
|
119
|
-
}
|
|
120
|
-
})();
|
|
121
|
-
}, 0);
|
|
122
|
-
timer.unref?.();
|
|
123
|
-
});
|
|
94
|
+
try {
|
|
95
|
+
const deps = buildRecoveryOfferWiring({
|
|
96
|
+
surface,
|
|
97
|
+
sessionManager: recovery.sessionManager,
|
|
98
|
+
runtime: recovery.runtime,
|
|
99
|
+
conversation: recovery.conversation,
|
|
100
|
+
commandContext,
|
|
101
|
+
writeLastSessionPointer: recovery.writeLastSessionPointer,
|
|
102
|
+
receipt: recovery.receipt,
|
|
103
|
+
render,
|
|
104
|
+
...(recovery.typeahead ? { typeahead: recovery.typeahead } : {}),
|
|
105
|
+
});
|
|
106
|
+
const outcome = await offerRecoverySnapshot({ ...deps, targetSessionId: sessionId });
|
|
107
|
+
if (outcome !== 'resumed') {
|
|
108
|
+
await plainResume();
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
// Best-effort by construction (mirrors offerRecoverySnapshot's own
|
|
112
|
+
// guarantee): a failure here must fall back to the plain resume rather
|
|
113
|
+
// than leave the boot with nothing resumed at all.
|
|
114
|
+
await plainResume().catch(() => {
|
|
115
|
+
// A failing plain resume here is no worse than the pre-existing
|
|
116
|
+
// behavior when session resume itself fails.
|
|
117
|
+
});
|
|
118
|
+
}
|
|
124
119
|
}
|
|
125
120
|
|
|
126
121
|
/**
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-view-render.ts, one agent opened full screen.
|
|
3
|
+
*
|
|
4
|
+
* The agent becomes the session: its own transcript is the spine (drawn by
|
|
5
|
+
* the same lane-graph path as main, conversation-rendering.ts), the agents
|
|
6
|
+
* it spawned branch off it as lanes, and steers you sent that it has not
|
|
7
|
+
* picked up yet sit at the bottom marked as queued. Its lanes are drawn with
|
|
8
|
+
* the color base shifted (lane-graph/paint.ts withLaneColorBase) so its spine
|
|
9
|
+
* keeps the color its lane has in main.
|
|
10
|
+
*
|
|
11
|
+
* row 0 ◆ engineer · the task · started by main 2m 14s ago
|
|
12
|
+
* row 1 (blank)
|
|
13
|
+
* rows the agent's transcript
|
|
14
|
+
*
|
|
15
|
+
* A WRFC chain owner takes no turns of its own: its view is its chain, each
|
|
16
|
+
* phase agent a lane off its spine drawn from that agent's own transcript
|
|
17
|
+
* (work-tree-model.ts buildWrfcOwnerModel).
|
|
18
|
+
*
|
|
19
|
+
* The thin bar down column 0, the blank rows above and below the body, and
|
|
20
|
+
* the scroll window are the caller's (shell/session-views.ts); this returns
|
|
21
|
+
* every line.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { createEmptyLine, type Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
25
|
+
import type { ConversationMessageSnapshot } from '@pellux/goodvibes-sdk/platform/core';
|
|
26
|
+
import { appendConversationMessages, drawConversationTurn, type ConversationRenderContext } from './conversation-rendering.ts';
|
|
27
|
+
import { buildWrfcOwnerModel } from './work-tree-model.ts';
|
|
28
|
+
import type { AgentLaneInfo, WorkTreeSources } from './work-tree-sources.ts';
|
|
29
|
+
import { laneColor, withLaneColorBase } from '../renderer/lane-graph/paint.ts';
|
|
30
|
+
import type { TreeGlyphSetName } from '../renderer/lane-graph/glyphs.ts';
|
|
31
|
+
import { UIFactory } from '../renderer/ui-factory.ts';
|
|
32
|
+
import { activeTokens } from '../renderer/theme.ts';
|
|
33
|
+
import { formatElapsed } from '../utils/format-elapsed.ts';
|
|
34
|
+
import { getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
|
|
35
|
+
|
|
36
|
+
export interface AgentViewInput {
|
|
37
|
+
readonly width: number;
|
|
38
|
+
readonly agent: AgentLaneInfo;
|
|
39
|
+
/** The agent's transcript (its lane info carries none for a WRFC owner). */
|
|
40
|
+
readonly messages: readonly ConversationMessageSnapshot[];
|
|
41
|
+
/** The agent's lane color index in main. */
|
|
42
|
+
readonly colorIndex: number;
|
|
43
|
+
/** Who started it: "main" or the parent agent's name. */
|
|
44
|
+
readonly parentName: string;
|
|
45
|
+
/** Main's work-tree sources (timings, spawned agents, waiting calls). */
|
|
46
|
+
readonly sources: WorkTreeSources;
|
|
47
|
+
/** Fold state for this view's beads; kept by the caller across frames. */
|
|
48
|
+
readonly collapse: Map<string, boolean>;
|
|
49
|
+
readonly glyphSet: TreeGlyphSetName;
|
|
50
|
+
readonly frame: number;
|
|
51
|
+
readonly now: number;
|
|
52
|
+
/** Steers sent from this view that the agent has not picked up yet, oldest first. */
|
|
53
|
+
readonly queuedSteers: readonly string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const TEXT_X = 3;
|
|
57
|
+
|
|
58
|
+
function put(line: Line, x: number, endX: number, text: string, style: { fg: string; bold?: boolean }): number {
|
|
59
|
+
let cx = x;
|
|
60
|
+
for (const ch of text) {
|
|
61
|
+
const w = getDisplayWidth(ch);
|
|
62
|
+
if (w <= 0) continue;
|
|
63
|
+
if (cx + w > endX || cx >= line.length) break;
|
|
64
|
+
line[cx] = { char: ch, fg: style.fg, bg: '', bold: style.bold ?? false, dim: false, underline: false, italic: false, strikethrough: false };
|
|
65
|
+
if (w === 2 && cx + 1 < line.length) line[cx + 1] = { ...line[cx]!, char: '' };
|
|
66
|
+
cx += w;
|
|
67
|
+
}
|
|
68
|
+
return cx;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function stateText(agent: AgentLaneInfo, parentName: string, now: number): { text: string; tone: 'faint' | 'good' | 'bad' | 'warn' } {
|
|
72
|
+
const started = agent.startedAt !== undefined ? `started by ${parentName} ${formatElapsed(Math.max(0, now - agent.startedAt))} ago` : `started by ${parentName}`;
|
|
73
|
+
switch (agent.status) {
|
|
74
|
+
case 'running': return { text: started, tone: 'faint' };
|
|
75
|
+
case 'pending': return { text: `waiting to start · ${started}`, tone: 'faint' };
|
|
76
|
+
case 'completed': {
|
|
77
|
+
const took = agent.startedAt !== undefined && agent.completedAt !== undefined ? ` in ${formatElapsed(agent.completedAt - agent.startedAt)}` : '';
|
|
78
|
+
return { text: `finished${took} · ${agent.toolCallCount} tool${agent.toolCallCount === 1 ? '' : 's'}`, tone: 'good' };
|
|
79
|
+
}
|
|
80
|
+
case 'failed': return { text: `failed${agent.error ? `: ${agent.error.split('\n')[0]}` : ''}`, tone: 'bad' };
|
|
81
|
+
case 'cancelled': return { text: 'stopped', tone: 'warn' };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The ◆ head row. */
|
|
86
|
+
function headRow(input: AgentViewInput): Line {
|
|
87
|
+
const t = activeTokens();
|
|
88
|
+
const line = createEmptyLine(input.width);
|
|
89
|
+
for (const cell of line) cell.bg = '';
|
|
90
|
+
const end = input.width - 3;
|
|
91
|
+
const color = laneColor(input.colorIndex);
|
|
92
|
+
let x = put(line, TEXT_X, end, '◆', { fg: color, bold: true });
|
|
93
|
+
// A chain owner is named for what it is, as its lane in main is.
|
|
94
|
+
x = put(line, x + 1, end, input.agent.wrfcPhases ? 'WRFC chain' : input.agent.hostedLabel ?? input.agent.name, { fg: color, bold: true });
|
|
95
|
+
const state = stateText(input.agent, input.parentName, input.now);
|
|
96
|
+
const stateFg = state.tone === 'good' ? t.success : state.tone === 'bad' ? t.error : state.tone === 'warn' ? t.warning : t.textFaint;
|
|
97
|
+
const task = input.agent.task.split('\n').find((l) => l.trim().length > 0)?.trim() ?? '';
|
|
98
|
+
// The state is kept whole; the task yields its room first.
|
|
99
|
+
const stateW = getDisplayWidth(state.text) + 3;
|
|
100
|
+
const taskRoom = end - x - 3 - stateW;
|
|
101
|
+
if (task && taskRoom >= 8) {
|
|
102
|
+
x = put(line, x + 1, end, '·', { fg: t.textFaint });
|
|
103
|
+
x = put(line, x + 1, end, truncateDisplay(task, taskRoom), { fg: t.textMuted });
|
|
104
|
+
}
|
|
105
|
+
x = put(line, x + 1, end, '·', { fg: t.textFaint });
|
|
106
|
+
put(line, x + 1, end, truncateDisplay(state.text, Math.max(0, end - x - 1)), { fg: stateFg });
|
|
107
|
+
return line;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Every line of the agent view, top to bottom. */
|
|
111
|
+
export function renderAgentView(input: AgentViewInput): Line[] {
|
|
112
|
+
const t = activeTokens();
|
|
113
|
+
const lines: Line[] = [headRow(input), createEmptyLine(input.width)];
|
|
114
|
+
const running = input.agent.status === 'running' || input.agent.status === 'pending';
|
|
115
|
+
const context: ConversationRenderContext = {
|
|
116
|
+
history: { addLine: (l) => { lines.push(l); }, addLines: (ls) => { lines.push(...ls); }, getLineCount: () => lines.length },
|
|
117
|
+
blockRegistry: [],
|
|
118
|
+
collapseState: input.collapse,
|
|
119
|
+
errorLineRegistry: [],
|
|
120
|
+
messageKindRegistry: new Map(),
|
|
121
|
+
configManager: null,
|
|
122
|
+
splashOptions: {},
|
|
123
|
+
// The agent's own turns are live while the agent runs, not while main does.
|
|
124
|
+
workTreeSources: { ...input.sources, turnActive: () => running, turnTiming: undefined },
|
|
125
|
+
treeGlyphSet: input.glyphSet,
|
|
126
|
+
focusId: null,
|
|
127
|
+
frame: input.frame,
|
|
128
|
+
};
|
|
129
|
+
const phases = input.agent.wrfcPhases;
|
|
130
|
+
if (phases) {
|
|
131
|
+
if (phases.length > 0) {
|
|
132
|
+
const model = buildWrfcOwnerModel({ info: input.agent, sources: context.workTreeSources ?? {}, collapse: input.collapse });
|
|
133
|
+
withLaneColorBase(input.colorIndex, () => { lines.push(...drawConversationTurn(context, model, input.width).lines); });
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
withLaneColorBase(input.colorIndex, () => appendConversationMessages(context, [...input.messages], input.width, []));
|
|
137
|
+
}
|
|
138
|
+
const empty = phases ? phases.length === 0 : input.messages.length === 0;
|
|
139
|
+
if (empty) {
|
|
140
|
+
const note = createEmptyLine(input.width);
|
|
141
|
+
for (const cell of note) cell.bg = '';
|
|
142
|
+
const text = phases
|
|
143
|
+
? (running ? 'The chain has not started a phase yet.' : 'This chain ran no phases.')
|
|
144
|
+
: running ? 'No transcript yet: the agent has not taken a turn.' : 'This agent left no transcript.';
|
|
145
|
+
put(note, TEXT_X + 2, input.width - 3, text, { fg: t.textFaint });
|
|
146
|
+
lines.push(note);
|
|
147
|
+
}
|
|
148
|
+
for (const steer of input.queuedSteers) {
|
|
149
|
+
lines.push(createEmptyLine(input.width));
|
|
150
|
+
lines.push(...UIFactory.createMessageBar(input.width, steer));
|
|
151
|
+
const note = createEmptyLine(input.width);
|
|
152
|
+
for (const cell of note) cell.bg = '';
|
|
153
|
+
put(note, TEXT_X + 2, input.width - 3, '⧗ queued · it arrives at the agent\'s next turn', { fg: t.textFaint });
|
|
154
|
+
lines.push(note);
|
|
155
|
+
}
|
|
156
|
+
return lines;
|
|
157
|
+
}
|