@pellux/goodvibes-tui 1.1.0 → 1.9.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.
Files changed (267) hide show
  1. package/CHANGELOG.md +85 -21
  2. package/README.md +28 -18
  3. package/docs/foundation-artifacts/operator-contract.json +4045 -1763
  4. package/package.json +2 -2
  5. package/src/audio/spoken-turn-controller.ts +12 -2
  6. package/src/audio/spoken-turn-wiring.ts +2 -1
  7. package/src/cli/help.ts +8 -1
  8. package/src/cli/service-posture.ts +2 -1
  9. package/src/cli/status.ts +2 -1
  10. package/src/cli/surface-command.ts +2 -2
  11. package/src/core/alert-gating.ts +67 -0
  12. package/src/core/approval-alert.ts +72 -0
  13. package/src/core/budget-breach-notifier.ts +106 -0
  14. package/src/core/composer-state.ts +11 -3
  15. package/src/core/conversation-line-cache.ts +27 -3
  16. package/src/core/conversation-rendering.ts +90 -14
  17. package/src/core/conversation.ts +18 -0
  18. package/src/core/focus-tracker.ts +41 -0
  19. package/src/core/long-task-notifier.ts +33 -6
  20. package/src/core/stream-event-wiring.ts +20 -1
  21. package/src/core/system-message-noise.ts +87 -0
  22. package/src/core/system-message-router.ts +104 -51
  23. package/src/core/turn-cancellation.ts +25 -0
  24. package/src/core/turn-event-wiring.ts +72 -3
  25. package/src/daemon/cli.ts +29 -2
  26. package/src/daemon/handlers/register.ts +8 -1
  27. package/src/daemon/service-commands.ts +329 -0
  28. package/src/export/cost-utils.ts +36 -0
  29. package/src/input/autocomplete.ts +27 -1
  30. package/src/input/command-registry.ts +84 -5
  31. package/src/input/commands/checkpoint-runtime.ts +280 -0
  32. package/src/input/commands/codebase-runtime.ts +232 -0
  33. package/src/input/commands/config.ts +43 -3
  34. package/src/input/commands/eval.ts +1 -1
  35. package/src/input/commands/health-runtime.ts +10 -2
  36. package/src/input/commands/image-runtime.ts +112 -0
  37. package/src/input/commands/intelligence-runtime.ts +11 -1
  38. package/src/input/commands/local-auth-runtime.ts +4 -1
  39. package/src/input/commands/local-runtime.ts +9 -3
  40. package/src/input/commands/marketplace-runtime.ts +2 -2
  41. package/src/input/commands/memory.ts +73 -35
  42. package/src/input/commands/operator-panel-runtime.ts +71 -31
  43. package/src/input/commands/planning-runtime.ts +116 -4
  44. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  45. package/src/input/commands/plugin-runtime.ts +2 -2
  46. package/src/input/commands/policy-dispatch.ts +21 -0
  47. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  48. package/src/input/commands/qrcode-runtime.ts +26 -6
  49. package/src/input/commands/recall-review.ts +35 -0
  50. package/src/input/commands/remote-runtime-setup.ts +5 -3
  51. package/src/input/commands/remote-runtime.ts +3 -3
  52. package/src/input/commands/runtime-services.ts +54 -13
  53. package/src/input/commands/services-runtime.ts +1 -1
  54. package/src/input/commands/session-content.ts +20 -9
  55. package/src/input/commands/session-workflow.ts +16 -1
  56. package/src/input/commands/settings-sync-runtime.ts +16 -4
  57. package/src/input/commands/shell-core.ts +25 -8
  58. package/src/input/commands/skills-runtime.ts +2 -8
  59. package/src/input/commands/subscription-runtime.ts +2 -2
  60. package/src/input/commands/test-runtime.ts +277 -0
  61. package/src/input/commands/websearch-runtime.ts +101 -0
  62. package/src/input/commands/work-plan-runtime.ts +36 -10
  63. package/src/input/commands/workstream-runtime.ts +488 -0
  64. package/src/input/commands.ts +12 -0
  65. package/src/input/config-modal-types.ts +175 -0
  66. package/src/input/config-modal.ts +592 -0
  67. package/src/input/feed-context-factory.ts +10 -8
  68. package/src/input/handler-command-route.ts +37 -20
  69. package/src/input/handler-content-actions.ts +17 -2
  70. package/src/input/handler-feed-routes.ts +74 -114
  71. package/src/input/handler-feed.ts +79 -27
  72. package/src/input/handler-interactions.ts +1 -3
  73. package/src/input/handler-modal-routes.ts +136 -6
  74. package/src/input/handler-modal-stack.ts +14 -7
  75. package/src/input/handler-modal-token-routes.ts +16 -49
  76. package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
  77. package/src/input/handler-onboarding.ts +71 -59
  78. package/src/input/handler-picker-routes.ts +26 -102
  79. package/src/input/handler-shortcuts.ts +74 -14
  80. package/src/input/handler-types.ts +2 -6
  81. package/src/input/handler-ui-state.ts +10 -22
  82. package/src/input/handler.ts +12 -29
  83. package/src/input/keybindings.ts +23 -8
  84. package/src/input/model-picker-types.ts +24 -6
  85. package/src/input/model-picker.ts +77 -2
  86. package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
  87. package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
  88. package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
  89. package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
  90. package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
  91. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  92. package/src/input/panel-integration-actions.ts +9 -170
  93. package/src/input/panel-mouse-geometry.ts +97 -0
  94. package/src/input/panel-paste-flood-guard.ts +86 -0
  95. package/src/input/selection-modal.ts +11 -0
  96. package/src/input/session-picker-modal.ts +44 -4
  97. package/src/input/settings-modal-data.ts +146 -2
  98. package/src/input/settings-modal-mutations.ts +6 -4
  99. package/src/input/settings-modal-types.ts +4 -2
  100. package/src/main.ts +49 -51
  101. package/src/panels/agent-inspector-shared.ts +2 -1
  102. package/src/panels/base-panel.ts +22 -1
  103. package/src/panels/builtin/agent.ts +21 -107
  104. package/src/panels/builtin/development.ts +15 -61
  105. package/src/panels/builtin/knowledge.ts +8 -32
  106. package/src/panels/builtin/operations.ts +84 -428
  107. package/src/panels/builtin/session.ts +21 -112
  108. package/src/panels/builtin/shared.ts +18 -46
  109. package/src/panels/builtin-modals.ts +218 -0
  110. package/src/panels/builtin-panels.ts +5 -0
  111. package/src/panels/cost-tracker-panel.ts +36 -10
  112. package/src/panels/diff-panel.ts +12 -43
  113. package/src/panels/eval-registry.ts +60 -0
  114. package/src/panels/fleet-deep-link.ts +31 -0
  115. package/src/panels/fleet-panel-format.ts +62 -0
  116. package/src/panels/fleet-panel-worktree-detail.ts +48 -0
  117. package/src/panels/fleet-panel.ts +758 -0
  118. package/src/panels/fleet-read-model.ts +520 -0
  119. package/src/panels/fleet-steer.ts +125 -0
  120. package/src/panels/fleet-stop.ts +153 -0
  121. package/src/panels/fleet-tabs.ts +230 -0
  122. package/src/panels/fleet-transcript.ts +356 -0
  123. package/src/panels/index.ts +7 -31
  124. package/src/panels/modals/hooks-modal.ts +187 -0
  125. package/src/panels/modals/keybindings-modal.ts +166 -0
  126. package/src/panels/modals/knowledge-modal.ts +158 -0
  127. package/src/panels/modals/local-auth-modal.ts +132 -0
  128. package/src/panels/modals/marketplace-modal.ts +218 -0
  129. package/src/panels/modals/memory-modal.ts +180 -0
  130. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  131. package/src/panels/modals/modal-theme.ts +42 -0
  132. package/src/panels/modals/pairing-modal.ts +163 -0
  133. package/src/panels/modals/planning-modal.ts +304 -0
  134. package/src/panels/modals/plugins-modal.ts +174 -0
  135. package/src/panels/modals/policy-modal.ts +256 -0
  136. package/src/panels/modals/provider-health-modal.ts +136 -0
  137. package/src/panels/modals/remote-modal.ts +115 -0
  138. package/src/panels/modals/sandbox-modal.ts +150 -0
  139. package/src/panels/modals/security-modal.ts +217 -0
  140. package/src/panels/modals/services-modal.ts +179 -0
  141. package/src/panels/modals/settings-sync-modal.ts +142 -0
  142. package/src/panels/modals/skills-modal.ts +189 -0
  143. package/src/panels/modals/subscription-modal.ts +172 -0
  144. package/src/panels/modals/work-plan-modal.ts +177 -0
  145. package/src/panels/panel-confirm-overlay.ts +72 -0
  146. package/src/panels/panel-manager.ts +123 -9
  147. package/src/panels/polish-core.ts +38 -25
  148. package/src/panels/project-planning-answer-actions.ts +8 -11
  149. package/src/panels/skills-panel.ts +7 -51
  150. package/src/panels/types.ts +37 -0
  151. package/src/permissions/hunk-selection.ts +179 -0
  152. package/src/permissions/prompt.ts +216 -13
  153. package/src/renderer/autocomplete-overlay.ts +28 -2
  154. package/src/renderer/compaction-history-modal.ts +19 -3
  155. package/src/renderer/compaction-preview.ts +13 -2
  156. package/src/renderer/compaction-quality.ts +100 -0
  157. package/src/renderer/compositor.ts +2 -3
  158. package/src/renderer/config-modal.ts +95 -0
  159. package/src/renderer/conversation-overlays.ts +10 -17
  160. package/src/renderer/fleet-tab-strip.ts +56 -0
  161. package/src/renderer/footer-tips.ts +15 -3
  162. package/src/renderer/fullscreen-primitives.ts +32 -22
  163. package/src/renderer/git-status.ts +43 -1
  164. package/src/renderer/help-overlay.ts +2 -2
  165. package/src/renderer/layout.ts +0 -4
  166. package/src/renderer/markdown.ts +7 -3
  167. package/src/renderer/modal-factory.ts +25 -20
  168. package/src/renderer/model-workspace.ts +62 -4
  169. package/src/renderer/overlay-box.ts +21 -17
  170. package/src/renderer/panel-workspace-bar.ts +8 -2
  171. package/src/renderer/process-indicator.ts +14 -3
  172. package/src/renderer/selection-modal-overlay.ts +6 -1
  173. package/src/renderer/session-picker-modal.ts +196 -3
  174. package/src/renderer/settings-modal-helpers.ts +2 -0
  175. package/src/renderer/settings-modal.ts +7 -0
  176. package/src/renderer/shell-surface.ts +8 -1
  177. package/src/renderer/status-glyphs.ts +14 -15
  178. package/src/renderer/system-message.ts +15 -3
  179. package/src/renderer/terminal-bg-probe.ts +339 -0
  180. package/src/renderer/terminal-escapes.ts +20 -0
  181. package/src/renderer/theme-mode-config.ts +67 -0
  182. package/src/renderer/theme.ts +91 -1
  183. package/src/renderer/thinking.ts +11 -3
  184. package/src/renderer/tool-call.ts +15 -9
  185. package/src/renderer/tool-result-summary.ts +148 -0
  186. package/src/renderer/turn-injection.ts +133 -0
  187. package/src/renderer/ui-factory.ts +154 -85
  188. package/src/renderer/ui-primitives.ts +30 -129
  189. package/src/runtime/bootstrap-command-context.ts +27 -1
  190. package/src/runtime/bootstrap-command-parts.ts +41 -10
  191. package/src/runtime/bootstrap-core.ts +47 -15
  192. package/src/runtime/bootstrap-hook-bridge.ts +7 -0
  193. package/src/runtime/bootstrap-shell.ts +48 -17
  194. package/src/runtime/bootstrap.ts +129 -22
  195. package/src/runtime/code-index-services.ts +135 -0
  196. package/src/runtime/legacy-daemon-migration.ts +516 -0
  197. package/src/runtime/memory-fold.ts +26 -0
  198. package/src/runtime/onboarding/derivation.ts +7 -2
  199. package/src/runtime/onboarding/snapshot.ts +27 -1
  200. package/src/runtime/onboarding/types.ts +31 -1
  201. package/src/runtime/operator-token-cleanup.ts +82 -1
  202. package/src/runtime/orchestrator-core-services.ts +59 -0
  203. package/src/runtime/process-lifecycle.ts +3 -1
  204. package/src/runtime/resume-notice.ts +209 -0
  205. package/src/runtime/services.ts +98 -46
  206. package/src/runtime/session-inbound-inputs.ts +252 -0
  207. package/src/runtime/session-spine-transport.ts +64 -0
  208. package/src/runtime/terminal-output-guard.ts +15 -8
  209. package/src/runtime/ui-services.ts +27 -3
  210. package/src/runtime/workstream-services.ts +327 -0
  211. package/src/runtime/wrfc-persistence.ts +124 -17
  212. package/src/shell/blocking-input.ts +68 -4
  213. package/src/shell/recovery-input-helpers.ts +170 -1
  214. package/src/shell/ui-openers.ts +171 -26
  215. package/src/utils/format-duration.ts +8 -18
  216. package/src/utils/splash-lines.ts +1 -1
  217. package/src/utils/terminal-width.ts +52 -0
  218. package/src/version.ts +1 -1
  219. package/src/panels/agent-inspector-panel.ts +0 -786
  220. package/src/panels/approval-panel.ts +0 -252
  221. package/src/panels/automation-control-panel.ts +0 -479
  222. package/src/panels/cockpit-panel.ts +0 -481
  223. package/src/panels/cockpit-read-model.ts +0 -238
  224. package/src/panels/communication-panel.ts +0 -256
  225. package/src/panels/control-plane-panel.ts +0 -470
  226. package/src/panels/debug-panel.ts +0 -615
  227. package/src/panels/docs-panel.ts +0 -384
  228. package/src/panels/eval-panel.ts +0 -627
  229. package/src/panels/file-explorer-panel.ts +0 -673
  230. package/src/panels/file-preview-panel.ts +0 -517
  231. package/src/panels/hooks-panel.ts +0 -401
  232. package/src/panels/incident-review-panel.ts +0 -406
  233. package/src/panels/intelligence-panel.ts +0 -383
  234. package/src/panels/knowledge-graph-panel.ts +0 -515
  235. package/src/panels/marketplace-panel.ts +0 -399
  236. package/src/panels/memory-panel.ts +0 -558
  237. package/src/panels/ops-control-panel.ts +0 -329
  238. package/src/panels/ops-strategy-panel.ts +0 -321
  239. package/src/panels/orchestration-panel.ts +0 -465
  240. package/src/panels/panel-list-panel.ts +0 -566
  241. package/src/panels/plan-dashboard-panel.ts +0 -707
  242. package/src/panels/policy-panel.ts +0 -517
  243. package/src/panels/project-planning-panel.ts +0 -731
  244. package/src/panels/provider-health-panel.ts +0 -678
  245. package/src/panels/provider-health-tracker.ts +0 -310
  246. package/src/panels/provider-health-views.ts +0 -567
  247. package/src/panels/qr-panel.ts +0 -280
  248. package/src/panels/remote-panel.ts +0 -534
  249. package/src/panels/routes-panel.ts +0 -241
  250. package/src/panels/sandbox-panel.ts +0 -456
  251. package/src/panels/security-panel.ts +0 -447
  252. package/src/panels/services-panel.ts +0 -329
  253. package/src/panels/session-browser-panel.ts +0 -496
  254. package/src/panels/settings-sync-panel.ts +0 -398
  255. package/src/panels/subscription-panel.ts +0 -342
  256. package/src/panels/symbol-outline-panel.ts +0 -619
  257. package/src/panels/system-messages-panel.ts +0 -364
  258. package/src/panels/tasks-panel.ts +0 -608
  259. package/src/panels/thinking-panel.ts +0 -333
  260. package/src/panels/tool-inspector-panel.ts +0 -537
  261. package/src/panels/work-plan-panel.ts +0 -540
  262. package/src/panels/worktree-panel.ts +0 -360
  263. package/src/panels/wrfc-panel.ts +0 -790
  264. package/src/renderer/agent-detail-modal.ts +0 -466
  265. package/src/renderer/live-tail-modal.ts +0 -156
  266. package/src/renderer/process-modal.ts +0 -671
  267. package/src/renderer/qr-renderer.ts +0 -120
@@ -1,333 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // ThinkingPanel — streams model reasoning tokens in real-time.
3
- // ---------------------------------------------------------------------------
4
-
5
- import type { Line } from '../types/grid.ts';
6
- import { BasePanel } from './base-panel.ts';
7
- import type { TurnEvent } from '@/runtime/index.ts';
8
- import type { UiEventFeed } from '../runtime/ui-events.ts';
9
- import {
10
- buildEmptyState,
11
- buildPanelLine,
12
- buildStyledPanelLine,
13
- buildPanelWorkspace,
14
- resolveScrollablePanelSection,
15
- extendPalette,
16
- DEFAULT_PANEL_PALETTE,
17
- type PanelWorkspaceSection,
18
- } from './polish.ts';
19
- import { truncateDisplay, wrapText } from '../utils/terminal-width.ts';
20
-
21
- const C = extendPalette(DEFAULT_PANEL_PALETTE, {
22
- // Domain-specific reasoning tones (purples) and the active-stream cyan have no
23
- // clean shared equivalent; selection background maps to the shared selectBg.
24
- reasoningFg: '#aa88ff',
25
- activeFg: '#cc99ff',
26
- turnLabel: '#7766bb',
27
- activeLabel: '#00ffff',
28
- });
29
-
30
- interface ReasoningBlock {
31
- // Real turn id from the TURN_*/STREAM_* event envelope — joins this block
32
- // to the same turn's rows in TasksPanel and the tool inspector, which key
33
- // off the identical turnId string.
34
- turnId: string;
35
- // Distributed-tracing correlation id (EventEnvelope.traceId), when the
36
- // originating event carried one — same field RuntimeTask.correlationId
37
- // joins against.
38
- correlationId?: string;
39
- content: string;
40
- active: boolean; // true = currently streaming
41
- collapsed: boolean;
42
- startedAt: number;
43
- updatedAt: number;
44
- }
45
-
46
- function fmtClock(ts: number): string {
47
- const d = new Date(ts);
48
- const h = String(d.getHours()).padStart(2, '0');
49
- const m = String(d.getMinutes()).padStart(2, '0');
50
- const s = String(d.getSeconds()).padStart(2, '0');
51
- return `${h}:${m}:${s}`;
52
- }
53
-
54
- type FlatRow = { kind: 'header'; blockIndex: number; text: string } | { kind: 'content'; text: string };
55
-
56
- export class ThinkingPanel extends BasePanel {
57
- private blocks: ReasoningBlock[] = [];
58
- private unsubs: Array<() => void> = [];
59
- private cursorIndex = 0;
60
- private scrollOffset = 0;
61
- private autoScroll = true;
62
- private lastWidth = 80;
63
- private static readonly MAX_BLOCKS = 100;
64
-
65
- constructor(private readonly turnEvents: UiEventFeed<TurnEvent>) {
66
- super('thinking', 'Thinking', 'T', 'ai');
67
- this._attachBus();
68
- }
69
-
70
- override onActivate(): void {
71
- this.needsRender = true;
72
- }
73
-
74
- override onDeactivate(): void {
75
- super.onDeactivate();
76
- }
77
-
78
- override onDestroy(): void {
79
- this._detachBus();
80
- }
81
-
82
- handleInput(key: string): boolean {
83
- switch (key) {
84
- case 'up': this._move(-1); this.autoScroll = false; return true;
85
- case 'down': this._move(1); return true;
86
- case 'pageup': this._move(-10); this.autoScroll = false; return true;
87
- case 'pagedown': this._move(10); return true;
88
- case 'return': this._toggleCollapse(); return true;
89
- case 'g': this.autoScroll = true; this.markDirty(); return true;
90
- default: return false;
91
- }
92
- }
93
-
94
- render(width: number, height: number): Line[] {
95
- if (height <= 0 || width <= 0) return [];
96
- this.lastWidth = width;
97
-
98
- const hasActive = this.blocks.some(b => b.active);
99
- const flat = this._buildFlat(width);
100
- const title = hasActive ? ' Thinking [streaming]' : ` Thinking [${this.blocks.length} blocks]`;
101
- const footerLines = [
102
- buildPanelLine(width, [
103
- [' Up/Down', DEFAULT_PANEL_PALETTE.info],
104
- [' scroll', DEFAULT_PANEL_PALETTE.dim],
105
- [' Enter', DEFAULT_PANEL_PALETTE.info],
106
- [' collapse', DEFAULT_PANEL_PALETTE.dim],
107
- [' g', DEFAULT_PANEL_PALETTE.info],
108
- [' jump to end', DEFAULT_PANEL_PALETTE.dim],
109
- [this.autoScroll ? ' auto-scroll ON' : ' manual', this.autoScroll ? DEFAULT_PANEL_PALETTE.good : DEFAULT_PANEL_PALETTE.warn],
110
- ]),
111
- ];
112
-
113
- if (flat.length === 0) {
114
- return buildPanelWorkspace(width, height, {
115
- title,
116
- intro: 'Live reasoning blocks stream here while the model is actively thinking.',
117
- sections: [
118
- {
119
- title: 'Reasoning',
120
- lines: buildEmptyState(
121
- width,
122
- ' No reasoning content yet',
123
- 'When the model emits thinking or reasoning deltas during a turn, they accumulate here in expandable per-turn blocks.',
124
- [{ command: '/chat <prompt>', summary: 'start a turn with a reasoning-capable model to stream thinking here' }],
125
- DEFAULT_PANEL_PALETTE,
126
- ),
127
- },
128
- ],
129
- footerLines,
130
- palette: DEFAULT_PANEL_PALETTE,
131
- });
132
- }
133
-
134
- if (this.autoScroll) {
135
- this.cursorIndex = Math.max(0, flat.length - 1);
136
- }
137
-
138
- this.cursorIndex = Math.max(0, Math.min(this.cursorIndex, Math.max(0, flat.length - 1)));
139
-
140
- const summary: PanelWorkspaceSection = {
141
- title: 'Summary',
142
- lines: [
143
- buildPanelLine(width, [
144
- [' Blocks ', DEFAULT_PANEL_PALETTE.label],
145
- [String(this.blocks.length), DEFAULT_PANEL_PALETTE.value],
146
- [' Active ', DEFAULT_PANEL_PALETTE.label],
147
- [String(this.blocks.filter((block) => block.active).length), hasActive ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.dim],
148
- [' Mode ', DEFAULT_PANEL_PALETTE.label],
149
- [this.autoScroll ? 'auto-scroll' : 'manual', this.autoScroll ? DEFAULT_PANEL_PALETTE.good : DEFAULT_PANEL_PALETTE.info],
150
- ]),
151
- ],
152
- };
153
-
154
- // Resolve the reasoning block that owns the cursor row (walk back to its
155
- // header) so the detail block describes the actual turn, not just row kind.
156
- let selectedBlockIndex = -1;
157
- for (let i = Math.min(this.cursorIndex, flat.length - 1); i >= 0; i--) {
158
- const row = flat[i];
159
- if (row?.kind === 'header') { selectedBlockIndex = row.blockIndex; break; }
160
- }
161
- const selectedBlock = selectedBlockIndex >= 0 ? this.blocks[selectedBlockIndex] : undefined;
162
- const selectedSection: PanelWorkspaceSection = {
163
- title: 'Selected',
164
- lines: selectedBlock
165
- ? [
166
- buildPanelLine(width, [
167
- [' Turn ', DEFAULT_PANEL_PALETTE.label],
168
- [selectedBlock.turnId, DEFAULT_PANEL_PALETTE.value],
169
- [' State ', DEFAULT_PANEL_PALETTE.label],
170
- [selectedBlock.active ? 'streaming' : 'complete', selectedBlock.active ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.good],
171
- [' View ', DEFAULT_PANEL_PALETTE.label],
172
- [selectedBlock.collapsed ? 'collapsed' : 'expanded', DEFAULT_PANEL_PALETTE.info],
173
- [' Chars ', DEFAULT_PANEL_PALETTE.label],
174
- [String(selectedBlock.content.length), DEFAULT_PANEL_PALETTE.dim],
175
- ]),
176
- buildPanelLine(width, [
177
- [' Correlation ', DEFAULT_PANEL_PALETTE.label],
178
- [selectedBlock.correlationId ?? 'n/a', DEFAULT_PANEL_PALETTE.dim],
179
- [' Started ', DEFAULT_PANEL_PALETTE.label],
180
- [fmtClock(selectedBlock.startedAt), DEFAULT_PANEL_PALETTE.dim],
181
- [' Updated ', DEFAULT_PANEL_PALETTE.label],
182
- [fmtClock(selectedBlock.updatedAt), DEFAULT_PANEL_PALETTE.dim],
183
- ]),
184
- ]
185
- : [buildPanelLine(width, [[' No block selected', DEFAULT_PANEL_PALETTE.dim]])],
186
- };
187
-
188
- const reasoningSection = resolveScrollablePanelSection(width, height, {
189
- intro: 'Live reasoning blocks stream here while the model is actively thinking.',
190
- footerLines,
191
- palette: DEFAULT_PANEL_PALETTE,
192
- beforeSections: [summary],
193
- section: {
194
- title: 'Reasoning',
195
- scrollableLines: flat.map((row, index) => this._renderRow(width, row, index === this.cursorIndex)),
196
- selectedIndex: this.cursorIndex,
197
- scrollOffset: this.scrollOffset,
198
- minRows: 8,
199
- },
200
- afterSections: [selectedSection],
201
- });
202
- this.scrollOffset = reasoningSection.scrollOffset;
203
-
204
- return buildPanelWorkspace(width, height, {
205
- title,
206
- intro: 'Live reasoning blocks stream here while the model is actively thinking.',
207
- sections: [
208
- summary,
209
- reasoningSection.section,
210
- selectedSection,
211
- ],
212
- footerLines,
213
- palette: DEFAULT_PANEL_PALETTE,
214
- });
215
- }
216
-
217
- private _renderRow(width: number, row: FlatRow, isCursor: boolean): Line {
218
- const bg = isCursor ? C.selectBg : '';
219
- if (row.kind === 'header') {
220
- const indicator = this.blocks[row.blockIndex]?.collapsed ? '▸' : '▾';
221
- const active = this.blocks[row.blockIndex]?.active;
222
- const bullet = active ? '\u25cf ' : '\u25e6 ';
223
- return buildStyledPanelLine(width, [
224
- { text: truncateDisplay(` ${bullet}${row.text} ${indicator}`, width), fg: active ? C.activeLabel : C.turnLabel, bg, bold: true },
225
- ]);
226
- }
227
- return buildStyledPanelLine(width, [
228
- { text: ' ', fg: C.reasoningFg, bg },
229
- { text: truncateDisplay(row.text, Math.max(0, width - 2)), fg: isCursor ? C.activeFg : C.reasoningFg, bg },
230
- ]);
231
- }
232
-
233
- private _buildFlat(width: number): FlatRow[] {
234
- const rows: FlatRow[] = [];
235
- for (let i = 0; i < this.blocks.length; i++) {
236
- const block = this.blocks[i]!;
237
- const turnLabel = `Turn ${block.turnId} ${fmtClock(block.startedAt)}${block.active ? ' (streaming)' : ''}`;
238
- rows.push({ kind: 'header', blockIndex: i, text: turnLabel });
239
- if (!block.collapsed) {
240
- const wrapped = wrapText(block.content || '(empty)', Math.max(1, width - 2));
241
- for (const line of wrapped) {
242
- rows.push({ kind: 'content', text: line });
243
- }
244
- }
245
- }
246
- return rows;
247
- }
248
-
249
- private _toggleCollapse(): void {
250
- // Find the header row at or before cursorIndex
251
- const flat = this._buildFlat(this.lastWidth);
252
- for (let i = this.cursorIndex; i >= 0; i--) {
253
- const row = flat[i];
254
- if (row?.kind === 'header') {
255
- const block = this.blocks[row.blockIndex];
256
- if (block) {
257
- block.collapsed = !block.collapsed;
258
- this.markDirty();
259
- return;
260
- }
261
- }
262
- }
263
- }
264
-
265
- private _move(delta: number): void {
266
- const flat = this._buildFlat(this.lastWidth);
267
- if (flat.length === 0) return;
268
- this.cursorIndex = Math.max(0, Math.min(flat.length - 1, this.cursorIndex + delta));
269
- this.markDirty();
270
- }
271
-
272
- private _attachBus(): void {
273
- if (this.unsubs.length > 0) return;
274
-
275
- let currentBlock: ReasoningBlock | null = null;
276
-
277
- // onEnvelope (not on) so real turnId/traceId/timestamp — the same fields
278
- // TasksPanel and the tool inspector join their own rows on — stamp each
279
- // block instead of a locally-incrementing counter.
280
- this.unsubs.push(this.turnEvents.onEnvelope('STREAM_START', (envelope) => {
281
- const block: ReasoningBlock = {
282
- turnId: envelope.turnId ?? envelope.payload.turnId,
283
- correlationId: envelope.traceId,
284
- content: '',
285
- active: true,
286
- collapsed: false,
287
- startedAt: envelope.ts,
288
- updatedAt: envelope.ts,
289
- };
290
- // Cap block count to prevent unbounded growth
291
- if (this.blocks.length >= ThinkingPanel.MAX_BLOCKS) {
292
- this.blocks.shift();
293
- }
294
- this.blocks.push(block);
295
- currentBlock = block;
296
- this.autoScroll = true;
297
- this.markDirty();
298
- }));
299
-
300
- this.unsubs.push(this.turnEvents.onEnvelope('STREAM_DELTA', (envelope) => {
301
- const reasoning = envelope.payload.reasoning;
302
- if (reasoning && currentBlock) {
303
- currentBlock.content += reasoning;
304
- currentBlock.updatedAt = envelope.ts;
305
- this.autoScroll = true;
306
- this.markDirty();
307
- }
308
- }));
309
-
310
- this.unsubs.push(this.turnEvents.onEnvelope('STREAM_END', (envelope) => {
311
- if (currentBlock) {
312
- currentBlock.active = false;
313
- currentBlock.updatedAt = envelope.ts;
314
- currentBlock = null;
315
- this.markDirty();
316
- }
317
- }));
318
-
319
- this.unsubs.push(this.turnEvents.onEnvelope('TURN_COMPLETED', (envelope) => {
320
- if (currentBlock) {
321
- currentBlock.active = false;
322
- currentBlock.updatedAt = envelope.ts;
323
- currentBlock = null;
324
- this.markDirty();
325
- }
326
- }));
327
- }
328
-
329
- private _detachBus(): void {
330
- for (const unsub of this.unsubs) unsub();
331
- this.unsubs = [];
332
- }
333
- }