@pellux/goodvibes-tui 1.0.0 → 1.7.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 (209) hide show
  1. package/CHANGELOG.md +130 -127
  2. package/README.md +32 -14
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +2 -2
  5. package/src/cli/ensure-goodvibes-gitignore.ts +32 -0
  6. package/src/cli/entrypoint.ts +2 -0
  7. package/src/core/alert-gating.ts +67 -0
  8. package/src/core/approval-alert.ts +72 -0
  9. package/src/core/budget-breach-notifier.ts +106 -0
  10. package/src/core/conversation-rendering.ts +19 -0
  11. package/src/core/conversation.ts +58 -0
  12. package/src/core/focus-tracker.ts +41 -0
  13. package/src/core/long-task-notifier.ts +33 -6
  14. package/src/core/stream-event-wiring.ts +104 -2
  15. package/src/core/stream-stall-watchdog.ts +41 -17
  16. package/src/core/system-message-router.ts +37 -51
  17. package/src/core/turn-cancellation.ts +20 -0
  18. package/src/core/turn-event-wiring.ts +64 -3
  19. package/src/export/cost-utils.ts +126 -8
  20. package/src/input/command-registry.ts +54 -1
  21. package/src/input/commands/checkpoint-runtime.ts +280 -0
  22. package/src/input/commands/codebase-runtime.ts +192 -0
  23. package/src/input/commands/diff-runtime.ts +61 -30
  24. package/src/input/commands/eval.ts +1 -1
  25. package/src/input/commands/health-runtime.ts +1 -1
  26. package/src/input/commands/image-runtime.ts +112 -0
  27. package/src/input/commands/intelligence-runtime.ts +11 -1
  28. package/src/input/commands/local-auth-runtime.ts +4 -1
  29. package/src/input/commands/local-runtime.ts +9 -3
  30. package/src/input/commands/marketplace-runtime.ts +2 -2
  31. package/src/input/commands/memory.ts +6 -1
  32. package/src/input/commands/operator-panel-runtime.ts +40 -24
  33. package/src/input/commands/planning-runtime.ts +26 -3
  34. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  35. package/src/input/commands/plugin-runtime.ts +2 -2
  36. package/src/input/commands/policy-dispatch.ts +21 -0
  37. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  38. package/src/input/commands/qrcode-runtime.ts +3 -3
  39. package/src/input/commands/recall-review.ts +35 -0
  40. package/src/input/commands/remote-runtime.ts +3 -3
  41. package/src/input/commands/runtime-services.ts +54 -13
  42. package/src/input/commands/services-runtime.ts +1 -1
  43. package/src/input/commands/session-content.ts +12 -0
  44. package/src/input/commands/session-workflow.ts +19 -1
  45. package/src/input/commands/settings-sync-runtime.ts +1 -1
  46. package/src/input/commands/share-runtime.ts +9 -2
  47. package/src/input/commands/shell-core.ts +15 -7
  48. package/src/input/commands/skills-runtime.ts +2 -8
  49. package/src/input/commands/subscription-runtime.ts +2 -2
  50. package/src/input/commands/test-runtime.ts +277 -0
  51. package/src/input/commands/websearch-runtime.ts +101 -0
  52. package/src/input/commands/work-plan-runtime.ts +36 -10
  53. package/src/input/commands/workstream-runtime.ts +338 -0
  54. package/src/input/commands.ts +12 -0
  55. package/src/input/config-modal-types.ts +161 -0
  56. package/src/input/config-modal.ts +377 -0
  57. package/src/input/feed-context-factory.ts +7 -8
  58. package/src/input/handler-command-route.ts +27 -17
  59. package/src/input/handler-content-actions.ts +22 -8
  60. package/src/input/handler-feed-routes.ts +107 -7
  61. package/src/input/handler-feed.ts +49 -13
  62. package/src/input/handler-interactions.ts +3 -3
  63. package/src/input/handler-modal-routes.ts +65 -0
  64. package/src/input/handler-modal-stack.ts +3 -5
  65. package/src/input/handler-modal-token-routes.ts +16 -49
  66. package/src/input/handler-picker-routes.ts +11 -94
  67. package/src/input/handler-shortcuts.ts +24 -14
  68. package/src/input/handler-types.ts +3 -6
  69. package/src/input/handler-ui-state.ts +10 -22
  70. package/src/input/handler.ts +10 -28
  71. package/src/input/keybindings.ts +22 -8
  72. package/src/input/model-picker-types.ts +24 -6
  73. package/src/input/model-picker.ts +58 -0
  74. package/src/input/panel-integration-actions.ts +9 -170
  75. package/src/input/settings-modal-data.ts +95 -0
  76. package/src/input/settings-modal-mutations.ts +6 -4
  77. package/src/main.ts +41 -44
  78. package/src/panels/agent-inspector-shared.ts +35 -11
  79. package/src/panels/base-panel.ts +22 -1
  80. package/src/panels/builtin/agent.ts +21 -107
  81. package/src/panels/builtin/development.ts +16 -62
  82. package/src/panels/builtin/knowledge.ts +8 -32
  83. package/src/panels/builtin/operations.ts +84 -428
  84. package/src/panels/builtin/session.ts +21 -112
  85. package/src/panels/builtin/shared.ts +9 -43
  86. package/src/panels/builtin-modals.ts +218 -0
  87. package/src/panels/builtin-panels.ts +5 -0
  88. package/src/panels/cost-tracker-panel.ts +63 -14
  89. package/src/panels/diff-panel.ts +123 -60
  90. package/src/panels/eval-registry.ts +60 -0
  91. package/src/panels/fleet-panel.ts +800 -0
  92. package/src/panels/fleet-read-model.ts +477 -0
  93. package/src/panels/fleet-steer.ts +92 -0
  94. package/src/panels/fleet-stop.ts +125 -0
  95. package/src/panels/fleet-tabs.ts +230 -0
  96. package/src/panels/fleet-transcript.ts +356 -0
  97. package/src/panels/git-panel.ts +9 -9
  98. package/src/panels/index.ts +7 -31
  99. package/src/panels/local-auth-panel.ts +10 -0
  100. package/src/panels/modals/hooks-modal.ts +187 -0
  101. package/src/panels/modals/keybindings-modal.ts +151 -0
  102. package/src/panels/modals/knowledge-modal.ts +158 -0
  103. package/src/panels/modals/local-auth-modal.ts +132 -0
  104. package/src/panels/modals/marketplace-modal.ts +218 -0
  105. package/src/panels/modals/memory-modal.ts +180 -0
  106. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  107. package/src/panels/modals/modal-theme.ts +36 -0
  108. package/src/panels/modals/pairing-modal.ts +144 -0
  109. package/src/panels/modals/planning-modal.ts +282 -0
  110. package/src/panels/modals/plugins-modal.ts +174 -0
  111. package/src/panels/modals/policy-modal.ts +256 -0
  112. package/src/panels/modals/provider-health-modal.ts +136 -0
  113. package/src/panels/modals/remote-modal.ts +115 -0
  114. package/src/panels/modals/sandbox-modal.ts +150 -0
  115. package/src/panels/modals/security-modal.ts +217 -0
  116. package/src/panels/modals/services-modal.ts +179 -0
  117. package/src/panels/modals/settings-sync-modal.ts +142 -0
  118. package/src/panels/modals/skills-modal.ts +189 -0
  119. package/src/panels/modals/subscription-modal.ts +172 -0
  120. package/src/panels/modals/work-plan-modal.ts +149 -0
  121. package/src/panels/panel-confirm-overlay.ts +72 -0
  122. package/src/panels/panel-manager.ts +108 -5
  123. package/src/panels/project-planning-answer-actions.ts +4 -2
  124. package/src/panels/scrollable-list-panel.ts +9 -0
  125. package/src/panels/skills-panel.ts +7 -51
  126. package/src/panels/token-budget-panel.ts +5 -3
  127. package/src/panels/types.ts +26 -0
  128. package/src/permissions/hunk-selection.ts +179 -0
  129. package/src/permissions/prompt.ts +60 -4
  130. package/src/renderer/compaction-history-modal.ts +19 -3
  131. package/src/renderer/compaction-preview.ts +13 -2
  132. package/src/renderer/compaction-quality.ts +100 -0
  133. package/src/renderer/config-modal.ts +81 -0
  134. package/src/renderer/conversation-overlays.ts +10 -17
  135. package/src/renderer/fleet-tab-strip.ts +56 -0
  136. package/src/renderer/footer-tips.ts +10 -2
  137. package/src/renderer/git-status.ts +40 -0
  138. package/src/renderer/help-overlay.ts +2 -2
  139. package/src/renderer/model-workspace.ts +44 -1
  140. package/src/renderer/panel-workspace-bar.ts +8 -2
  141. package/src/renderer/shell-surface.ts +8 -1
  142. package/src/renderer/turn-injection.ts +114 -0
  143. package/src/renderer/ui-factory.ts +91 -7
  144. package/src/runtime/bootstrap-command-context.ts +24 -1
  145. package/src/runtime/bootstrap-command-parts.ts +36 -7
  146. package/src/runtime/bootstrap-core.ts +19 -4
  147. package/src/runtime/bootstrap-hook-bridge.ts +5 -0
  148. package/src/runtime/bootstrap-shell.ts +41 -17
  149. package/src/runtime/bootstrap.ts +11 -17
  150. package/src/runtime/code-index-services.ts +112 -0
  151. package/src/runtime/orchestrator-core-services.ts +49 -0
  152. package/src/runtime/process-lifecycle.ts +3 -1
  153. package/src/runtime/services.ts +91 -43
  154. package/src/runtime/ui-services.ts +8 -0
  155. package/src/runtime/workstream-services.ts +195 -0
  156. package/src/shell/blocking-input.ts +22 -1
  157. package/src/shell/ui-openers.ts +129 -17
  158. package/src/utils/format-duration.ts +8 -18
  159. package/src/utils/splash-lines.ts +1 -1
  160. package/src/version.ts +1 -1
  161. package/src/panels/agent-inspector-panel.ts +0 -786
  162. package/src/panels/approval-panel.ts +0 -252
  163. package/src/panels/automation-control-panel.ts +0 -479
  164. package/src/panels/cockpit-panel.ts +0 -481
  165. package/src/panels/cockpit-read-model.ts +0 -233
  166. package/src/panels/communication-panel.ts +0 -256
  167. package/src/panels/control-plane-panel.ts +0 -470
  168. package/src/panels/debug-panel.ts +0 -609
  169. package/src/panels/docs-panel.ts +0 -375
  170. package/src/panels/eval-panel.ts +0 -627
  171. package/src/panels/file-explorer-panel.ts +0 -664
  172. package/src/panels/file-preview-panel.ts +0 -517
  173. package/src/panels/hooks-panel.ts +0 -401
  174. package/src/panels/incident-review-panel.ts +0 -406
  175. package/src/panels/intelligence-panel.ts +0 -383
  176. package/src/panels/knowledge-graph-panel.ts +0 -506
  177. package/src/panels/marketplace-panel.ts +0 -399
  178. package/src/panels/memory-panel.ts +0 -558
  179. package/src/panels/ops-control-panel.ts +0 -329
  180. package/src/panels/ops-strategy-panel.ts +0 -321
  181. package/src/panels/orchestration-panel.ts +0 -465
  182. package/src/panels/panel-list-panel.ts +0 -557
  183. package/src/panels/plan-dashboard-panel.ts +0 -707
  184. package/src/panels/policy-panel.ts +0 -517
  185. package/src/panels/project-planning-panel.ts +0 -721
  186. package/src/panels/provider-health-panel.ts +0 -678
  187. package/src/panels/provider-health-tracker.ts +0 -306
  188. package/src/panels/provider-health-views.ts +0 -560
  189. package/src/panels/qr-panel.ts +0 -280
  190. package/src/panels/remote-panel.ts +0 -534
  191. package/src/panels/routes-panel.ts +0 -241
  192. package/src/panels/sandbox-panel.ts +0 -456
  193. package/src/panels/security-panel.ts +0 -447
  194. package/src/panels/services-panel.ts +0 -329
  195. package/src/panels/session-browser-panel.ts +0 -487
  196. package/src/panels/settings-sync-panel.ts +0 -398
  197. package/src/panels/subscription-panel.ts +0 -342
  198. package/src/panels/symbol-outline-panel.ts +0 -619
  199. package/src/panels/system-messages-panel.ts +0 -364
  200. package/src/panels/tasks-panel.ts +0 -608
  201. package/src/panels/thinking-panel.ts +0 -333
  202. package/src/panels/tool-inspector-panel.ts +0 -537
  203. package/src/panels/work-plan-panel.ts +0 -530
  204. package/src/panels/worktree-panel.ts +0 -360
  205. package/src/panels/wrfc-panel.ts +0 -790
  206. package/src/renderer/agent-detail-modal.ts +0 -465
  207. package/src/renderer/live-tail-modal.ts +0 -156
  208. package/src/renderer/process-modal.ts +0 -656
  209. 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
- }