@pellux/goodvibes-tui 2.0.21 → 2.1.1
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 +65 -1
- package/README.md +30 -29
- package/docs/README.md +8 -5
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +28 -19
- 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-reference.ts +2 -2
- 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/live-verifier.ts +0 -10
- package/src/version.ts +1 -1
- 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/verification/verification-ledger.ts +0 -707
- /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/src/main.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { resolveGoodVibesDaemonHome, resolveGoodVibesHome } from '@pellux/goodvi
|
|
|
3
3
|
import { Compositor } from './renderer/compositor.ts';
|
|
4
4
|
import { type Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
5
5
|
import { UIFactory } from './renderer/ui-factory.ts';
|
|
6
|
+
import { resolveThrobberActivity } from './renderer/throbber.ts';
|
|
6
7
|
import { Orchestrator } from '@pellux/goodvibes-sdk/platform/core';
|
|
7
8
|
import { InputHandler } from './input/handler.ts';
|
|
8
9
|
import { SelectionManager } from '@pellux/goodvibes-terminal-shell';
|
|
@@ -17,31 +18,35 @@ import { handleBrokerApprovalChange, buildFixSessionAffordance, buildFixSessionE
|
|
|
17
18
|
import { CommandRegistry } from './input/command-registry.ts';
|
|
18
19
|
import type { CommandContext } from './input/command-registry.ts';
|
|
19
20
|
import { requestedEffortLevel } from './providers/reasoning-effort-surface.ts';
|
|
20
|
-
import {
|
|
21
|
+
import { wireWorkTree } from './core/work-tree-wiring.ts';
|
|
21
22
|
import { registerBuiltinCommands } from './input/commands.ts';
|
|
22
23
|
import { ScheduleManager } from '@pellux/goodvibes-sdk/platform/tools';
|
|
23
24
|
import { InputHistory } from './input/input-history.ts';
|
|
24
|
-
import { getTierPromptSupplement, getTierForContextWindow } from '@pellux/goodvibes-sdk/platform/providers';
|
|
25
|
+
import { describeContextWindowSource, getTierPromptSupplement, getTierForContextWindow } from '@pellux/goodvibes-sdk/platform/providers';
|
|
25
26
|
import { GitStatusProvider } from './renderer/git-status.ts';
|
|
26
27
|
import type { GitHeaderInfo } from './renderer/git-status.ts';
|
|
27
28
|
import { createShellLayout } from './renderer/layout-engine.ts';
|
|
28
|
-
import { buildShellFooter, estimateShellFooterHeight, promptCursorOffset } from './renderer/shell-surface.ts';
|
|
29
|
+
import { buildShellFooter, estimateShellFooterHeight, promptCursorOffset, statusCostText } from './renderer/shell-surface.ts';
|
|
30
|
+
import { HEADER_GAP_ROWS, withHeaderGap } from './renderer/header-line.ts';
|
|
31
|
+
import { TranscriptScroll } from './shell/transcript-scroll.ts';
|
|
32
|
+
import { formatStatusReport } from './shell/status-report.ts';
|
|
33
|
+
import { voiceCaptureDescription } from './renderer/voice-capture-chip.ts';
|
|
34
|
+
import { voiceCaptureRowVisible } from './core/voice-capture-status.ts';
|
|
35
|
+
import { resolveWebSurfaceUrl } from '@pellux/goodvibes-sdk/platform/runtime/feature-announcements';
|
|
29
36
|
import { createFailoverTurnState, resolveActiveModelDisplay } from './core/active-model-identity.ts';
|
|
30
37
|
import { computePromptContentWidth } from './renderer/prompt-content-width.ts';
|
|
31
|
-
import { buildConversationViewport } from './renderer/conversation-layout.ts';
|
|
32
|
-
import { applyConversationOverlays } from './renderer/conversation-overlays.ts';
|
|
33
|
-
import { buildPanelCompositeData } from './renderer/panel-composite.ts';
|
|
38
|
+
import { buildConversationViewport, centerViewportContent } from './renderer/conversation-layout.ts';
|
|
39
|
+
import { applyConversationOverlays, buildConversationLayers } from './renderer/conversation-overlays.ts';
|
|
34
40
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
35
|
-
import { registerBuiltinPanels } from './panels/builtin-panels.ts';
|
|
36
41
|
import { bootstrapRuntime } from './runtime/bootstrap.ts';
|
|
37
42
|
import type { BootstrapContext } from './runtime/bootstrap.ts';
|
|
38
43
|
import { selfUpdateAtLaunch } from './cli/launch-auto-update.ts';
|
|
39
44
|
import { buildSharedOrchestratorCoreServices, refreshMemoryRecallSnapshot } from './runtime/orchestrator-core-services.ts';
|
|
40
45
|
import { createSessionContinuityHintsBuilder } from './runtime/session-continuity-hints.ts';
|
|
41
|
-
import { resolveWebSurfaceUrl } from '@pellux/goodvibes-sdk/platform/runtime/feature-announcements';
|
|
42
46
|
import { readLastSessionPointer } from '@/runtime/index.ts';
|
|
43
47
|
import { startRecoveryAutosave } from './runtime/recovery-autosave.ts';
|
|
44
|
-
import {
|
|
48
|
+
import { startRecoveryOffer } from './runtime/recovery-prompt.ts';
|
|
49
|
+
import { createStartupTypeaheadGate } from './runtime/startup-typeahead-gate.ts';
|
|
45
50
|
import { buildRecoveryOfferWiring } from './runtime/recovery-offer-wiring.ts';
|
|
46
51
|
import { handleBlockingShellInput, type PendingPermissionState } from './shell/blocking-input.ts';
|
|
47
52
|
import { handleErrorAffordanceKey } from './shell/recovery-input-helpers.ts';
|
|
@@ -53,21 +58,21 @@ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
53
58
|
import { prepareShellCliRuntime } from './cli/entrypoint.ts';
|
|
54
59
|
import { applyInitialTuiCliState, reportFatalStartupError } from './cli/tui-startup.ts';
|
|
55
60
|
import { applyConfiguredHitlMode, applyRuntimeConfigValue, applyTerminalRuntimeConfigDefaults } from '@pellux/goodvibes-terminal-shell';
|
|
56
|
-
import { renderToolCallBlock } from './renderer/tool-call.ts';
|
|
57
61
|
import { installVoiceCapture } from './shell/voice-capture-shell.ts';
|
|
58
62
|
import { allowTerminalWrite, installFullScreenTerminalOutputGuard } from '@pellux/goodvibes-terminal-shell/terminal-output-guard';
|
|
59
63
|
import { installProcessLifecycle } from './runtime/process-lifecycle.ts';
|
|
60
64
|
import { createRenderScheduler } from '@pellux/goodvibes-terminal-shell';
|
|
61
65
|
import { buildCommandArgsHint } from './input/command-args-hint.ts';
|
|
62
66
|
import { summarizeRunningAgents } from './renderer/process-summary.ts';
|
|
63
|
-
import { footerFleetCost } from './
|
|
67
|
+
import { footerFleetCost } from './views/fleet-read-model.ts';
|
|
68
|
+
import { footerTargetRows } from './renderer/footer-targets.ts';
|
|
64
69
|
import { formatUserFacingErrorLine } from './core/format-user-error.ts';
|
|
65
70
|
import { wireStreamEventMetrics, createStreamMetrics, type StreamMetrics, type WireStreamEventMetricsResult } from './core/stream-event-wiring.ts';
|
|
66
71
|
import { wireTurnEventHandlers } from './core/turn-event-wiring.ts';
|
|
67
72
|
import { resolveContextStatusHint } from './renderer/context-status-hint.ts';
|
|
68
73
|
import { isEffectiveDangerMode } from '@pellux/goodvibes-sdk/platform/config';
|
|
69
74
|
import { applyComposerCapture, applyAtModelDirective } from './input/composer-capture.ts';
|
|
70
|
-
import { makeComposerEditorOpener } from './input/composer-editor.ts';
|
|
75
|
+
import { makeComposerEditorOpener, makeFileEditorOpener } from './input/composer-editor.ts';
|
|
71
76
|
import { evaluateSessionMaintenance } from '@/runtime/index.ts';
|
|
72
77
|
import { createCancelGeneration } from './core/turn-cancellation.ts';
|
|
73
78
|
import { wireInteractionSeams, createMemoryProvenanceUi } from './runtime/interaction-seams.ts';
|
|
@@ -75,14 +80,15 @@ import { createPowerChipSource } from './core/power-chip-source.ts';
|
|
|
75
80
|
import { fetchDaemonPowerState, installKeepAwakeRemoteForward } from './runtime/power-keepawake-remote.ts';
|
|
76
81
|
import { wrapRequestPermissionWithAlert } from './core/approval-alert.ts';
|
|
77
82
|
import { createTerminalNotifier } from './core/terminal-notifier.ts';
|
|
78
|
-
import { setPanelFrameRequester } from './panels/base-panel.ts';
|
|
79
83
|
import { createDeferredRender } from './renderer/deferred-render.ts';
|
|
80
84
|
import { wireSessionAmbience } from './runtime/session-ambience-wiring.ts';
|
|
81
85
|
import { createSpokenTurnInputOptions } from './audio/spoken-turn-model-routing.ts';
|
|
82
86
|
|
|
83
87
|
import { ALT_SCREEN_ENTER, ALT_SCREEN_EXIT, MOUSE_ENABLE, MOUSE_DISABLE, CURSOR_HIDE, CURSOR_SHOW, CLEAR_SCREEN, KEYBOARD_EXT_ENABLE, KEYBOARD_EXT_DISABLE, PASTE_ENABLE, PASTE_DISABLE, FOCUS_ENABLE, FOCUS_DISABLE } from './renderer/terminal-escapes.ts';
|
|
84
88
|
import { installBackgroundThemeProbe } from './renderer/terminal-bg-probe.ts';
|
|
89
|
+
import { registerThemeRefresh } from './renderer/theme.ts';
|
|
85
90
|
import { VERSION } from './version.ts';
|
|
91
|
+
import { SessionViews } from './shell/session-views.ts';
|
|
86
92
|
|
|
87
93
|
async function main() {
|
|
88
94
|
const stdout = process.stdout;
|
|
@@ -158,12 +164,12 @@ async function main() {
|
|
|
158
164
|
// that should compete with real session alerts for attention).
|
|
159
165
|
for (const line of launchUpdateLines) systemMessageRouter.low(`[Update] ${line}`);
|
|
160
166
|
|
|
161
|
-
const
|
|
162
|
-
const buildSessionContinuityHints = createSessionContinuityHintsBuilder({ readModels: uiServices.readModels, panelManager });
|
|
167
|
+
const buildSessionContinuityHints = createSessionContinuityHintsBuilder({ readModels: uiServices.readModels });
|
|
163
168
|
|
|
164
169
|
// Callable before the scheduler exists; see deferred-render.ts for why.
|
|
165
170
|
const deferredRender = createDeferredRender();
|
|
166
171
|
const render = deferredRender.render;
|
|
172
|
+
const typeahead = createStartupTypeaheadGate(); // keys typed before a startup modal appeared never answer it
|
|
167
173
|
|
|
168
174
|
let pendingPermission: PendingPermissionState | null = null;
|
|
169
175
|
// One-key jump/attach to a spawned CI fix-session: the affordance ARMS the id; the next 'j' runs the resume so the user never retypes it.
|
|
@@ -183,12 +189,12 @@ async function main() {
|
|
|
183
189
|
}));
|
|
184
190
|
refreshFixSessionsFromApprovals(() => approvalBroker.listApprovals(), onFixSessionStarted, onFixSessionError); // catch pre-subscription stamps
|
|
185
191
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// Cached from the overlay-aware clamp the renderer computes (conversation-layout) each frame so scroll() clamps against exactly what is displayed, not a footer estimate.
|
|
189
|
-
let lastMaxScroll: number | null = null;
|
|
192
|
+
// Where the main transcript is scrolled to; each frame's overlay-aware clamp (conversation-layout) is fed back so a scroll clamps against exactly what is displayed (shell/transcript-scroll.ts).
|
|
193
|
+
const transcript = new TranscriptScroll();
|
|
190
194
|
// Stream and tool-timer state; mutated by wireStreamEventMetrics handlers, read during render.
|
|
191
195
|
const streamMetrics: StreamMetrics = createStreamMetrics();
|
|
196
|
+
// When the running compaction was first seen (the throbber's elapsed time); undefined while none runs.
|
|
197
|
+
let compactingSinceMs: number | undefined;
|
|
192
198
|
// Live failover record, written by the failover path, read every frame so the header and footer agree.
|
|
193
199
|
const failoverState = createFailoverTurnState();
|
|
194
200
|
|
|
@@ -196,33 +202,37 @@ async function main() {
|
|
|
196
202
|
// Live-microphone footer row (push-to-talk or the wake detector); assigned once voice capture is wired below, null until then so pre-wiring frames size correctly.
|
|
197
203
|
let voiceCaptureStatus: () => import('./core/voice-capture-status.ts').VoiceCaptureIndicatorState | null = () => null;
|
|
198
204
|
|
|
205
|
+
// Agents and background processes opened full screen (shell/session-views.ts); set once the input handler exists.
|
|
206
|
+
let sessionViews: SessionViews | null = null;
|
|
207
|
+
let lastHeaderModel = runtime.model;
|
|
199
208
|
const getViewportHeight = (): number => {
|
|
200
209
|
if (input.onboardingWizard.active) return stdout.rows || 24;
|
|
201
210
|
const promptLines: number = input.getVisiblePromptLineCount(getPromptContentWidth());
|
|
202
211
|
const currentModel = providerRegistry.getCurrentModel();
|
|
203
212
|
const contextWindow = providerRegistry.getContextWindowForModel(currentModel);
|
|
204
213
|
const rows = stdout.rows || 24;
|
|
205
|
-
//
|
|
206
|
-
return rows -
|
|
214
|
+
// The header row (+ the session chips), and on the main screen the empty row under them (a view's body brings its own).
|
|
215
|
+
return rows - (sessionViews?.headerRows() ?? 1) - (sessionViews?.active ? 0 : HEADER_GAP_ROWS) - estimateShellFooterHeight(promptLines);
|
|
207
216
|
};
|
|
208
217
|
|
|
209
218
|
const scroll = (delta: number) => {
|
|
210
|
-
//
|
|
211
|
-
// The footer estimate is only a fallback for the pre-first-render frame.
|
|
212
|
-
|
|
213
|
-
scrollTop = Math.max(0, Math.min(scrollTop + delta, maxScroll));
|
|
214
|
-
// Re-lock if user scrolled to bottom, otherwise unlock
|
|
215
|
-
scrollLocked = scrollTop >= maxScroll;
|
|
219
|
+
if (sessionViews?.active) { sessionViews.scroll(-delta); return; } // a view scrolls its own lines (up is positive there)
|
|
220
|
+
// The footer estimate is only a fallback for the pre-first-render frame; reaching the bottom re-locks.
|
|
221
|
+
transcript.scrollBy(delta, () => Math.max(0, conversation.history.getLineCount() - getViewportHeight()));
|
|
216
222
|
};
|
|
217
223
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
scrollTop = Math.max(0, conversation.history.getLineCount() - vHeight);
|
|
224
|
+
// Back to the live bottom: re-lock, and the next frame follows the tail again.
|
|
225
|
+
const scrollToLiveBottom = () => {
|
|
226
|
+
transcript.toBottom();
|
|
227
|
+
render();
|
|
223
228
|
};
|
|
224
229
|
|
|
230
|
+
// Respect a manual scroll-up: only follow the tail while parked at the bottom (submitInput re-locks on new input).
|
|
231
|
+
const scrollToEnd = (vHeight: number) => transcript.followTail(conversation.history.getLineCount(), vHeight);
|
|
232
|
+
|
|
225
233
|
const unsubs: Array<() => void> = [];
|
|
234
|
+
// The work tree's live facts: call/turn timings, agent lanes, the call a permission prompt holds, fold persistence (work-tree-wiring.ts).
|
|
235
|
+
unsubs.push(...wireWorkTree({ conversation, events: uiServices.events, agentManager, listChains: () => ctx.services.wrfcController.listChains(), fleetNodes: () => ctx.services.processRegistry.query().nodes, pendingCallId: () => pendingPermission?.callId, turnActive: () => orchestrator.isThinking, sessionsDir: ctx.services.surface.sessionsDir, sessionId: () => runtime.sessionId, requestRender: () => render() }).unsubs);
|
|
226
236
|
let recoveryInterval: ReturnType<typeof setInterval> | null = null;
|
|
227
237
|
let stopSpokenOutputForExit: (() => Promise<void>) | null = null;
|
|
228
238
|
// The optional "used N memories" provenance chip (default OFF), see interaction-seams.ts.
|
|
@@ -273,7 +283,7 @@ async function main() {
|
|
|
273
283
|
unsubs.push(...ambience.unsubs);
|
|
274
284
|
const submitInput = (text: string, content?: ContentPart[], options: { readonly spokenOutput?: boolean } = {}) => {
|
|
275
285
|
input.clearModalStack();
|
|
276
|
-
|
|
286
|
+
transcript.toBottom(); // Re-lock on any user input
|
|
277
287
|
conversation.dismissSplash(); // owner rule: any submission retires the splash for the run
|
|
278
288
|
let processedText = applyAtModelDirective(text, {
|
|
279
289
|
providerRegistry, runtime, configManager, notify: (m) => systemMessageRouter.high(m),
|
|
@@ -311,16 +321,14 @@ async function main() {
|
|
|
311
321
|
render();
|
|
312
322
|
return;
|
|
313
323
|
}
|
|
314
|
-
|
|
315
|
-
scrollTop = Math.max(0, line);
|
|
324
|
+
transcript.jumpTo(line);
|
|
316
325
|
render();
|
|
317
326
|
};
|
|
318
327
|
|
|
319
328
|
const scrollToLine = (line: number) => {
|
|
320
329
|
conversation.getDisplayBlocks();
|
|
321
330
|
const maxScroll = Math.max(0, conversation.history.getLineCount() - getViewportHeight());
|
|
322
|
-
|
|
323
|
-
scrollTop = Math.max(0, Math.min(line, maxScroll));
|
|
331
|
+
transcript.jumpTo(line, maxScroll);
|
|
324
332
|
render();
|
|
325
333
|
};
|
|
326
334
|
|
|
@@ -352,12 +360,12 @@ async function main() {
|
|
|
352
360
|
...buildPendingPermissionExtras(request, resolve, approvalBroker),
|
|
353
361
|
};
|
|
354
362
|
render();
|
|
355
|
-
}), { focusTracker: ctx.services.focusTracker, configGet: (k: string) => configManager.get(k as Parameters<typeof configManager.get>[0]), webhookNotifier: ctx.services.webhookNotifier, terminalNotifier });
|
|
363
|
+
}), { focusTracker: ctx.services.focusTracker, configGet: (k: string) => configManager.get(k as Parameters<typeof configManager.get>[0]), webhookNotifier: ctx.services.webhookNotifier, terminalNotifier, conversation });
|
|
356
364
|
|
|
357
365
|
const input: InputHandler = new InputHandler(
|
|
358
366
|
() => render(),
|
|
359
367
|
selection,
|
|
360
|
-
() =>
|
|
368
|
+
() => transcript.top,
|
|
361
369
|
getViewportHeight,
|
|
362
370
|
() => conversation.history,
|
|
363
371
|
scroll,
|
|
@@ -390,7 +398,6 @@ async function main() {
|
|
|
390
398
|
shell: {
|
|
391
399
|
bookmarkManager: ctx.services.bookmarkManager,
|
|
392
400
|
keybindingsManager: ctx.services.keybindingsManager,
|
|
393
|
-
panelManager,
|
|
394
401
|
processManager,
|
|
395
402
|
profileManager: ctx.services.profileManager,
|
|
396
403
|
},
|
|
@@ -409,9 +416,19 @@ async function main() {
|
|
|
409
416
|
);
|
|
410
417
|
|
|
411
418
|
orchestratorRefs.getViewportHeight = getViewportHeight; orchestratorRefs.scrollToEnd = scrollToEnd;
|
|
419
|
+
const views = new SessionViews({
|
|
420
|
+
conversation, agentManager, processManager, fleetNodes: () => ctx.services.processRegistry.query().nodes,
|
|
421
|
+
steer: (id, text) => ctx.views.fleet.actions.steer(id, text), killAgent: (id) => ctx.views.fleet.actions.kill(id, { cascade: true }),
|
|
422
|
+
mainBusy: () => orchestrator.isThinking, mainModel: () => lastHeaderModel, promptText: () => input.prompt, requestRender: () => render(),
|
|
423
|
+
});
|
|
424
|
+
sessionViews = views; input.sessionView = views; unsubs.push(() => views.dispose());
|
|
425
|
+
// Esc while the main transcript is scrolled back returns to the live bottom and never interrupts the turn (handler-modal-stack.ts).
|
|
426
|
+
input.transcriptScroll = { scrolledBack: () => transcript.scrolledBack && !views.active, toBottom: scrollToLiveBottom };
|
|
427
|
+
commandContext.openSessionView = (target) => views.open(target);
|
|
412
428
|
|
|
413
429
|
input.setCommandRegistry(commandRegistry, commandContext);
|
|
414
430
|
commandContext.openComposerEditor = makeComposerEditorOpener({ buffer: input, stdin, stdout, writeGuard: allowTerminalWrite, repaint: () => { compositor.resetDiff(); render(); }, cwd: workingDir, env: process.env, notify: (m) => systemMessageRouter.high(m) });
|
|
431
|
+
commandContext.openFileInEditor = makeFileEditorOpener({ stdin, stdout, writeGuard: allowTerminalWrite, repaint: () => { compositor.resetDiff(); render(); }, cwd: workingDir, env: process.env, notify: (m) => systemMessageRouter.high(m) });
|
|
415
432
|
input.setConversationManager(conversation);
|
|
416
433
|
input.setContentWidth(getPromptContentWidth()); input.filePicker.setOnUpdate(() => render());
|
|
417
434
|
// retirement: agentDetailModal/processModal setOnRefresh wiring removed,
|
|
@@ -437,26 +454,37 @@ async function main() {
|
|
|
437
454
|
|
|
438
455
|
// Cache the current model for consistent values across the entire render frame
|
|
439
456
|
const currentModel = providerRegistry.getCurrentModel();
|
|
440
|
-
// Resolve the
|
|
441
|
-
//
|
|
442
|
-
|
|
457
|
+
// Resolve the context window once, so the footer meter, the status hint and
|
|
458
|
+
// the context inspector agree. Only a window some source states is shown:
|
|
459
|
+
// null means unknown (a guess, or disproven by a larger accepted request),
|
|
460
|
+
// and every consumer below reads 0 as "no window to measure against".
|
|
461
|
+
const knownContextWindow = providerRegistry.getKnownContextWindowForModel(currentModel);
|
|
462
|
+
const contextWindow = knownContextWindow ?? 0;
|
|
443
463
|
const sessionSnapshot = uiServices.readModels.session.getSnapshot();
|
|
444
464
|
const agentSnapshot = uiServices.readModels.agents.getSnapshot();
|
|
445
465
|
|
|
446
466
|
const activeModel = resolveActiveModelDisplay({ serving: currentModel, configuredRegistryKey: configManager.get('provider.model') as string, configuredLabel: runtime.model, configuredProvider: runtime.provider, failover: failoverState.current() });
|
|
447
|
-
|
|
467
|
+
lastHeaderModel = activeModel.headerModel;
|
|
468
|
+
const viewFrame = views.frame(width); // an agent or process open full screen, or null in main
|
|
469
|
+
const headerLines = viewFrame ? [viewFrame.header] : UIFactory.createHeader(width, activeModel.headerModel, conversation.title || undefined, lastGitInfoRef.value, undefined, activeModel.divergenceNote);
|
|
470
|
+
const chipsRow = views.chips(width);
|
|
471
|
+
if (chipsRow) headerLines.push(chipsRow);
|
|
472
|
+
// Scrolled away from the live bottom: the back-to-bottom pill, with the esc keycap only while the next Esc goes there.
|
|
473
|
+
const promptEmpty = input.prompt.length === 0 && !input.commandMode;
|
|
474
|
+
const backToBottom = viewFrame
|
|
475
|
+
? (views.scrolledBack() ? { escKey: views.escGoesToBottom() } : null)
|
|
476
|
+
: (transcript.scrolledBack && !conversation.isSplashShowing() ? { escKey: promptEmpty && !conversation.workTree.focused && !input.indicatorFocused } : null);
|
|
448
477
|
const managerAgents = agentManager.list().filter((a) => a.status === 'running' || a.status === 'pending');
|
|
449
478
|
const runtimeAgents = agentSnapshot.active;
|
|
450
479
|
const runningAgentSummary = summarizeRunningAgents(managerAgents, runtimeAgents, ctx.services.wrfcController.listChains());
|
|
451
480
|
const runningAgentCount = runningAgentSummary.count;
|
|
452
|
-
const runningProcessCount = processManager.list().filter((p) => !p.
|
|
481
|
+
const runningProcessCount = processManager.list().filter((p) => !p.done).length;
|
|
453
482
|
const cw = getPromptContentWidth();
|
|
454
483
|
const promptInfo = input.getWrappedPromptInfo(cw);
|
|
455
484
|
const commandArgsHint = buildCommandArgsHint(input.prompt, commandRegistry);
|
|
456
485
|
const composerState = deriveComposerState({
|
|
457
486
|
text: input.prompt,
|
|
458
487
|
commandMode: input.commandMode,
|
|
459
|
-
panelFocused: input.panelFocused,
|
|
460
488
|
pendingApproval: pendingPermission !== null,
|
|
461
489
|
hasAttachments: input.getImageAttachments().size > 0, turnState: sessionSnapshot.turnState,
|
|
462
490
|
});
|
|
@@ -464,84 +492,86 @@ async function main() {
|
|
|
464
492
|
evaluate: (args) => evaluateSessionMaintenance({ configManager, ...args, sessionMemoryCount: ctx.services.sessionMemoryStore.list().length }),
|
|
465
493
|
currentTokens: orchestrator.lastInputTokens, contextWindow,
|
|
466
494
|
});
|
|
495
|
+
// What main is doing while it works (a turn, a compaction): the throbber row above the input area.
|
|
496
|
+
const compacting = ctx.services.contextAccountingHolder.getSource()?.getCompactionState().isCompacting === true;
|
|
497
|
+
if (compacting && compactingSinceMs === undefined) compactingSinceMs = Date.now();
|
|
498
|
+
else if (!compacting) compactingSinceMs = undefined;
|
|
499
|
+
const throbberNow = Date.now();
|
|
500
|
+
const activeToolCallId = streamMetrics.activeToolCallId;
|
|
501
|
+
// An ask brokered for a background agent carries an attribution; only main's own ask is main's activity.
|
|
502
|
+
const mainApproval = pendingPermission && pendingPermission.attribution === undefined ? pendingPermission : null;
|
|
503
|
+
const throbberActivity = resolveThrobberActivity({
|
|
504
|
+
turnActive: orchestrator.isThinking,
|
|
505
|
+
compacting,
|
|
506
|
+
compactingSinceMs,
|
|
507
|
+
pendingApproval: mainApproval ? { name: mainApproval.tool, args: mainApproval.args } : null,
|
|
508
|
+
activeTool: streamMetrics.activeToolName !== undefined
|
|
509
|
+
? { name: streamMetrics.activeToolName, args: activeToolCallId !== undefined ? streamMetrics.toolArgsByCallId.get(activeToolCallId) : undefined, startedAtMs: streamMetrics.activeToolStartedAtMs }
|
|
510
|
+
: null,
|
|
511
|
+
modelPhrase: UIFactory.busyPhrase(orchestrator.thinkingFrame, orchestrator.streamingOutputTokens, UIFactory.computeRenderStallInfo(streamMetrics, throbberNow), mainApproval !== null),
|
|
512
|
+
turnStartMs: streamMetrics.startTime,
|
|
513
|
+
ttftMs: streamMetrics.ttftMs,
|
|
514
|
+
tokenSpeed: (configManager.get('display.showTokenSpeed') as boolean) ? streamMetrics.tokenSpeed : undefined,
|
|
515
|
+
now: throbberNow,
|
|
516
|
+
});
|
|
517
|
+
const throbber = throbberActivity ? { spinner: orchestrator.getSpinner(), frame: orchestrator.thinkingFrame, activity: throbberActivity } : null;
|
|
467
518
|
const footerLines = buildShellFooter({
|
|
468
519
|
width,
|
|
469
520
|
promptText: promptInfo.visibleLines.join('\n'),
|
|
470
521
|
promptLineCount: promptInfo.visibleLines.length,
|
|
471
522
|
promptCursorPos: promptCursorOffset(promptInfo),
|
|
472
|
-
usage: { up: orchestrator.usage.input, down: orchestrator.usage.output, fleetCostUsd: footerFleetCost(() => ctx.services.processRegistry.query().nodes, runningAgentCount > 0) },
|
|
523
|
+
usage: { up: orchestrator.usage.input, down: orchestrator.usage.output, cacheRead: orchestrator.usage.cacheRead, cacheWrite: orchestrator.usage.cacheWrite, fleetCostUsd: footerFleetCost(() => ctx.services.processRegistry.query().nodes, runningAgentCount > 0) },
|
|
473
524
|
showExitNotice: input.showExitNotice,
|
|
474
525
|
lastCopyTime: input.lastCopyTime,
|
|
475
|
-
model: activeModel.footerModel,
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
contextWindow,
|
|
526
|
+
model: activeModel.footerModel, // prices the cost; the header names the model
|
|
527
|
+
workingDir, homeDirectory, view: viewFrame?.footer ?? null,
|
|
528
|
+
branch: lastGitInfoRef.value?.branch,
|
|
529
|
+
contextWindow: knownContextWindow,
|
|
480
530
|
contextStatusHint,
|
|
481
531
|
retryHint: retryAffordanceHint(retryAffordance),
|
|
482
532
|
scriptableStatusLine: scriptableStatusline.current(),
|
|
483
|
-
// Compact footer posture on short terminals so the shell stays usable.
|
|
484
|
-
compact: height < 30,
|
|
485
533
|
// behavior.autoCompactThreshold is stored as a percent integer (e.g. 80);
|
|
486
|
-
// the
|
|
534
|
+
// the bar expects a fraction [0..1]. Clamp to [0,1] to guard nonsense values.
|
|
487
535
|
compactThreshold: Math.min(1, Math.max(0, (configManager.get('behavior.autoCompactThreshold') as number) / 100)),
|
|
488
536
|
dangerMode: isEffectiveDangerMode(configManager),
|
|
489
537
|
lastInputTokens: orchestrator.lastInputTokens,
|
|
490
538
|
commandArgsHint,
|
|
491
|
-
|
|
492
|
-
// Cross-surface spine posture segment (adopted-daemon mode only).
|
|
493
|
-
sessionSpineStatus: (() => { const s = uiServices.platform.externalServices?.inspect(); return s?.sessionSpineActive && s.sessionSpineStatus && s.sessionSpineStatus !== 'unknown' ? s.sessionSpineStatus : undefined; })(), runningAgentCount, runningProcessCount,
|
|
494
|
-
webSurfaceUrl: configManager.get('web.enabled') ? resolveWebSurfaceUrl(configManager) : undefined,
|
|
539
|
+
runningAgentCount, runningProcessCount,
|
|
495
540
|
// Always-visible "sleep disabled" chip, topology-aware: the DAEMON's state in adopted-external mode, the in-process manager otherwise (power-chip-source.ts).
|
|
496
541
|
powerKeepAwake: powerChipSource.get().keepAwake,
|
|
497
|
-
// Composer must not read as focused while the
|
|
498
|
-
promptFocused: !input.
|
|
542
|
+
// Composer must not read as focused while the process indicator owns keyboard focus.
|
|
543
|
+
promptFocused: !input.indicatorFocused && !conversation.workTree.focused && viewFrame?.footer.disabledReason === undefined, workTreeFocused: conversation.workTree.focused && !viewFrame,
|
|
499
544
|
indicatorFocused: input.indicatorFocused,
|
|
500
545
|
runningAgentProgress: runningAgentSummary.progress,
|
|
501
|
-
composerMode: composerState.modeLabel,
|
|
502
|
-
composerStatus: composerState.statusLabel,
|
|
503
546
|
composerFlags: composerState.flags,
|
|
504
547
|
composerPendingRisk: composerState.pendingRisk, permissionMode: configManager.get('permissions.mode') as string, voiceCapture: voiceCaptureStatus(),
|
|
548
|
+
throbber, turnRunning: orchestrator.isThinking, backToBottom,
|
|
505
549
|
}).lines;
|
|
506
550
|
|
|
507
551
|
const onboardingOwnsScreen = input.onboardingWizard.active;
|
|
508
|
-
|
|
552
|
+
// The main screen keeps one empty row under the header; a view's body starts with its own.
|
|
553
|
+
const shellHeaderLines = onboardingOwnsScreen ? [] : viewFrame ? headerLines : withHeaderGap(headerLines, width);
|
|
554
|
+
input.bodyTopRow = shellHeaderLines.length; // mouse rows map to transcript rows from here
|
|
509
555
|
const shellFooterLines = onboardingOwnsScreen ? [] : footerLines;
|
|
510
|
-
|
|
511
|
-
? panelManager.getRightWidth(width)
|
|
512
|
-
: 0;
|
|
556
|
+
input.footerTargets = footerTargetRows(shellFooterLines, height - shellFooterLines.length); // clickable usage rows open Usage
|
|
513
557
|
const shellLayout = createShellLayout({
|
|
514
558
|
width,
|
|
515
559
|
height,
|
|
516
560
|
headerHeight: shellHeaderLines.length,
|
|
517
561
|
footerHeight: shellFooterLines.length,
|
|
518
|
-
panelWidth,
|
|
519
562
|
});
|
|
520
|
-
input.setPanelMouseLayout(shellLayout.panel
|
|
521
|
-
? {
|
|
522
|
-
x: shellLayout.panel.x,
|
|
523
|
-
y: shellLayout.panel.y,
|
|
524
|
-
width: shellLayout.panel.width,
|
|
525
|
-
height: shellLayout.panel.height,
|
|
526
|
-
hasBottomPane: panelManager.isBottomPaneVisible() && panelManager.getBottomPane().panels.length > 0,
|
|
527
|
-
verticalSplitRatio: panelManager.getVerticalSplitRatio(),
|
|
528
|
-
}
|
|
529
|
-
: null);
|
|
530
563
|
const vHeight = shellLayout.body.height;
|
|
531
564
|
const conversationWidth = shellLayout.conversation.width;
|
|
532
565
|
activeConversationWidth = conversationWidth;
|
|
533
|
-
const hasPanelWorkspace = !onboardingOwnsScreen && panelManager.isVisible() && panelManager.getAllOpen().length > 0;
|
|
534
|
-
conversation.setSplashSuppressed(hasPanelWorkspace);
|
|
535
566
|
|
|
536
567
|
// Flush pending renders after updating the width provider and splash posture
|
|
537
568
|
// so the transcript and splash rebuild against the current shell layout.
|
|
569
|
+
conversation.workTree.tickLive(); // running beads spin and count up
|
|
538
570
|
conversation.getDisplayBlocks();
|
|
539
571
|
if (conversation.consumeSplashTransition()) compositor.requestFullRepaint(); // splash → transcript: repaint the whole viewport once
|
|
540
572
|
|
|
541
573
|
// Calculate how many rows are consumed by overlays (thinking, permissions, queue, file picker)
|
|
542
574
|
let overlayRows = 0;
|
|
543
|
-
if (orchestrator.isThinking) overlayRows += 2; // spinner + blank
|
|
544
|
-
if (pendingPermission) overlayRows += PermissionPromptUI.getPromptHeight(pendingPermission, pendingPermission.hunkState, pendingPermission.detailsExpanded, pendingPermission.requestedBy, PermissionPromptUI.promptViewState(pendingPermission, conversationWidth, approvalBroker));
|
|
545
575
|
overlayRows += orchestrator.messageQueue.length * 3; // queued messages
|
|
546
576
|
// File picker and model picker overlay rows computed from actual rendered line count below
|
|
547
577
|
// Selection modal overlay rows are computed from actual rendered line count below
|
|
@@ -553,102 +583,83 @@ async function main() {
|
|
|
553
583
|
conversation,
|
|
554
584
|
width: conversationWidth,
|
|
555
585
|
viewportHeight: vHeight,
|
|
556
|
-
scrollTop,
|
|
557
|
-
scrollLocked,
|
|
586
|
+
scrollTop: transcript.top,
|
|
587
|
+
scrollLocked: transcript.locked,
|
|
558
588
|
overlayRows,
|
|
559
589
|
});
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
//
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
conversationWidth,
|
|
574
|
-
orchestrator.getSpinner(),
|
|
575
|
-
orchestrator.thinkingFrame,
|
|
576
|
-
showSpeed ? streamMetrics.tokenSpeed : undefined,
|
|
577
|
-
showPreview ? partialToolPreview : undefined,
|
|
578
|
-
orchestrator.streamingInputTokens > 0 ? orchestrator.streamingInputTokens : undefined,
|
|
579
|
-
orchestrator.streamingOutputTokens > 0 ? orchestrator.streamingOutputTokens : undefined,
|
|
580
|
-
turnElapsedMs,
|
|
581
|
-
streamMetrics.ttftMs,
|
|
582
|
-
stallInfo,
|
|
583
|
-
pendingPermission !== null,
|
|
584
|
-
);
|
|
585
|
-
viewport.push(...thinking);
|
|
586
|
-
// Live tool timer: render the currently executing tool row with ticking elapsed.
|
|
587
|
-
if (streamMetrics.activeToolName !== undefined && streamMetrics.activeToolStartedAtMs !== undefined) {
|
|
588
|
-
const liveToolCall = { id: streamMetrics.activeToolCallId ?? 'live', name: streamMetrics.activeToolName, arguments: {} };
|
|
589
|
-
viewport.push(...renderToolCallBlock(liveToolCall, 'executing', undefined, conversationWidth, undefined, undefined, undefined, streamMetrics.activeToolStartedAtMs));
|
|
590
|
-
}
|
|
590
|
+
// Unlocked but already at the bottom (a jump that landed there, output that
|
|
591
|
+
// shrank) is the live bottom: it follows again, and the pill drawn this frame goes.
|
|
592
|
+
if (transcript.settle(conversationViewport.nextScrollTop, conversationViewport.maxScroll) && backToBottom && !viewFrame) render();
|
|
593
|
+
const scrollTop = transcript.top;
|
|
594
|
+
let viewport = viewFrame ? viewFrame.body(vHeight) : conversation.isSplashShowing()
|
|
595
|
+
? centerViewportContent(conversationViewport.viewport, conversationViewport.effectiveHeight, conversationWidth)
|
|
596
|
+
: conversationViewport.viewport;
|
|
597
|
+
|
|
598
|
+
if (orchestrator.isThinking && !viewFrame) {
|
|
599
|
+
// The spinner and phrase are the throbber's (above the input area); the
|
|
600
|
+
// opt-in partial tool preview keeps its own faint row under the transcript.
|
|
601
|
+
const partialToolPreview = (configManager.get('display.showToolPreview') as boolean) ? sessionSnapshot.streamToolPreview : undefined;
|
|
602
|
+
if (partialToolPreview) viewport.push(UIFactory.createToolPreviewRow(conversationWidth, partialToolPreview));
|
|
591
603
|
}
|
|
592
604
|
|
|
593
|
-
if (
|
|
594
|
-
viewport.push(...PermissionPromptUI.createPromptLines(conversationWidth, pendingPermission, pendingPermission.hunkState, pendingPermission.detailsExpanded, pendingPermission.requestedBy, PermissionPromptUI.promptViewState(pendingPermission, conversationWidth, approvalBroker)));
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
viewport.push(...UIFactory.createQueuedMessageList(conversationWidth, orchestrator.listQueuedMessages()));
|
|
598
|
-
viewport.push(...memoryProvenanceUi.renderChip(conversationWidth, configManager));
|
|
599
|
-
|
|
600
|
-
viewport = applyConversationOverlays(viewport, {
|
|
601
|
-
input,
|
|
602
|
-
conversation,
|
|
603
|
-
commandRegistry,
|
|
604
|
-
keybindingsManager: ctx.services.keybindingsManager,
|
|
605
|
-
conversationWidth,
|
|
606
|
-
viewportHeight: vHeight,
|
|
607
|
-
contextWindow,
|
|
608
|
-
});
|
|
605
|
+
if (!viewFrame) viewport.push(...UIFactory.createQueuedMessageList(conversationWidth, orchestrator.listQueuedMessages()), ...memoryProvenanceUi.renderChip(conversationWidth, configManager));
|
|
609
606
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
? { panelData: undefined, panelWidth: 0 }
|
|
613
|
-
: buildPanelCompositeData(
|
|
614
|
-
panelManager,
|
|
615
|
-
input,
|
|
616
|
-
shellLayout.panel?.width ?? 0,
|
|
617
|
-
shellLayout.panel?.height ?? vHeight,
|
|
618
|
-
);
|
|
607
|
+
const overlayContext = { input, conversation, commandRegistry, keybindingsManager: ctx.services.keybindingsManager, contextWindow };
|
|
608
|
+
viewport = applyConversationOverlays(viewport, { ...overlayContext, conversationWidth, viewportHeight: vHeight });
|
|
619
609
|
|
|
620
610
|
compositor.composite({
|
|
621
611
|
width, height,
|
|
622
612
|
header: shellHeaderLines,
|
|
623
613
|
viewport,
|
|
624
614
|
footer: shellFooterLines,
|
|
625
|
-
selection: onboardingOwnsScreen ? undefined : {
|
|
615
|
+
selection: onboardingOwnsScreen || viewFrame ? undefined : {
|
|
626
616
|
isCellSelected: (col, row) => selection.isCellSelected(col, row),
|
|
627
617
|
scrollTop,
|
|
628
618
|
lineCount: conversation.history.getLineCount(),
|
|
629
619
|
},
|
|
630
|
-
search: !onboardingOwnsScreen && input.searchManager.active ? {
|
|
620
|
+
search: !onboardingOwnsScreen && !viewFrame && input.searchManager.active ? {
|
|
631
621
|
manager: input.searchManager,
|
|
632
622
|
scrollTop,
|
|
633
623
|
viewportStartY: shellHeaderLines.length,
|
|
634
624
|
} : undefined,
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
});
|
|
625
|
+
layers: buildConversationLayers({ ...overlayContext, screenWidth: width, screenHeight: height, headerRows: shellHeaderLines.length, footerRows: shellFooterLines.length, permission: pendingPermission ? PermissionPromptUI.renderPromptModal(width, height, pendingPermission, pendingPermission, approvalBroker) : null }),
|
|
626
|
+
}); typeahead.framePainted(); // a startup modal counts as seen from its first painted frame
|
|
638
627
|
};
|
|
639
628
|
const renderScheduler = createRenderScheduler(renderNow, undefined, () => lifecycle.isTerminalRestored()); // coalescer; no frames after terminal restore
|
|
640
629
|
deferredRender.set(() => renderScheduler.schedule()); // from here on, render() actually schedules
|
|
641
630
|
const terminalOutputGuard = installFullScreenTerminalOutputGuard({ stdout, stderr: process.stderr, onCapture: (total) => { commandContext.session.runtime.terminalWritesIntercepted = total; render(); } });
|
|
642
631
|
|
|
643
632
|
setRenderRequest(() => renderScheduler.flushNow()); // bootstrap's 16ms coalescer composites via the (restore-gated) scheduler
|
|
644
|
-
setPanelFrameRequester(render); // live panels repaint when idle (a replay finding: fleet sat stale until keypress)
|
|
645
633
|
orchestratorRefs.requestRender = render;
|
|
646
634
|
commandContext.renderRequest = render;
|
|
635
|
+
// /status: what the footer rows used to show, from the same sources (shell/status-report.ts).
|
|
636
|
+
commandContext.describeStatus = () => {
|
|
637
|
+
const serving = providerRegistry.getCurrentModel();
|
|
638
|
+
const active = resolveActiveModelDisplay({ serving, configuredRegistryKey: configManager.get('provider.model') as string, configuredLabel: runtime.model, configuredProvider: runtime.provider, failover: failoverState.current() });
|
|
639
|
+
const spine = uiServices.platform.externalServices?.inspect();
|
|
640
|
+
const voice = voiceCaptureStatus();
|
|
641
|
+
const git = lastGitInfoRef.value;
|
|
642
|
+
return formatStatusReport({
|
|
643
|
+
workingDirectory: workingDir, branch: git?.branch, dirty: git?.dirty,
|
|
644
|
+
model: active.footerModel, provider: active.footerProvider, modelNote: active.divergenceNote,
|
|
645
|
+
permissionMode: configManager.get('permissions.mode') as string, toolCount: toolRegistry.list().length, notifyMode: modeManager.getHITLMode(),
|
|
646
|
+
usage: orchestrator.usage,
|
|
647
|
+
cost: statusCostText({ up: orchestrator.usage.input, down: orchestrator.usage.output, cacheRead: orchestrator.usage.cacheRead, cacheWrite: orchestrator.usage.cacheWrite, fleetCostUsd: footerFleetCost(() => ctx.services.processRegistry.query().nodes, true) }, active.footerModel),
|
|
648
|
+
contextTokens: orchestrator.lastInputTokens, contextWindow: providerRegistry.getKnownContextWindowForModel(serving) ?? 0, contextWindowSource: describeContextWindowSource(serving),
|
|
649
|
+
compactFraction: Math.min(1, Math.max(0, (configManager.get('behavior.autoCompactThreshold') as number) / 100)),
|
|
650
|
+
sessionSpine: spine?.sessionSpineActive && spine.sessionSpineStatus && spine.sessionSpineStatus !== 'unknown' ? spine.sessionSpineStatus : undefined,
|
|
651
|
+
webSurfaceUrl: configManager.get('web.enabled') ? resolveWebSurfaceUrl(configManager) : undefined,
|
|
652
|
+
autoApprove: isEffectiveDangerMode(configManager), keepAwake: powerChipSource.get().keepAwake,
|
|
653
|
+
microphone: voice && voiceCaptureRowVisible(voice) ? voiceCaptureDescription(voice) : null,
|
|
654
|
+
runningAgents: agentManager.list().filter((a) => a.status === 'running' || a.status === 'pending').length,
|
|
655
|
+
runningProcesses: processManager.list().filter((p) => !p.done).length,
|
|
656
|
+
});
|
|
657
|
+
};
|
|
647
658
|
wireShellUiOpeners({
|
|
648
659
|
commandContext,
|
|
649
660
|
input,
|
|
650
|
-
|
|
651
|
-
|
|
661
|
+
views: ctx.views,
|
|
662
|
+
viewPanelAdapter: ctx.services.panelManager,
|
|
652
663
|
configManager,
|
|
653
664
|
providerRegistry,
|
|
654
665
|
runtime,
|
|
@@ -667,7 +678,7 @@ async function main() {
|
|
|
667
678
|
render, trustPromptRef,
|
|
668
679
|
});
|
|
669
680
|
|
|
670
|
-
const { refreshGit, unsubs: turnUnsubs } = wireTurnEventHandlers({
|
|
681
|
+
const { refreshGit, unsubs: turnUnsubs, continueTurnAfterFailover } = wireTurnEventHandlers({
|
|
671
682
|
events: uiServices.events,
|
|
672
683
|
conversation,
|
|
673
684
|
runtime,
|
|
@@ -709,7 +720,7 @@ async function main() {
|
|
|
709
720
|
const streamResult: WireStreamEventMetricsResult = wireStreamEventMetrics({
|
|
710
721
|
events: uiServices.events, orchestrator, providerRegistry,
|
|
711
722
|
systemMessageRouter, render, metrics: streamMetrics,
|
|
712
|
-
providerOptimizer: ctx.services.providerOptimizer, costLookup: providerRegistry, retryTurn,
|
|
723
|
+
providerOptimizer: ctx.services.providerOptimizer, costLookup: providerRegistry, retryTurn, onFailoverRetry: continueTurnAfterFailover,
|
|
713
724
|
failoverState, getConfiguredRegistryKey: () => configManager.get('provider.model') as string | undefined,
|
|
714
725
|
// The REQUESTED level, through the one helper every remap site reads from.
|
|
715
726
|
getConfiguredReasoningEffort: () => requestedEffortLevel(configManager),
|
|
@@ -729,15 +740,16 @@ async function main() {
|
|
|
729
740
|
// --- Terminal setup ---
|
|
730
741
|
stdin.setRawMode(true); stdin.resume(); stdin.setEncoding('utf8');
|
|
731
742
|
allowTerminalWrite(() => stdout.write((cli.flags.noAltScreen ? '' : ALT_SCREEN_ENTER) + CLEAR_SCREEN + CURSOR_HIDE + MOUSE_ENABLE + KEYBOARD_EXT_ENABLE + PASTE_ENABLE + FOCUS_ENABLE));
|
|
732
|
-
// forced dark/light applies before first paint; auto (TTY only)
|
|
733
|
-
//
|
|
734
|
-
|
|
743
|
+
// forced dark/light applies before first paint; auto (TTY only) repaints once if the OSC 11 probe says light. probePalette adds OSC 10 + OSC 4;0..15
|
|
744
|
+
// and retries until the terminal answers (terminal-palette-reader.ts); the `system` theme re-themes when it arrives. filterInput strips replies from stdin.
|
|
745
|
+
registerThemeRefresh(() => conversation.clearLineCache()); // theme/mode change: cached transcript lines re-render in the new colours
|
|
746
|
+
const themeProbe = installBackgroundThemeProbe({ configManager, isTTY: Boolean(stdout.isTTY), writeQuery: (b) => allowTerminalWrite(() => stdout.write(b)), requestRepaint: () => { compositor.resetDiff(); render(); }, probePalette: true, forwardInput: (b) => routeInput(b), subscribeResize: (l) => { stdout.on('resize', l); } });
|
|
735
747
|
|
|
736
748
|
// continueRecovery lets --continue/bare --resume check the target session for a live crash snapshot newer than its store before resuming (see tui-startup.ts).
|
|
737
|
-
|
|
749
|
+
const typeaheadHooks = { onModalShown: () => typeahead.arm(), onFlowSettled: () => { const held = typeahead.release(); if (held.length > 0) routeInput(held); } };
|
|
750
|
+
applyInitialTuiCliState({ cli, input, commandRegistry, commandContext, shellPaths: ctx.services.shellPaths, surface: ctx.services.surface, render, continueRecovery: { sessionManager: ctx.services.sessionManager, runtime, conversation, writeLastSessionPointer, receipt: (line) => systemMessageRouter.userReceipt(line), typeahead: typeaheadHooks } });
|
|
738
751
|
|
|
739
|
-
|
|
740
|
-
const data = themeProbe.filterInput(raw); if (data.length === 0) return;
|
|
752
|
+
const routeInput = (data: string): void => {
|
|
741
753
|
const blocking = handleBlockingShellInput({
|
|
742
754
|
data, pendingPermission, render,
|
|
743
755
|
abortTurn: () => orchestrator.abort(),
|
|
@@ -760,7 +772,8 @@ async function main() {
|
|
|
760
772
|
}
|
|
761
773
|
|
|
762
774
|
input.feed(data);
|
|
763
|
-
}
|
|
775
|
+
};
|
|
776
|
+
stdin.on('data', (raw: string) => { const data = typeahead.filter(themeProbe.filterInput(raw)); if (data.length > 0) routeInput(data); });
|
|
764
777
|
process.on('SIGINT', sigintHandler); process.on('unhandledRejection', unhandledRejectionHandler); stdout.on('resize', resizeHandler);
|
|
765
778
|
|
|
766
779
|
// State restores happen ONLY when the user explicitly asks, a CLI flag
|
|
@@ -769,16 +782,13 @@ async function main() {
|
|
|
769
782
|
// deliberately no unconditional auto-restore here: a bare launch never
|
|
770
783
|
// loads a saved conversation on its own (owner ruling).
|
|
771
784
|
|
|
772
|
-
//
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
// Crash-recovery snapshot: asked, never assumed. Scheduled after the first
|
|
777
|
-
// frame so the question is drawn rather than posed at a blank terminal.
|
|
778
|
-
scheduleRecoveryOffer(buildRecoveryOfferWiring({
|
|
785
|
+
// Crash-recovery snapshot: asked, never assumed, and opened before the first frame so the composer is never live under it;
|
|
786
|
+
// keys typed before it was painted are held and replayed into the composer once it is answered (startup-typeahead-gate.ts).
|
|
787
|
+
void startRecoveryOffer(buildRecoveryOfferWiring({
|
|
779
788
|
surface: ctx.services.surface, sessionManager: ctx.services.sessionManager, runtime, conversation, commandContext,
|
|
780
|
-
writeLastSessionPointer, receipt: (line) => systemMessageRouter.userReceipt(line), render,
|
|
789
|
+
writeLastSessionPointer, receipt: (line) => systemMessageRouter.userReceipt(line), render, typeahead: typeaheadHooks,
|
|
781
790
|
}));
|
|
791
|
+
conversation.rebuildHistory(); render(); // initial render
|
|
782
792
|
|
|
783
793
|
// Auto-save to recovery file every 60s + multi-instance liveness-marker
|
|
784
794
|
// refresh, see runtime/recovery-autosave.ts.
|