@pellux/goodvibes-tui 0.26.0 → 0.28.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 +40 -0
- package/README.md +12 -7
- package/docs/foundation-artifacts/operator-contract.json +4 -1
- package/package.json +2 -2
- package/src/cli/bundle-command.ts +2 -1
- package/src/cli/service-posture.ts +2 -1
- package/src/core/context-usage.ts +53 -0
- package/src/core/conversation-rendering.ts +2 -2
- package/src/core/conversation.ts +53 -8
- package/src/core/session-recovery.ts +26 -18
- package/src/core/system-message-router.ts +42 -26
- package/src/core/turn-event-wiring.ts +13 -16
- package/src/daemon/handlers/calendar/caldav-client.ts +2 -1
- package/src/daemon/handlers/inbox/index.ts +2 -1
- package/src/daemon/handlers/inbox/poller.ts +2 -1
- package/src/daemon/handlers/inbox/providers/discord.ts +2 -1
- package/src/daemon/handlers/inbox/providers/email.ts +2 -1
- package/src/daemon/handlers/inbox/providers/route-util.ts +2 -1
- package/src/daemon/handlers/inbox/providers/slack.ts +2 -1
- package/src/daemon/handlers/triage/integration.ts +2 -1
- package/src/daemon/handlers/triage/pipeline.ts +2 -1
- package/src/daemon/handlers/triage/tagger/discord.ts +2 -1
- package/src/daemon/handlers/triage/tagger/imap.ts +4 -3
- package/src/export/gist-uploader.ts +3 -1
- package/src/input/command-registry.ts +1 -0
- package/src/input/commands/cloudflare-runtime.ts +2 -1
- package/src/input/commands/eval.ts +4 -3
- package/src/input/commands/guidance-runtime.ts +2 -4
- package/src/input/commands/health-runtime.ts +13 -7
- package/src/input/commands/knowledge.ts +2 -1
- package/src/input/commands/runtime-services.ts +40 -10
- package/src/input/handler-command-route.ts +1 -1
- package/src/input/handler-feed-routes.ts +61 -4
- package/src/input/handler-feed.ts +13 -0
- package/src/input/handler-interactions.ts +2 -1
- package/src/input/handler-modal-routes.ts +2 -1
- package/src/input/handler-onboarding-cloudflare.ts +2 -1
- package/src/input/handler-onboarding.ts +8 -8
- package/src/input/handler-picker-routes.ts +18 -13
- package/src/input/handler-ui-state.ts +5 -1
- package/src/input/keybindings.ts +5 -0
- package/src/input/model-picker.ts +5 -0
- package/src/input/panel-integration-actions.ts +10 -0
- package/src/input/tts-settings-actions.ts +2 -1
- package/src/main.ts +52 -60
- package/src/panels/agent-inspector-panel.ts +90 -15
- package/src/panels/agent-inspector-shared.ts +3 -3
- package/src/panels/agent-logs-panel.ts +149 -72
- package/src/panels/approval-panel.ts +146 -95
- package/src/panels/automation-control-panel.ts +24 -1
- package/src/panels/base-panel.ts +28 -3
- package/src/panels/builtin/agent.ts +3 -1
- package/src/panels/builtin/development.ts +2 -1
- package/src/panels/builtin/session.ts +1 -1
- package/src/panels/cockpit-panel.ts +24 -5
- package/src/panels/cockpit-read-model.ts +2 -1
- package/src/panels/communication-panel.ts +108 -43
- package/src/panels/context-visualizer-panel.ts +49 -15
- package/src/panels/control-plane-panel.ts +27 -1
- package/src/panels/cost-tracker-panel.ts +87 -65
- package/src/panels/debug-panel.ts +61 -37
- package/src/panels/diff-panel.ts +59 -30
- package/src/panels/docs-panel.ts +30 -24
- package/src/panels/eval-panel.ts +130 -81
- package/src/panels/expandable-list-panel.ts +189 -0
- package/src/panels/file-explorer-panel.ts +42 -42
- package/src/panels/file-preview-panel.ts +11 -3
- package/src/panels/forensics-panel.ts +27 -13
- package/src/panels/git-panel.ts +65 -84
- package/src/panels/hooks-panel.ts +36 -2
- package/src/panels/incident-review-panel.ts +31 -10
- package/src/panels/intelligence-panel.ts +152 -71
- package/src/panels/knowledge-graph-panel.ts +89 -27
- package/src/panels/local-auth-panel.ts +17 -11
- package/src/panels/marketplace-panel.ts +33 -13
- package/src/panels/memory-panel.ts +7 -5
- package/src/panels/ops-control-panel.ts +69 -7
- package/src/panels/ops-strategy-panel.ts +61 -43
- package/src/panels/orchestration-panel.ts +34 -4
- package/src/panels/panel-list-panel.ts +33 -8
- package/src/panels/panel-manager.ts +23 -4
- package/src/panels/plan-dashboard-panel.ts +183 -66
- package/src/panels/plugins-panel.ts +48 -10
- package/src/panels/policy-panel.ts +60 -23
- package/src/panels/polish-core.ts +157 -0
- package/src/panels/polish-tables.ts +258 -0
- package/src/panels/polish.ts +44 -145
- package/src/panels/project-planning-panel.ts +27 -4
- package/src/panels/provider-accounts-panel.ts +55 -18
- package/src/panels/provider-health-panel.ts +118 -87
- package/src/panels/provider-stats-panel.ts +47 -22
- package/src/panels/qr-panel.ts +23 -11
- package/src/panels/remote-panel.ts +12 -5
- package/src/panels/routes-panel.ts +27 -5
- package/src/panels/sandbox-panel.ts +62 -27
- package/src/panels/schedule-panel.ts +44 -24
- package/src/panels/scrollable-list-panel.ts +124 -10
- package/src/panels/security-panel.ts +72 -20
- package/src/panels/services-panel.ts +68 -22
- package/src/panels/session-browser-panel.ts +42 -26
- package/src/panels/session-maintenance.ts +2 -2
- package/src/panels/settings-sync-panel.ts +30 -15
- package/src/panels/skills-panel.ts +2 -1
- package/src/panels/subscription-panel.ts +21 -3
- package/src/panels/symbol-outline-panel.ts +40 -47
- package/src/panels/system-messages-panel.ts +60 -27
- package/src/panels/tasks-panel.ts +36 -14
- package/src/panels/thinking-panel.ts +32 -36
- package/src/panels/token-budget-panel.ts +80 -53
- package/src/panels/tool-inspector-panel.ts +37 -39
- package/src/panels/types.ts +25 -0
- package/src/panels/watchers-panel.ts +25 -5
- package/src/panels/work-plan-panel.ts +127 -35
- package/src/panels/worktree-panel.ts +65 -20
- package/src/panels/wrfc-panel-format.ts +133 -0
- package/src/panels/wrfc-panel.ts +53 -147
- package/src/renderer/agent-detail-modal.ts +2 -2
- package/src/renderer/compaction-preview.ts +2 -1
- package/src/renderer/compositor.ts +46 -43
- package/src/renderer/modal-utils.ts +0 -10
- package/src/renderer/model-picker-overlay.ts +9 -4
- package/src/renderer/model-workspace.ts +2 -3
- package/src/renderer/panel-composite.ts +7 -20
- package/src/renderer/panel-workspace-bar.ts +14 -8
- package/src/renderer/process-modal.ts +2 -2
- package/src/renderer/progress.ts +3 -2
- package/src/renderer/tab-strip.ts +148 -34
- package/src/renderer/ui-factory.ts +4 -6
- package/src/runtime/bootstrap-command-context.ts +3 -0
- package/src/runtime/bootstrap-command-parts.ts +3 -1
- package/src/runtime/bootstrap-core.ts +31 -31
- package/src/runtime/bootstrap-shell.ts +1 -0
- package/src/runtime/onboarding/apply.ts +4 -3
- package/src/runtime/onboarding/snapshot.ts +4 -3
- package/src/runtime/process-lifecycle.ts +195 -0
- package/src/runtime/services.ts +4 -1
- package/src/runtime/ui/model-picker/health-enrichment.ts +3 -0
- package/src/runtime/ui/model-picker/types.ts +4 -0
- package/src/runtime/wrfc-persistence.ts +20 -5
- package/src/shell/ui-openers.ts +3 -2
- package/src/utils/format-duration.ts +55 -0
- package/src/utils/format-number.ts +71 -0
- package/src/verification/live-verifier.ts +4 -3
- package/src/version.ts +1 -1
- package/src/core/context-auto-compact.ts +0 -110
- package/src/panels/panel-picker.ts +0 -106
- package/src/renderer/panel-picker-overlay.ts +0 -202
- package/src/renderer/panel-tab-bar.ts +0 -69
|
@@ -8,10 +8,14 @@ import type { SessionMemoryQuery } from '../runtime/ui-service-queries.ts';
|
|
|
8
8
|
import {
|
|
9
9
|
buildEmptyState,
|
|
10
10
|
buildGuidanceLine,
|
|
11
|
+
buildKeyboardHints,
|
|
12
|
+
buildMeterLine,
|
|
11
13
|
buildStyledPanelLine,
|
|
14
|
+
buildTable,
|
|
12
15
|
buildPanelWorkspace,
|
|
13
16
|
resolveScrollablePanelSection,
|
|
14
17
|
DEFAULT_PANEL_PALETTE,
|
|
18
|
+
extendPalette,
|
|
15
19
|
type PanelWorkspaceSection,
|
|
16
20
|
} from './polish.ts';
|
|
17
21
|
|
|
@@ -32,22 +36,16 @@ interface TurnUsage {
|
|
|
32
36
|
// Colors
|
|
33
37
|
// ---------------------------------------------------------------------------
|
|
34
38
|
|
|
35
|
-
const C = {
|
|
36
|
-
|
|
39
|
+
const C = extendPalette(DEFAULT_PANEL_PALETTE, {
|
|
40
|
+
// Token data-series colors (input/output/cache) — categorical, no shared equivalent
|
|
37
41
|
input: '#00ffff',
|
|
38
42
|
output: '#d000ff',
|
|
39
43
|
cacheRead: '#00d700',
|
|
40
44
|
cacheWrite: '#ffaf00',
|
|
45
|
+
// Progress-bar track shade and per-turn table sub-header gray
|
|
41
46
|
barBg: '236',
|
|
42
|
-
label: '244',
|
|
43
|
-
value: '252',
|
|
44
|
-
sectionHeader: '238',
|
|
45
|
-
warnYellow: '#ffaf00',
|
|
46
|
-
warnRed: '#ff5f5f',
|
|
47
|
-
dim: '240',
|
|
48
|
-
good: '#5fd700',
|
|
49
47
|
turnHeader: '242',
|
|
50
|
-
}
|
|
48
|
+
});
|
|
51
49
|
|
|
52
50
|
// Warning thresholds for context window usage
|
|
53
51
|
const WARN_YELLOW = 0.70;
|
|
@@ -99,11 +97,17 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
99
97
|
private sessionReadModel: UiReadModel<UiSessionSnapshot> | null = null;
|
|
100
98
|
private readonly sessionMemoryStore: SessionMemoryQuery;
|
|
101
99
|
private readonly configManager: Pick<ConfigManager, 'get'>;
|
|
100
|
+
private readonly requestRender: () => void;
|
|
102
101
|
|
|
103
|
-
constructor(
|
|
102
|
+
constructor(
|
|
103
|
+
sessionMemoryStore: SessionMemoryQuery,
|
|
104
|
+
configManager: Pick<ConfigManager, 'get'>,
|
|
105
|
+
requestRender: () => void = () => {},
|
|
106
|
+
) {
|
|
104
107
|
super('tokens', 'Tokens', 'T', 'monitoring');
|
|
105
108
|
this.sessionMemoryStore = sessionMemoryStore;
|
|
106
109
|
this.configManager = configManager;
|
|
110
|
+
this.requestRender = requestRender;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
// ---------------------------------------------------------------------------
|
|
@@ -149,7 +153,7 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
149
153
|
this.turnHistory.shift();
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
this.
|
|
156
|
+
this._refreshAndRender();
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
// ---------------------------------------------------------------------------
|
|
@@ -161,11 +165,17 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
161
165
|
this.refresh();
|
|
162
166
|
// Poll every 2 s while active so the context bar stays current during streaming
|
|
163
167
|
if (this.refreshTimer !== null) clearInterval(this.refreshTimer);
|
|
164
|
-
this.refreshTimer = setInterval(() => this.
|
|
168
|
+
this.refreshTimer = setInterval(() => this._refreshAndRender(), 2_000);
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
override onDeactivate(): void {
|
|
168
172
|
super.onDeactivate();
|
|
173
|
+
// Stop the off-screen poll so a backgrounded Tokens tab does not keep
|
|
174
|
+
// refreshing (and, once requestRender is wired, force a repaint) every 2 s.
|
|
175
|
+
if (this.refreshTimer !== null) {
|
|
176
|
+
clearInterval(this.refreshTimer);
|
|
177
|
+
this.refreshTimer = null;
|
|
178
|
+
}
|
|
169
179
|
}
|
|
170
180
|
|
|
171
181
|
override onDestroy(): void {
|
|
@@ -194,6 +204,15 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
194
204
|
this.markDirty();
|
|
195
205
|
}
|
|
196
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Refresh data and request a repaint. Used by the background poll and on
|
|
209
|
+
* turn completion, where no key event will otherwise drive a render.
|
|
210
|
+
*/
|
|
211
|
+
private _refreshAndRender(): void {
|
|
212
|
+
this.refresh();
|
|
213
|
+
this.requestRender();
|
|
214
|
+
}
|
|
215
|
+
|
|
197
216
|
// ---------------------------------------------------------------------------
|
|
198
217
|
// Rendering
|
|
199
218
|
// ---------------------------------------------------------------------------
|
|
@@ -221,6 +240,12 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
221
240
|
const priorSections = [...sections];
|
|
222
241
|
const turnsSection = resolveScrollablePanelSection(width, height, {
|
|
223
242
|
intro: 'Live context pressure, session token composition, cache usage, and recent turn deltas.',
|
|
243
|
+
footerLines: [
|
|
244
|
+
buildKeyboardHints(width, [
|
|
245
|
+
{ keys: '/compact', label: 'compress context' },
|
|
246
|
+
{ keys: '/clear', label: 'reset session' },
|
|
247
|
+
], DEFAULT_PANEL_PALETTE),
|
|
248
|
+
],
|
|
224
249
|
palette: DEFAULT_PANEL_PALETTE,
|
|
225
250
|
beforeSections: priorSections,
|
|
226
251
|
section: {
|
|
@@ -257,6 +282,12 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
257
282
|
title: ' Token Budget',
|
|
258
283
|
intro: 'Live context pressure, session token composition, cache usage, and recent turn deltas.',
|
|
259
284
|
sections,
|
|
285
|
+
footerLines: [
|
|
286
|
+
buildKeyboardHints(width, [
|
|
287
|
+
{ keys: '/compact', label: 'compress context' },
|
|
288
|
+
{ keys: '/clear', label: 'reset session' },
|
|
289
|
+
], DEFAULT_PANEL_PALETTE),
|
|
290
|
+
],
|
|
260
291
|
palette: DEFAULT_PANEL_PALETTE,
|
|
261
292
|
});
|
|
262
293
|
});
|
|
@@ -369,10 +400,10 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
369
400
|
let barColor: string;
|
|
370
401
|
let warnSuffix = '';
|
|
371
402
|
if (pct >= WARN_RED) {
|
|
372
|
-
barColor = C.
|
|
403
|
+
barColor = C.bad;
|
|
373
404
|
warnSuffix = ' ! CRITICAL';
|
|
374
405
|
} else if (pct >= WARN_YELLOW) {
|
|
375
|
-
barColor = C.
|
|
406
|
+
barColor = C.warn;
|
|
376
407
|
warnSuffix = ' ! HIGH';
|
|
377
408
|
} else {
|
|
378
409
|
barColor = C.good;
|
|
@@ -382,12 +413,12 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
382
413
|
const suffix = ` ${fmtTok(this.lastInputTokens)}/${fmtTok(this.contextWindow)} (${pctInt}%)${warnSuffix}`;
|
|
383
414
|
const BAR_W = Math.max(8, width - label.length - suffix.length - 2);
|
|
384
415
|
const filled = Math.round(pct * BAR_W);
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
{
|
|
389
|
-
{
|
|
390
|
-
|
|
416
|
+
// Context fill is the headline metric — render it with the shared meter
|
|
417
|
+
// primitive so the bar glyphs and width handling match every other panel.
|
|
418
|
+
lines.push(buildMeterLine(width, filled, BAR_W,
|
|
419
|
+
{ filled: barColor, empty: C.barBg, label: C.label },
|
|
420
|
+
{ prefix: label, suffix },
|
|
421
|
+
));
|
|
391
422
|
return lines;
|
|
392
423
|
}
|
|
393
424
|
|
|
@@ -420,41 +451,37 @@ export class TokenBudgetPanel extends BasePanel {
|
|
|
420
451
|
|
|
421
452
|
/** Last N turns table: turn#, in, out, CR, CW, total. */
|
|
422
453
|
private renderTurnHistory(width: number, maxRows: number): Line[] {
|
|
423
|
-
const lines: Line[] = [];
|
|
424
|
-
|
|
425
|
-
// Column headers
|
|
426
|
-
const colLine = createEmptyLine(width);
|
|
427
|
-
const headers = [' #', ' Input', ' Output', ' CR', ' CW', ' Total'];
|
|
428
|
-
const cols = [3, 9, 9, 6, 6, 9];
|
|
429
|
-
let hx = 0;
|
|
430
|
-
for (let i = 0; i < headers.length; i++) {
|
|
431
|
-
const h = headers[i]!;
|
|
432
|
-
for (const ch of h.slice(0, cols[i]!)) {
|
|
433
|
-
if (hx >= width) break;
|
|
434
|
-
colLine[hx++] = createStyledCell(ch, { fg: C.turnHeader, dim: true });
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
lines.push(colLine);
|
|
438
|
-
|
|
439
454
|
const available = Math.max(0, maxRows - 1); // minus col header
|
|
440
455
|
const toShow = this.turnHistory.slice(-Math.max(0, available));
|
|
441
456
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
]
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
457
|
+
// Width-aware aligned table via the shared primitive (replaces manual
|
|
458
|
+
// per-cell column math, which miscounts on wide glyphs).
|
|
459
|
+
return buildTable(
|
|
460
|
+
width,
|
|
461
|
+
[
|
|
462
|
+
{ label: '#', width: 4, align: 'right' },
|
|
463
|
+
{ label: 'Input', width: 9, align: 'right' },
|
|
464
|
+
{ label: 'Output', width: 9, align: 'right' },
|
|
465
|
+
{ label: 'CR', width: 7, align: 'right' },
|
|
466
|
+
{ label: 'CW', width: 7, align: 'right' },
|
|
467
|
+
{ label: 'Total', align: 'right' },
|
|
468
|
+
],
|
|
469
|
+
toShow.map((t, i) => {
|
|
470
|
+
const turnNum = this.turnHistory.length - toShow.length + i + 1;
|
|
471
|
+
const total = t.input + t.output + t.cacheRead + t.cacheWrite;
|
|
472
|
+
return {
|
|
473
|
+
cells: [
|
|
474
|
+
{ text: String(turnNum), fg: C.dim },
|
|
475
|
+
{ text: fmtTok(t.input), fg: C.input },
|
|
476
|
+
{ text: fmtTok(t.output), fg: C.output },
|
|
477
|
+
{ text: fmtTok(t.cacheRead), fg: C.cacheRead },
|
|
478
|
+
{ text: fmtTok(t.cacheWrite), fg: C.cacheWrite },
|
|
479
|
+
{ text: fmtTok(total), fg: C.value },
|
|
480
|
+
],
|
|
481
|
+
};
|
|
482
|
+
}),
|
|
483
|
+
{ ...DEFAULT_PANEL_PALETTE, label: C.turnHeader },
|
|
484
|
+
);
|
|
458
485
|
}
|
|
459
486
|
|
|
460
487
|
/** Paint a plain text string into a new Line. */
|
|
@@ -13,26 +13,19 @@ import {
|
|
|
13
13
|
buildPanelWorkspace,
|
|
14
14
|
resolveScrollablePanelSection,
|
|
15
15
|
DEFAULT_PANEL_PALETTE,
|
|
16
|
+
extendPalette,
|
|
16
17
|
type PanelWorkspaceSection,
|
|
17
18
|
} from './polish.ts';
|
|
18
19
|
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
20
|
+
import { formatDuration } from '../utils/format-duration.ts';
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
errorFg: '#ff6666',
|
|
28
|
-
durationFg: '#aaaa66',
|
|
29
|
-
argsFg: '#aaaaaa',
|
|
30
|
-
dimFg: '#555566',
|
|
31
|
-
selected: '#00ffff',
|
|
32
|
-
selectedBg: '#1a2a3a',
|
|
33
|
-
labelFg: '#8888bb',
|
|
34
|
-
filterFg: '#ffcc44',
|
|
35
|
-
} as const;
|
|
22
|
+
// Panel-specific accents only; shared tones come from DEFAULT_PANEL_PALETTE so
|
|
23
|
+
// theme changes propagate. selectedBg->selectBg, errorFg->bad are shared keys.
|
|
24
|
+
const C = extendPalette(DEFAULT_PANEL_PALETTE, {
|
|
25
|
+
selected: '#00ffff',
|
|
26
|
+
toolFg: '#00ccff',
|
|
27
|
+
argsFg: '#aaaaaa',
|
|
28
|
+
});
|
|
36
29
|
|
|
37
30
|
const MAX_ENTRIES = 500;
|
|
38
31
|
|
|
@@ -64,14 +57,12 @@ function shortTime(ms: number): string {
|
|
|
64
57
|
}
|
|
65
58
|
|
|
66
59
|
function formatMs(ms: number): string {
|
|
67
|
-
|
|
68
|
-
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
69
|
-
return `${Math.floor(ms / 60000)}m${Math.floor((ms % 60000) / 1000)}s`;
|
|
60
|
+
return formatDuration(ms);
|
|
70
61
|
}
|
|
71
62
|
|
|
72
63
|
function truncateJson(val: unknown, maxLen = 120): string {
|
|
73
64
|
const s = JSON.stringify(val) ?? 'null';
|
|
74
|
-
return s
|
|
65
|
+
return truncateDisplay(s, maxLen);
|
|
75
66
|
}
|
|
76
67
|
|
|
77
68
|
function summarizeResult(result: unknown): string | undefined {
|
|
@@ -81,7 +72,7 @@ function summarizeResult(result: unknown): string | undefined {
|
|
|
81
72
|
const record = result as Record<string, unknown>;
|
|
82
73
|
if (typeof record.preview === 'string' && record.preview.trim()) {
|
|
83
74
|
const compact = record.preview.replace(/\s+/g, ' ').trim();
|
|
84
|
-
return compact
|
|
75
|
+
return truncateDisplay(compact, 72);
|
|
85
76
|
}
|
|
86
77
|
if (typeof record.kind === 'string' && typeof record.byteSize === 'number') {
|
|
87
78
|
return `${record.kind} (${record.byteSize}B)`;
|
|
@@ -150,22 +141,23 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
150
141
|
if (height <= 0 || width <= 0) return [];
|
|
151
142
|
|
|
152
143
|
const running = this.records.filter(r => r.endMs === undefined).length;
|
|
144
|
+
const distinctTools = new Set(this.records.map((r) => r.tool)).size;
|
|
153
145
|
const filterLabel = this.filterMode === 'all' ? '' : ` [${this.filterMode}]`;
|
|
154
146
|
const title = ` Tools [${this.records.length} calls${running > 0 ? `, ${running} running` : ''}]${filterLabel}`;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
[' expand', DEFAULT_PANEL_PALETTE.dim],
|
|
161
|
-
[' f', DEFAULT_PANEL_PALETTE.info],
|
|
162
|
-
[' filter', DEFAULT_PANEL_PALETTE.dim],
|
|
163
|
-
[' c', DEFAULT_PANEL_PALETTE.info],
|
|
164
|
-
[' clear', DEFAULT_PANEL_PALETTE.dim],
|
|
165
|
-
[' g', DEFAULT_PANEL_PALETTE.info],
|
|
166
|
-
[' end', DEFAULT_PANEL_PALETTE.dim],
|
|
167
|
-
]),
|
|
147
|
+
// Context-aware footer: only advertise keys that do something in the current
|
|
148
|
+
// state — filter needs >1 tool, clear/end need at least one call.
|
|
149
|
+
const footerSegments: Array<[string, string, string?]> = [
|
|
150
|
+
[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' scroll', DEFAULT_PANEL_PALETTE.dim],
|
|
151
|
+
[' Enter', DEFAULT_PANEL_PALETTE.info], [' expand', DEFAULT_PANEL_PALETTE.dim],
|
|
168
152
|
];
|
|
153
|
+
if (distinctTools > 1) {
|
|
154
|
+
footerSegments.push([' f', DEFAULT_PANEL_PALETTE.info], [' filter', DEFAULT_PANEL_PALETTE.dim]);
|
|
155
|
+
}
|
|
156
|
+
if (this.records.length > 0) {
|
|
157
|
+
footerSegments.push([' c', DEFAULT_PANEL_PALETTE.info], [' clear', DEFAULT_PANEL_PALETTE.dim]);
|
|
158
|
+
footerSegments.push([' g', DEFAULT_PANEL_PALETTE.info], [this.autoScroll ? ' end (live)' : ' jump to end', DEFAULT_PANEL_PALETTE.dim]);
|
|
159
|
+
}
|
|
160
|
+
const footerLines = [buildPanelLine(width, footerSegments)];
|
|
169
161
|
|
|
170
162
|
const flat = this._getFlat();
|
|
171
163
|
|
|
@@ -182,9 +174,15 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
182
174
|
title: 'Calls',
|
|
183
175
|
lines: buildEmptyState(
|
|
184
176
|
width,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
177
|
+
this.records.length > 0 && this.filterMode !== 'all'
|
|
178
|
+
? ` No "${this.filterMode}" calls`
|
|
179
|
+
: ' No tool calls yet',
|
|
180
|
+
this.records.length > 0 && this.filterMode !== 'all'
|
|
181
|
+
? 'The active tool filter hides every recorded call.'
|
|
182
|
+
: 'Tool executions appear here as the agent works. Expand a call to inspect its arguments and result payload.',
|
|
183
|
+
this.records.length > 0 && this.filterMode !== 'all'
|
|
184
|
+
? [{ command: 'f', summary: 'cycle the tool filter back to all calls' }]
|
|
185
|
+
: [{ command: '/spawn <task>', summary: 'run an agent to populate the tool-call timeline' }],
|
|
188
186
|
DEFAULT_PANEL_PALETTE,
|
|
189
187
|
),
|
|
190
188
|
},
|
|
@@ -271,7 +269,7 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
271
269
|
}
|
|
272
270
|
|
|
273
271
|
private _renderRow(width: number, row: FlatRow, isCursor: boolean): Line {
|
|
274
|
-
const bg = isCursor ? C.
|
|
272
|
+
const bg = isCursor ? C.selectBg : '';
|
|
275
273
|
if (row.kind === 'call') {
|
|
276
274
|
return buildStyledPanelLine(width, [
|
|
277
275
|
{ text: isCursor ? '▸' : ' ', fg: C.selected, bg, bold: isCursor },
|
|
@@ -280,7 +278,7 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
280
278
|
}
|
|
281
279
|
return buildStyledPanelLine(width, [
|
|
282
280
|
{ text: ' ', fg: C.argsFg, bg },
|
|
283
|
-
{ text: truncateDisplay(row.text, Math.max(0, width - 2)), fg: row.isError ? C.
|
|
281
|
+
{ text: truncateDisplay(row.text, Math.max(0, width - 2)), fg: row.isError ? C.bad : C.argsFg, bg },
|
|
284
282
|
]);
|
|
285
283
|
}
|
|
286
284
|
|
package/src/panels/types.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import type { Line } from '../types/grid.ts';
|
|
2
|
+
import type { StatusState } from '../renderer/status-glyphs.ts';
|
|
2
3
|
import type { ComponentResourceContract, ComponentHealthState } from '../runtime/perf/panel-contracts.ts';
|
|
4
|
+
// Routed through the `@/` alias (not `./panel-manager.ts`) so this foundational
|
|
5
|
+
// types module stays a leaf in the relative-import graph the architecture
|
|
6
|
+
// cycle-checker walks. Type-only, erased at runtime — no real dependency edge.
|
|
7
|
+
import type { PanelManager } from '@/panels/panel-manager.ts';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Context passed to a panel's `handlePanelIntegrationAction` hook so it can
|
|
11
|
+
* drive cross-panel behavior (e.g. file-explorer opening the preview panel)
|
|
12
|
+
* without the input layer needing `instanceof` knowledge of each panel type.
|
|
13
|
+
*/
|
|
14
|
+
export interface PanelIntegrationContext {
|
|
15
|
+
readonly panelManager: PanelManager;
|
|
16
|
+
readonly executeCommand?: (name: string, args: string[]) => Promise<unknown>;
|
|
17
|
+
}
|
|
3
18
|
|
|
4
19
|
/**
|
|
5
20
|
* Named logical key identifiers emitted by the input tokenizer.
|
|
@@ -67,6 +82,16 @@ export interface Panel {
|
|
|
67
82
|
// Scroll input (optional)
|
|
68
83
|
// Positive delta scrolls down; negative delta scrolls up.
|
|
69
84
|
handleScroll?(deltaRows: number): boolean;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Cross-panel integration hook (optional). Called before the panel's own
|
|
88
|
+
* `handleInput` when a navigation/confirm key is pressed, so a panel can
|
|
89
|
+
* drive another panel (e.g. open a file in the preview panel). Return `true`
|
|
90
|
+
* to consume the key. The legacy `handlePanelIntegrationAction` router in
|
|
91
|
+
* `src/input/panel-integration-actions.ts` consults this first, then falls
|
|
92
|
+
* back to its built-in `instanceof` routing.
|
|
93
|
+
*/
|
|
94
|
+
handlePanelIntegrationAction?(key: string, ctx: PanelIntegrationContext): boolean;
|
|
70
95
|
}
|
|
71
96
|
|
|
72
97
|
export interface PanelRegistration extends Pick<Panel, 'id' | 'name' | 'icon' | 'category'> {
|
|
@@ -4,9 +4,11 @@ import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
|
4
4
|
import type { UiReadModel, UiWatchersSnapshot } from '../runtime/ui-read-models.ts';
|
|
5
5
|
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
6
6
|
import {
|
|
7
|
+
buildDetailBlock,
|
|
7
8
|
buildEmptyState,
|
|
8
9
|
buildGuidanceLine,
|
|
9
10
|
buildKeyValueLine,
|
|
11
|
+
buildKeyboardHints,
|
|
10
12
|
buildPanelLine,
|
|
11
13
|
buildPanelWorkspace,
|
|
12
14
|
DEFAULT_PANEL_PALETTE,
|
|
@@ -59,10 +61,18 @@ export class WatchersPanel extends ScrollableListPanel<WatcherEntry> {
|
|
|
59
61
|
public constructor(readModel?: UiReadModel<UiWatchersSnapshot>) {
|
|
60
62
|
super('watchers', 'Watchers', 'W', 'monitoring');
|
|
61
63
|
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
64
|
+
this.filterEnabled = true;
|
|
65
|
+
this.filterLabel = 'Filter watchers';
|
|
62
66
|
this.readModel = readModel;
|
|
63
67
|
this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
protected override filterMatches(watcher: WatcherEntry, q: string): boolean {
|
|
71
|
+
return watcher.label.toLowerCase().includes(q)
|
|
72
|
+
|| watcher.state.toLowerCase().includes(q)
|
|
73
|
+
|| String(watcher.sourceStatus ?? '').toLowerCase().includes(q);
|
|
74
|
+
}
|
|
75
|
+
|
|
66
76
|
public override onDestroy(): void {
|
|
67
77
|
this.unsub?.();
|
|
68
78
|
}
|
|
@@ -144,7 +154,7 @@ export class WatchersPanel extends ScrollableListPanel<WatcherEntry> {
|
|
|
144
154
|
this.clampSelection();
|
|
145
155
|
const selected = watchers[this.selectedIndex]!;
|
|
146
156
|
|
|
147
|
-
const
|
|
157
|
+
const detailRows: Line[] = [
|
|
148
158
|
buildPanelLine(width, [
|
|
149
159
|
[' Watcher: ', C.label],
|
|
150
160
|
[selected.label, C.value],
|
|
@@ -171,23 +181,33 @@ export class WatchersPanel extends ScrollableListPanel<WatcherEntry> {
|
|
|
171
181
|
]),
|
|
172
182
|
];
|
|
173
183
|
if (selected.degradedReason) {
|
|
174
|
-
|
|
184
|
+
detailRows.push(buildPanelLine(width, [
|
|
175
185
|
[' Reason: ', C.label],
|
|
176
186
|
[truncateDisplay(selected.degradedReason, Math.max(0, width - 11)), C.warn],
|
|
177
187
|
]));
|
|
178
188
|
}
|
|
179
189
|
if (selected.lastError) {
|
|
180
|
-
|
|
190
|
+
detailRows.push(buildPanelLine(width, [
|
|
181
191
|
[' Error: ', C.label],
|
|
182
192
|
[truncateDisplay(selected.lastError, Math.max(0, width - 10)), C.error],
|
|
183
193
|
]));
|
|
184
194
|
}
|
|
185
|
-
|
|
195
|
+
|
|
196
|
+
const hints = this.filterActive
|
|
197
|
+
? [{ keys: 'type', label: 'filter' }, { keys: 'Enter', label: 'apply' }, { keys: 'Esc', label: 'clear' }]
|
|
198
|
+
: [
|
|
199
|
+
{ keys: 'Up/Down', label: 'move' },
|
|
200
|
+
{ keys: '/schedule list', label: 'consumers' },
|
|
201
|
+
{ keys: '/', label: 'filter' },
|
|
202
|
+
];
|
|
186
203
|
|
|
187
204
|
return this.renderList(width, height, {
|
|
188
205
|
title: 'Watchers',
|
|
189
206
|
header: headerLines,
|
|
190
|
-
footer:
|
|
207
|
+
footer: [
|
|
208
|
+
...buildDetailBlock(width, `Watcher · ${selected.label}`, detailRows, C),
|
|
209
|
+
buildKeyboardHints(width, hints, C),
|
|
210
|
+
],
|
|
191
211
|
});
|
|
192
212
|
}
|
|
193
213
|
}
|