@pellux/goodvibes-tui 0.18.20 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +154 -0
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +7 -3
- package/src/core/conversation-rendering.ts +22 -6
- package/src/core/orchestrator.ts +1 -1
- package/src/input/commands/diff-runtime.ts +6 -5
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +2 -2
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/session-content.ts +1 -1
- package/src/input/commands/session.ts +0 -1
- package/src/input/commands/shell-core.ts +3 -2
- package/src/input/commands/skills-runtime.ts +2 -2
- package/src/input/commands/subscription-runtime.ts +4 -4
- package/src/input/feed-context-factory.ts +236 -0
- package/src/input/handler-feed.ts +44 -6
- package/src/input/handler-shortcuts.ts +138 -125
- package/src/input/handler.ts +119 -119
- package/src/input/keybindings.ts +30 -0
- package/src/input/panel-integration-actions.ts +2 -1
- package/src/input/settings-modal-types.ts +60 -0
- package/src/input/settings-modal.ts +83 -65
- package/src/panels/agent-inspector-panel.ts +10 -9
- package/src/panels/agent-logs-panel.ts +26 -6
- package/src/panels/approval-panel.ts +55 -82
- package/src/panels/automation-control-panel.ts +120 -161
- package/src/panels/base-panel.ts +108 -3
- package/src/panels/communication-panel.ts +69 -107
- package/src/panels/context-visualizer-panel.ts +2 -0
- package/src/panels/control-plane-panel.ts +117 -172
- package/src/panels/diff-panel.ts +2 -0
- package/src/panels/file-explorer-panel.ts +51 -31
- package/src/panels/file-preview-panel.ts +57 -35
- package/src/panels/git-panel.ts +12 -13
- package/src/panels/hooks-panel.ts +103 -138
- package/src/panels/incident-review-panel.ts +59 -109
- package/src/panels/knowledge-panel.ts +75 -107
- package/src/panels/local-auth-panel.ts +77 -93
- package/src/panels/marketplace-panel.ts +51 -69
- package/src/panels/mcp-panel.ts +110 -155
- package/src/panels/memory-panel.ts +90 -158
- package/src/panels/ops-control-panel.ts +51 -85
- package/src/panels/orchestration-panel.ts +70 -51
- package/src/panels/panel-list-panel.ts +5 -4
- package/src/panels/panel-manager.ts +25 -2
- package/src/panels/plan-dashboard-panel.ts +2 -0
- package/src/panels/plugins-panel.ts +37 -60
- package/src/panels/polish.ts +51 -2
- package/src/panels/provider-accounts-panel.ts +1 -0
- package/src/panels/provider-health-panel.ts +6 -8
- package/src/panels/routes-panel.ts +91 -141
- package/src/panels/schedule-panel.ts +7 -6
- package/src/panels/scrollable-list-panel.ts +64 -16
- package/src/panels/security-panel.ts +118 -152
- package/src/panels/services-panel.ts +63 -105
- package/src/panels/session-browser-panel.ts +19 -18
- package/src/panels/settings-sync-panel.ts +79 -123
- package/src/panels/skills-panel.ts +114 -230
- package/src/panels/subscription-panel.ts +64 -86
- package/src/panels/system-messages-panel.ts +147 -141
- package/src/panels/tasks-panel.ts +130 -179
- package/src/panels/token-budget-panel.ts +2 -0
- package/src/panels/watchers-panel.ts +89 -137
- package/src/panels/worktree-panel.ts +1 -0
- package/src/panels/wrfc-panel.ts +2 -0
- package/src/renderer/agent-detail-modal.ts +2 -2
- package/src/renderer/ansi-sanitize.ts +76 -0
- package/src/renderer/buffer.ts +23 -1
- package/src/renderer/diff.ts +8 -0
- package/src/renderer/help-overlay.ts +48 -28
- package/src/renderer/markdown.ts +3 -145
- package/src/renderer/settings-modal-helpers.ts +27 -0
- package/src/renderer/settings-modal.ts +18 -1
- package/src/renderer/status-glyphs.ts +21 -0
- package/src/renderer/status-token.ts +4 -8
- package/src/renderer/tool-call.ts +4 -3
- package/src/runtime/bootstrap-core.ts +1 -1
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap.ts +7 -8
- package/src/runtime/diagnostics/panels/policy.ts +2 -1
- package/src/shell/ui-openers.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Line } from '../types/grid.ts';
|
|
2
2
|
import { createEmptyLine } from '../types/grid.ts';
|
|
3
|
-
import {
|
|
3
|
+
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
4
4
|
import type { RuntimeTask, TaskLifecycleState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/tasks';
|
|
5
5
|
import type { ManagedWorktreeMeta } from '@pellux/goodvibes-sdk/platform/runtime/worktree/registry';
|
|
6
6
|
import type { UiReadModel, UiTasksSnapshot, UiWorktreeSnapshot } from '../runtime/ui-read-models.ts';
|
|
@@ -13,8 +13,6 @@ import {
|
|
|
13
13
|
buildSummaryBlock,
|
|
14
14
|
buildPanelWorkspace,
|
|
15
15
|
DEFAULT_PANEL_PALETTE,
|
|
16
|
-
resolvePrimaryScrollableSection,
|
|
17
|
-
type PanelWorkspaceSection,
|
|
18
16
|
} from './polish.ts';
|
|
19
17
|
|
|
20
18
|
const C = {
|
|
@@ -148,18 +146,17 @@ function reviewTaskWorktreeAttachments(
|
|
|
148
146
|
});
|
|
149
147
|
}
|
|
150
148
|
|
|
151
|
-
export class TasksPanel extends
|
|
149
|
+
export class TasksPanel extends ScrollableListPanel<RuntimeTask> {
|
|
152
150
|
private readonly readModel?: UiReadModel<UiTasksSnapshot>;
|
|
153
151
|
private readonly worktrees?: UiReadModel<UiWorktreeSnapshot>;
|
|
154
152
|
private readonly unsubscribers: readonly (() => void)[];
|
|
155
|
-
private selectedIndex = 0;
|
|
156
|
-
private scrollOffset = 0;
|
|
157
153
|
|
|
158
154
|
public constructor(
|
|
159
155
|
readModel: UiReadModel<UiTasksSnapshot> | undefined,
|
|
160
156
|
worktrees?: UiReadModel<UiWorktreeSnapshot>,
|
|
161
157
|
) {
|
|
162
158
|
super('tasks', 'Tasks', 'J', 'monitoring');
|
|
159
|
+
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
163
160
|
this.readModel = readModel;
|
|
164
161
|
this.worktrees = worktrees;
|
|
165
162
|
this.unsubscribers = [
|
|
@@ -177,39 +174,46 @@ export class TasksPanel extends BasePanel {
|
|
|
177
174
|
for (const unsubscribe of this.unsubscribers) unsubscribe();
|
|
178
175
|
}
|
|
179
176
|
|
|
177
|
+
protected override getPalette() { return C; }
|
|
178
|
+
protected override getEmptyStateMessage() { return ' No tasks recorded yet.'; }
|
|
179
|
+
protected override getEmptyStateActions() {
|
|
180
|
+
return [
|
|
181
|
+
{ command: '/tasks create', summary: 'create a tracked task from the shell' },
|
|
182
|
+
{ command: '/orchestration', summary: 'review graph-native task execution and WRFC flows' },
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
protected getItems(): readonly RuntimeTask[] {
|
|
187
|
+
if (!this.readModel) return [];
|
|
188
|
+
return sortTasks([...this.readModel.getSnapshot().tasks]);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
protected renderItem(task: RuntimeTask, index: number, selected: boolean, width: number): Line {
|
|
192
|
+
return buildPanelListRow(width, [
|
|
193
|
+
{ text: task.status.padEnd(10), fg: statusColor(task.status) },
|
|
194
|
+
{ text: ` ${kindLabel(task.kind).padEnd(12)}`, fg: C.value },
|
|
195
|
+
{ text: ` ${task.id.slice(0, 8)} `, fg: C.dim },
|
|
196
|
+
{ text: task.title.slice(0, Math.max(0, width - 37)), fg: C.value },
|
|
197
|
+
], C, { selected });
|
|
198
|
+
}
|
|
199
|
+
|
|
180
200
|
public handleInput(key: string): boolean {
|
|
181
|
-
const tasks = this._tasks();
|
|
182
|
-
if (tasks.length === 0) return false;
|
|
183
|
-
if (key === 'up' || key === 'k') {
|
|
184
|
-
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
185
|
-
this.markDirty();
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
if (key === 'down' || key === 'j') {
|
|
189
|
-
this.selectedIndex = Math.min(tasks.length - 1, this.selectedIndex + 1);
|
|
190
|
-
this.markDirty();
|
|
191
|
-
return true;
|
|
192
|
-
}
|
|
193
201
|
if (key === 'home') {
|
|
194
202
|
this.selectedIndex = 0;
|
|
195
203
|
this.markDirty();
|
|
196
204
|
return true;
|
|
197
205
|
}
|
|
198
206
|
if (key === 'end') {
|
|
199
|
-
|
|
207
|
+
const tasks = this.getItems();
|
|
208
|
+
this.selectedIndex = Math.max(0, tasks.length - 1);
|
|
200
209
|
this.markDirty();
|
|
201
210
|
return true;
|
|
202
211
|
}
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private _tasks(): RuntimeTask[] {
|
|
207
|
-
if (!this.readModel) return [];
|
|
208
|
-
return sortTasks([...this.readModel.getSnapshot().tasks]);
|
|
212
|
+
return super.handleInput(key);
|
|
209
213
|
}
|
|
210
214
|
|
|
211
215
|
public render(width: number, height: number): Line[] {
|
|
212
|
-
this.
|
|
216
|
+
this.clampSelection();
|
|
213
217
|
const intro = 'Live task lifecycle, ownership, retries, and result/error details across runtime execution domains.';
|
|
214
218
|
const footerLines = [buildPanelLine(width, [[' Up/Down move Home/End jump', C.dim]])];
|
|
215
219
|
|
|
@@ -232,34 +236,7 @@ export class TasksPanel extends BasePanel {
|
|
|
232
236
|
return workspace;
|
|
233
237
|
}
|
|
234
238
|
|
|
235
|
-
const tasks = this.
|
|
236
|
-
if (tasks.length === 0) {
|
|
237
|
-
const workspace = buildPanelWorkspace(width, height, {
|
|
238
|
-
title: 'Task Control Room',
|
|
239
|
-
intro,
|
|
240
|
-
sections: [{
|
|
241
|
-
title: 'Overview',
|
|
242
|
-
lines: [
|
|
243
|
-
buildPanelLine(width, [[' queued:0 running:0 blocked:0 failed:0 completed:0', C.dim]]),
|
|
244
|
-
...buildEmptyState(
|
|
245
|
-
width,
|
|
246
|
-
' No tasks recorded yet.',
|
|
247
|
-
'Tasks will appear here as exec, agent, ACP, scheduler, daemon, MCP, plugin, and integration work starts.',
|
|
248
|
-
[
|
|
249
|
-
{ command: '/tasks create', summary: 'create a tracked task from the shell' },
|
|
250
|
-
{ command: '/orchestration', summary: 'review graph-native task execution and WRFC flows' },
|
|
251
|
-
],
|
|
252
|
-
C,
|
|
253
|
-
),
|
|
254
|
-
],
|
|
255
|
-
}],
|
|
256
|
-
palette: C,
|
|
257
|
-
});
|
|
258
|
-
while (workspace.length < height) workspace.push(createEmptyLine(width));
|
|
259
|
-
return workspace;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
this.selectedIndex = Math.min(this.selectedIndex, tasks.length - 1);
|
|
239
|
+
const tasks = this.getItems();
|
|
263
240
|
const counts = STATUS_ORDER.map((status) => ({
|
|
264
241
|
status,
|
|
265
242
|
count: tasks.filter((task) => task.status === status).length,
|
|
@@ -270,7 +247,6 @@ export class TasksPanel extends BasePanel {
|
|
|
270
247
|
const queuedCount = counts.find((entry) => entry.status === 'queued')?.count ?? 0;
|
|
271
248
|
const completedCount = counts.find((entry) => entry.status === 'completed')?.count ?? 0;
|
|
272
249
|
|
|
273
|
-
const selected = tasks[this.selectedIndex]!;
|
|
274
250
|
const postureLines: Line[] = [
|
|
275
251
|
buildPanelLine(width, [
|
|
276
252
|
[' queued ', C.label],
|
|
@@ -284,7 +260,11 @@ export class TasksPanel extends BasePanel {
|
|
|
284
260
|
[' completed ', C.label],
|
|
285
261
|
[String(completedCount), completedCount > 0 ? C.completed : C.dim],
|
|
286
262
|
]),
|
|
287
|
-
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
const selected = tasks[this.selectedIndex];
|
|
266
|
+
if (selected) {
|
|
267
|
+
postureLines.push(buildPanelLine(width, [
|
|
288
268
|
[' selected ', C.label],
|
|
289
269
|
[selected.id, C.info],
|
|
290
270
|
[' status ', C.label],
|
|
@@ -293,156 +273,127 @@ export class TasksPanel extends BasePanel {
|
|
|
293
273
|
[selected.kind, C.value],
|
|
294
274
|
[' owner ', C.label],
|
|
295
275
|
[selected.owner.slice(0, Math.max(0, width - 46)), C.dim],
|
|
296
|
-
])
|
|
276
|
+
]));
|
|
277
|
+
}
|
|
278
|
+
postureLines.push(
|
|
297
279
|
buildGuidanceLine(width, '/teamwork review', 'inspect task-family posture, archetype metadata, and recovery options for active work', C),
|
|
298
280
|
buildGuidanceLine(width, '/worktree task <task-id>', 'review worktree ownership, restore, and merge posture for the selected task', C),
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const detailRows: Line[] = [
|
|
302
|
-
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const detailRows: Line[] = [];
|
|
284
|
+
if (selected) {
|
|
285
|
+
const descriptor = selected.description ? parseTaskDescriptor(selected.description) : null;
|
|
286
|
+
detailRows.push(buildPanelLine(width, [
|
|
303
287
|
[' Title: ', C.label],
|
|
304
288
|
[selected.title, C.value],
|
|
305
289
|
[' Status: ', C.label],
|
|
306
290
|
[selected.status, statusColor(selected.status)],
|
|
307
291
|
[' Kind: ', C.label],
|
|
308
292
|
[selected.kind, C.value],
|
|
309
|
-
])
|
|
310
|
-
buildPanelLine(width, [
|
|
293
|
+
]));
|
|
294
|
+
detailRows.push(buildPanelLine(width, [
|
|
311
295
|
[' Owner: ', C.label],
|
|
312
296
|
[selected.owner, C.value],
|
|
313
297
|
[' Cancellable: ', C.label],
|
|
314
298
|
[selected.cancellable ? 'yes' : 'no', selected.cancellable ? C.running : C.failed],
|
|
315
299
|
[' Queue: ', C.label],
|
|
316
300
|
[formatWhen(selected.queuedAt), C.dim],
|
|
317
|
-
])
|
|
318
|
-
buildPanelLine(width, [
|
|
301
|
+
]));
|
|
302
|
+
detailRows.push(buildPanelLine(width, [
|
|
319
303
|
[' Started: ', C.label],
|
|
320
304
|
[formatWhen(selected.startedAt), C.dim],
|
|
321
305
|
[' Ended: ', C.label],
|
|
322
306
|
[formatWhen(selected.endedAt), C.dim],
|
|
323
307
|
[' Duration: ', C.label],
|
|
324
308
|
[formatDuration(selected.startedAt, selected.endedAt), C.dim],
|
|
325
|
-
]),
|
|
326
|
-
];
|
|
327
|
-
if (descriptor?.mode || descriptor?.family || descriptor?.source) {
|
|
328
|
-
detailRows.push(buildPanelLine(width, [
|
|
329
|
-
[' Mode: ', C.label],
|
|
330
|
-
[descriptor?.mode ?? 'n/a', C.value],
|
|
331
|
-
[' Family: ', C.label],
|
|
332
|
-
[descriptor?.family ?? 'n/a', C.info],
|
|
333
|
-
[' Source: ', C.label],
|
|
334
|
-
[descriptor?.source ?? 'builtin/runtime', C.dim],
|
|
335
|
-
]));
|
|
336
|
-
}
|
|
337
|
-
if (descriptor?.reviewMode || descriptor?.executionProtocol || descriptor?.template) {
|
|
338
|
-
detailRows.push(buildPanelLine(width, [
|
|
339
|
-
[' Review: ', C.label],
|
|
340
|
-
[descriptor?.reviewMode ?? 'n/a', C.value],
|
|
341
|
-
[' Protocol: ', C.label],
|
|
342
|
-
[descriptor?.executionProtocol ?? 'n/a', C.value],
|
|
343
|
-
[' Template: ', C.label],
|
|
344
|
-
[descriptor?.template ?? 'n/a', C.dim],
|
|
345
|
-
]));
|
|
346
|
-
}
|
|
347
|
-
if (selected.correlationId || selected.turnId) {
|
|
348
|
-
detailRows.push(buildPanelLine(width, [
|
|
349
|
-
[' Correlation: ', C.label],
|
|
350
|
-
[selected.correlationId ?? 'n/a', C.dim],
|
|
351
|
-
[' Turn: ', C.label],
|
|
352
|
-
[selected.turnId ?? 'n/a', C.dim],
|
|
353
|
-
]));
|
|
354
|
-
}
|
|
355
|
-
if (selected.parentTaskId || selected.childTaskIds.length > 0) {
|
|
356
|
-
detailRows.push(buildPanelLine(width, [
|
|
357
|
-
[' Parent: ', C.label],
|
|
358
|
-
[selected.parentTaskId ?? 'none', C.dim],
|
|
359
|
-
[' Children: ', C.label],
|
|
360
|
-
[selected.childTaskIds.length > 0 ? selected.childTaskIds.join(', ') : 'none', C.dim],
|
|
361
|
-
]));
|
|
362
|
-
}
|
|
363
|
-
const attachedWorktrees = reviewTaskWorktreeAttachments(selected.id, this.worktrees);
|
|
364
|
-
if (attachedWorktrees.total > 0) {
|
|
365
|
-
detailRows.push(buildPanelLine(width, [
|
|
366
|
-
[' Worktrees: ', C.label],
|
|
367
|
-
[`${attachedWorktrees.total} tracked`, C.info],
|
|
368
|
-
[' Active: ', C.label],
|
|
369
|
-
[String(attachedWorktrees.active), attachedWorktrees.active > 0 ? C.running : C.dim],
|
|
370
|
-
[' Paused: ', C.label],
|
|
371
|
-
[String(attachedWorktrees.paused), attachedWorktrees.paused > 0 ? C.blocked : C.dim],
|
|
372
309
|
]));
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
310
|
+
if (descriptor?.mode || descriptor?.family || descriptor?.source) {
|
|
311
|
+
detailRows.push(buildPanelLine(width, [
|
|
312
|
+
[' Mode: ', C.label],
|
|
313
|
+
[descriptor?.mode ?? 'n/a', C.value],
|
|
314
|
+
[' Family: ', C.label],
|
|
315
|
+
[descriptor?.family ?? 'n/a', C.info],
|
|
316
|
+
[' Source: ', C.label],
|
|
317
|
+
[descriptor?.source ?? 'builtin/runtime', C.dim],
|
|
318
|
+
]));
|
|
319
|
+
}
|
|
320
|
+
if (descriptor?.reviewMode || descriptor?.executionProtocol || descriptor?.template) {
|
|
321
|
+
detailRows.push(buildPanelLine(width, [
|
|
322
|
+
[' Review: ', C.label],
|
|
323
|
+
[descriptor?.reviewMode ?? 'n/a', C.value],
|
|
324
|
+
[' Protocol: ', C.label],
|
|
325
|
+
[descriptor?.executionProtocol ?? 'n/a', C.value],
|
|
326
|
+
[' Template: ', C.label],
|
|
327
|
+
[descriptor?.template ?? 'n/a', C.dim],
|
|
328
|
+
]));
|
|
329
|
+
}
|
|
330
|
+
if (selected.correlationId || selected.turnId) {
|
|
331
|
+
detailRows.push(buildPanelLine(width, [
|
|
332
|
+
[' Correlation: ', C.label],
|
|
333
|
+
[selected.correlationId ?? 'n/a', C.dim],
|
|
334
|
+
[' Turn: ', C.label],
|
|
335
|
+
[selected.turnId ?? 'n/a', C.dim],
|
|
336
|
+
]));
|
|
337
|
+
}
|
|
338
|
+
if (selected.parentTaskId || selected.childTaskIds.length > 0) {
|
|
339
|
+
detailRows.push(buildPanelLine(width, [
|
|
340
|
+
[' Parent: ', C.label],
|
|
341
|
+
[selected.parentTaskId ?? 'none', C.dim],
|
|
342
|
+
[' Children: ', C.label],
|
|
343
|
+
[selected.childTaskIds.length > 0 ? selected.childTaskIds.join(', ') : 'none', C.dim],
|
|
344
|
+
]));
|
|
345
|
+
}
|
|
346
|
+
const attachedWorktrees = reviewTaskWorktreeAttachments(selected.id, this.worktrees);
|
|
347
|
+
if (attachedWorktrees.total > 0) {
|
|
348
|
+
detailRows.push(buildPanelLine(width, [
|
|
349
|
+
[' Worktrees: ', C.label],
|
|
350
|
+
[`${attachedWorktrees.total} tracked`, C.info],
|
|
351
|
+
[' Active: ', C.label],
|
|
352
|
+
[String(attachedWorktrees.active), attachedWorktrees.active > 0 ? C.running : C.dim],
|
|
353
|
+
[' Paused: ', C.label],
|
|
354
|
+
[String(attachedWorktrees.paused), attachedWorktrees.paused > 0 ? C.blocked : C.dim],
|
|
355
|
+
]));
|
|
378
356
|
detailRows.push(buildPanelLine(width, [[
|
|
379
|
-
` ${
|
|
380
|
-
|
|
357
|
+
` Next: /worktree task ${selected.id} /worktree recover task ${selected.id}`,
|
|
358
|
+
C.dim,
|
|
381
359
|
]]));
|
|
360
|
+
for (const record of attachedWorktrees.records.slice(0, 2)) {
|
|
361
|
+
detailRows.push(buildPanelLine(width, [[
|
|
362
|
+
` ${record.state.padEnd(15)} ${record.path}`.slice(0, Math.max(0, width - 2)),
|
|
363
|
+
record.state === 'active' ? C.running : record.state === 'paused' ? C.blocked : C.dim,
|
|
364
|
+
]]));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (selected.retryPolicy) {
|
|
368
|
+
detailRows.push(buildPanelLine(width, [
|
|
369
|
+
[' Retry: ', C.label],
|
|
370
|
+
[`${selected.retryPolicy.currentAttempt}/${selected.retryPolicy.maxAttempts} ${selected.retryPolicy.backoff}`, C.value],
|
|
371
|
+
]));
|
|
372
|
+
}
|
|
373
|
+
if (selected.error) {
|
|
374
|
+
detailRows.push(buildPanelLine(width, [
|
|
375
|
+
[' Error: ', C.label],
|
|
376
|
+
[selected.error.slice(0, Math.max(0, width - 10)), C.failed],
|
|
377
|
+
]));
|
|
378
|
+
}
|
|
379
|
+
if (selected.result !== undefined) {
|
|
380
|
+
const resultText = safeJson(selected.result);
|
|
381
|
+
detailRows.push(buildPanelLine(width, [
|
|
382
|
+
[' Result: ', C.label],
|
|
383
|
+
[resultText.slice(0, Math.max(0, width - 11)), C.dim],
|
|
384
|
+
]));
|
|
382
385
|
}
|
|
383
386
|
}
|
|
384
|
-
if (selected.retryPolicy) {
|
|
385
|
-
detailRows.push(buildPanelLine(width, [
|
|
386
|
-
[' Retry: ', C.label],
|
|
387
|
-
[`${selected.retryPolicy.currentAttempt}/${selected.retryPolicy.maxAttempts} ${selected.retryPolicy.backoff}`, C.value],
|
|
388
|
-
]));
|
|
389
|
-
}
|
|
390
|
-
if (selected.error) {
|
|
391
|
-
detailRows.push(buildPanelLine(width, [
|
|
392
|
-
[' Error: ', C.label],
|
|
393
|
-
[selected.error.slice(0, Math.max(0, width - 10)), C.failed],
|
|
394
|
-
]));
|
|
395
|
-
}
|
|
396
|
-
if (selected.result !== undefined) {
|
|
397
|
-
const resultText = safeJson(selected.result);
|
|
398
|
-
detailRows.push(buildPanelLine(width, [
|
|
399
|
-
[' Result: ', C.label],
|
|
400
|
-
[resultText.slice(0, Math.max(0, width - 11)), C.dim],
|
|
401
|
-
]));
|
|
402
|
-
}
|
|
403
|
-
const postureSection: PanelWorkspaceSection = { lines: buildSummaryBlock(width, 'Task posture', postureLines, C) };
|
|
404
|
-
const selectedSection: PanelWorkspaceSection = { lines: buildDetailBlock(width, 'Selected task', detailRows, C) };
|
|
405
|
-
const rawTaskLines: Line[] = [];
|
|
406
|
-
for (let absolute = 0; absolute < tasks.length; absolute++) {
|
|
407
|
-
const task = tasks[absolute]!;
|
|
408
|
-
rawTaskLines.push(buildPanelListRow(width, [
|
|
409
|
-
{ text: task.status.padEnd(10), fg: statusColor(task.status) },
|
|
410
|
-
{ text: ` ${kindLabel(task.kind).padEnd(12)}`, fg: C.value },
|
|
411
|
-
{ text: ` ${task.id.slice(0, 8)} `, fg: C.dim },
|
|
412
|
-
{ text: task.title.slice(0, Math.max(0, width - 37)), fg: C.value },
|
|
413
|
-
], C, { selected: absolute === this.selectedIndex }));
|
|
414
|
-
}
|
|
415
|
-
const resolvedTasksSection = resolvePrimaryScrollableSection(width, height, {
|
|
416
|
-
intro,
|
|
417
|
-
footerLines,
|
|
418
|
-
palette: C,
|
|
419
|
-
beforeSections: [postureSection],
|
|
420
|
-
section: {
|
|
421
|
-
title: 'Tasks',
|
|
422
|
-
scrollableLines: rawTaskLines,
|
|
423
|
-
selectedIndex: this.selectedIndex,
|
|
424
|
-
scrollOffset: this.scrollOffset,
|
|
425
|
-
guardRows: 1,
|
|
426
|
-
minRows: 4,
|
|
427
|
-
appendWindowSummary: { dimColor: C.dim },
|
|
428
|
-
},
|
|
429
|
-
afterSections: [selectedSection],
|
|
430
|
-
});
|
|
431
|
-
this.scrollOffset = resolvedTasksSection.scrollOffset;
|
|
432
387
|
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
selectedSection,
|
|
437
|
-
];
|
|
438
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
388
|
+
const headerLines: Line[] = buildSummaryBlock(width, 'Task posture', postureLines, C);
|
|
389
|
+
|
|
390
|
+
return this.renderList(width, height, {
|
|
439
391
|
title: 'Task Control Room',
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
392
|
+
header: headerLines,
|
|
393
|
+
footer: [
|
|
394
|
+
...buildDetailBlock(width, 'Selected task', detailRows, C),
|
|
395
|
+
...footerLines,
|
|
396
|
+
],
|
|
444
397
|
});
|
|
445
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
446
|
-
return lines.slice(0, height);
|
|
447
398
|
}
|
|
448
399
|
}
|
|
@@ -199,6 +199,7 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
199
199
|
// ---------------------------------------------------------------------------
|
|
200
200
|
|
|
201
201
|
override render(width: number, height: number): Line[] {
|
|
202
|
+
return this.trackedRender(() => {
|
|
202
203
|
const sections: PanelWorkspaceSection[] = [];
|
|
203
204
|
|
|
204
205
|
if (this.contextWindow > 0) {
|
|
@@ -258,6 +259,7 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
258
259
|
sections,
|
|
259
260
|
palette: DEFAULT_PANEL_PALETTE,
|
|
260
261
|
});
|
|
262
|
+
});
|
|
261
263
|
}
|
|
262
264
|
|
|
263
265
|
private renderMaintenance(width: number): Line[] {
|