@pellux/goodvibes-tui 0.28.0 → 0.29.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 +101 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/input/commands/cost-runtime.ts +49 -0
- package/src/input/commands/operator-runtime.ts +5 -1
- package/src/input/commands.ts +2 -0
- package/src/input/handler-feed-routes.ts +12 -35
- package/src/input/handler-feed.ts +4 -8
- package/src/input/handler-shortcuts.ts +51 -0
- package/src/input/handler.ts +43 -6
- package/src/input/keybindings.ts +48 -8
- package/src/input/panel-integration-actions.ts +107 -1
- package/src/main.ts +5 -1
- package/src/panels/agent-inspector-panel.ts +125 -36
- package/src/panels/agent-inspector-shared.ts +144 -0
- package/src/panels/approval-panel.ts +67 -16
- package/src/panels/automation-control-panel.ts +368 -124
- package/src/panels/builtin/agent.ts +28 -37
- package/src/panels/builtin/development.ts +40 -32
- package/src/panels/builtin/knowledge.ts +15 -6
- package/src/panels/builtin/operations.ts +178 -117
- package/src/panels/builtin/session.ts +35 -9
- package/src/panels/builtin/shared.ts +98 -6
- package/src/panels/cockpit-panel.ts +232 -73
- package/src/panels/communication-panel.ts +58 -20
- package/src/panels/confirm-state.ts +8 -1
- package/src/panels/control-plane-panel.ts +351 -118
- package/src/panels/cost-tracker-panel.ts +165 -7
- package/src/panels/debug-panel.ts +312 -159
- package/src/panels/diff-panel.ts +203 -57
- package/src/panels/docs-panel.ts +152 -66
- package/src/panels/eval-panel.ts +220 -42
- package/src/panels/file-explorer-panel.ts +202 -122
- package/src/panels/file-preview-panel.ts +132 -57
- package/src/panels/git-panel.ts +309 -128
- package/src/panels/hooks-panel.ts +150 -22
- package/src/panels/incident-review-panel.ts +223 -35
- package/src/panels/index.ts +0 -4
- package/src/panels/intelligence-panel.ts +212 -86
- package/src/panels/knowledge-graph-panel.ts +461 -101
- package/src/panels/local-auth-panel.ts +240 -28
- package/src/panels/marketplace-panel.ts +193 -26
- package/src/panels/memory-panel.ts +78 -77
- package/src/panels/ops-control-panel.ts +146 -29
- package/src/panels/ops-strategy-panel.ts +72 -4
- package/src/panels/orchestration-panel.ts +231 -69
- package/src/panels/panel-list-panel.ts +154 -131
- package/src/panels/panel-manager.ts +97 -9
- package/src/panels/plan-dashboard-panel.ts +333 -17
- package/src/panels/plugins-panel.ts +184 -29
- package/src/panels/policy-panel.ts +210 -38
- package/src/panels/polish-core.ts +7 -2
- package/src/panels/polish.ts +23 -4
- package/src/panels/project-planning-answer-actions.ts +134 -0
- package/src/panels/project-planning-panel.ts +62 -113
- package/src/panels/provider-health-panel.ts +434 -518
- package/src/panels/provider-health-routes.ts +203 -0
- package/src/panels/provider-health-tracker.ts +194 -6
- package/src/panels/provider-health-views.ts +560 -0
- package/src/panels/qr-panel.ts +116 -30
- package/src/panels/remote-panel.ts +114 -36
- package/src/panels/routes-panel.ts +63 -22
- package/src/panels/sandbox-panel.ts +174 -42
- package/src/panels/scrollable-list-panel.ts +19 -135
- package/src/panels/security-panel.ts +133 -33
- package/src/panels/services-panel.ts +116 -64
- package/src/panels/session-browser-panel.ts +73 -2
- package/src/panels/session-maintenance.ts +4 -122
- package/src/panels/settings-sync-panel.ts +335 -72
- package/src/panels/skills-panel.ts +157 -89
- package/src/panels/subscription-panel.ts +86 -33
- package/src/panels/symbol-outline-panel.ts +248 -108
- package/src/panels/system-messages-panel.ts +114 -13
- package/src/panels/tasks-panel.ts +326 -139
- package/src/panels/thinking-panel.ts +43 -10
- package/src/panels/token-budget-panel.ts +194 -18
- package/src/panels/tool-inspector-panel.ts +144 -34
- package/src/panels/types.ts +40 -4
- package/src/panels/work-plan-panel.ts +280 -17
- package/src/panels/worktree-panel.ts +175 -42
- package/src/panels/wrfc-panel.ts +116 -24
- package/src/renderer/conversation-overlays.ts +25 -11
- package/src/renderer/footer-tips.ts +41 -0
- package/src/renderer/fullscreen-primitives.ts +22 -16
- package/src/renderer/help-overlay.ts +91 -14
- package/src/renderer/hint-grammar.ts +52 -0
- package/src/renderer/layout.ts +7 -7
- package/src/renderer/modal-factory.ts +23 -15
- package/src/renderer/model-picker-overlay.ts +21 -7
- package/src/renderer/overlay-box.ts +16 -10
- package/src/renderer/process-indicator.ts +3 -1
- package/src/renderer/progress.ts +5 -4
- package/src/renderer/search-overlay.ts +27 -6
- package/src/renderer/session-picker-modal.ts +6 -4
- package/src/renderer/settings-modal.ts +70 -10
- package/src/renderer/shell-surface.ts +41 -15
- package/src/renderer/status-glyphs.ts +11 -9
- package/src/renderer/theme.ts +60 -3
- package/src/renderer/ui-factory.ts +41 -30
- package/src/renderer/ui-primitives.ts +23 -2
- package/src/runtime/bootstrap-shell.ts +35 -18
- package/src/runtime/diagnostics/panels/index.ts +0 -3
- package/src/shell/ui-openers.ts +14 -22
- package/src/utils/format-duration.ts +2 -2
- package/src/utils/splash-lines.ts +44 -3
- package/src/version.ts +1 -1
- package/src/work-plans/work-plan-store.ts +13 -0
- package/src/panels/agent-logs-panel.ts +0 -635
- package/src/panels/agent-logs-shared.ts +0 -129
- package/src/panels/context-visualizer-panel.ts +0 -238
- package/src/panels/forensics-panel.ts +0 -378
- package/src/panels/provider-account-snapshot.ts +0 -259
- package/src/panels/provider-accounts-panel.ts +0 -255
- package/src/panels/provider-stats-panel.ts +0 -391
- package/src/panels/schedule-panel.ts +0 -365
- package/src/panels/watchers-panel.ts +0 -213
- package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
- package/src/runtime/diagnostics/panels/policy.ts +0 -177
|
@@ -6,6 +6,7 @@ import type { Line } from '../types/grid.ts';
|
|
|
6
6
|
import { BasePanel } from './base-panel.ts';
|
|
7
7
|
import type { ToolEvent, TurnEvent } from '@/runtime/index.ts';
|
|
8
8
|
import type { UiEventFeed } from '../runtime/ui-events.ts';
|
|
9
|
+
import type { PanelIntegrationContext } from './types.ts';
|
|
9
10
|
import {
|
|
10
11
|
buildEmptyState,
|
|
11
12
|
buildPanelLine,
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
extendPalette,
|
|
17
18
|
type PanelWorkspaceSection,
|
|
18
19
|
} from './polish.ts';
|
|
20
|
+
import { type ConfirmState, handleConfirmInput, renderConfirmLines } from './confirm-state.ts';
|
|
19
21
|
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
20
22
|
import { formatDuration } from '../utils/format-duration.ts';
|
|
21
23
|
|
|
@@ -33,15 +35,23 @@ interface ToolCallRecord {
|
|
|
33
35
|
callId: string;
|
|
34
36
|
tool: string;
|
|
35
37
|
args: Record<string, unknown>;
|
|
38
|
+
/** Wall-clock time the call was received from the LLM (queued, not yet executing). */
|
|
36
39
|
startMs: number;
|
|
40
|
+
/**
|
|
41
|
+
* Real execution-start time from TOOL_EXECUTING.startedAt — used for duration
|
|
42
|
+
* math when present so displayed durations reflect actual run time rather
|
|
43
|
+
* than time spent in permission/hook queueing. Falls back to `startMs` when
|
|
44
|
+
* a call completes without ever reaching TOOL_EXECUTING (e.g. denied before
|
|
45
|
+
* execution).
|
|
46
|
+
*/
|
|
47
|
+
execStartMs?: number;
|
|
37
48
|
endMs?: number;
|
|
38
49
|
result?: unknown;
|
|
39
50
|
error?: string;
|
|
51
|
+
cancelled?: boolean;
|
|
40
52
|
expanded: boolean;
|
|
41
53
|
approved?: boolean;
|
|
42
54
|
outputClass?: string;
|
|
43
|
-
policyAction?: string;
|
|
44
|
-
spillBackend?: string;
|
|
45
55
|
resultSummary?: string;
|
|
46
56
|
}
|
|
47
57
|
|
|
@@ -80,6 +90,20 @@ function summarizeResult(result: unknown): string | undefined {
|
|
|
80
90
|
return undefined;
|
|
81
91
|
}
|
|
82
92
|
|
|
93
|
+
function statusLabel(rec: ToolCallRecord): string {
|
|
94
|
+
if (rec.endMs === undefined) return 'running';
|
|
95
|
+
if (rec.cancelled) return 'cancelled';
|
|
96
|
+
if (rec.error) return 'error';
|
|
97
|
+
return 'completed';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function statusColor(rec: ToolCallRecord): string {
|
|
101
|
+
if (rec.endMs === undefined) return DEFAULT_PANEL_PALETTE.warn;
|
|
102
|
+
if (rec.cancelled) return DEFAULT_PANEL_PALETTE.dim;
|
|
103
|
+
if (rec.error) return DEFAULT_PANEL_PALETTE.bad;
|
|
104
|
+
return DEFAULT_PANEL_PALETTE.good;
|
|
105
|
+
}
|
|
106
|
+
|
|
83
107
|
function detectOutputClass(tool: string, args: Record<string, unknown>): string {
|
|
84
108
|
const name = tool.toLowerCase();
|
|
85
109
|
if (name.includes('read') || name.includes('find') || name.includes('inspect') || name.includes('grep')) return 'read';
|
|
@@ -97,6 +121,10 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
97
121
|
private scrollOffset = 0;
|
|
98
122
|
private autoScroll = true;
|
|
99
123
|
private _flatCache: FlatRow[] | null = null;
|
|
124
|
+
/** Pending confirm for 'c' clear — destructive (drops call history), so it goes through the project-standard confirm contract. */
|
|
125
|
+
private _confirm: ConfirmState<'clear'> | null = null;
|
|
126
|
+
/** Set by handleInput('a') when the selected call is awaiting a permission decision; consumed by handlePanelIntegrationAction. */
|
|
127
|
+
private _pendingApprovalJump = false;
|
|
100
128
|
|
|
101
129
|
constructor(
|
|
102
130
|
private readonly toolEvents: UiEventFeed<ToolEvent>,
|
|
@@ -124,19 +152,56 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
124
152
|
}
|
|
125
153
|
|
|
126
154
|
handleInput(key: string): boolean {
|
|
155
|
+
const confirmResult = handleConfirmInput(this._confirm, key);
|
|
156
|
+
if (confirmResult === 'confirmed') {
|
|
157
|
+
this.records = [];
|
|
158
|
+
this._confirm = null;
|
|
159
|
+
this.markDirty();
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
if (confirmResult === 'cancelled') {
|
|
163
|
+
this._confirm = null;
|
|
164
|
+
this.markDirty();
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
if (confirmResult === 'absorbed') return true;
|
|
168
|
+
|
|
127
169
|
switch (key) {
|
|
128
170
|
case 'up': this._move(-1); this.autoScroll = false; return true;
|
|
129
171
|
case 'down': this._move(1); return true;
|
|
130
172
|
case 'pageup': this._move(-10); this.autoScroll = false; return true;
|
|
131
173
|
case 'pagedown': this._move(10); return true;
|
|
132
174
|
case 'return': this._toggleExpand(); return true;
|
|
133
|
-
case 'c':
|
|
175
|
+
case 'c': {
|
|
176
|
+
if (this.records.length === 0) return false;
|
|
177
|
+
this._confirm = { subject: 'clear', label: `${this.records.length} tool call${this.records.length === 1 ? '' : 's'}`, verb: 'Clear' };
|
|
178
|
+
this.markDirty();
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
134
181
|
case 'g': this.autoScroll = true; this.markDirty(); return true;
|
|
135
182
|
case 'f': this._cycleFilter(); return true;
|
|
183
|
+
case 'a': {
|
|
184
|
+
const rec = this._selectedRecord();
|
|
185
|
+
if (!rec || rec.approved !== undefined || rec.endMs !== undefined) return false;
|
|
186
|
+
this._pendingApprovalJump = true;
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
136
189
|
default: return false;
|
|
137
190
|
}
|
|
138
191
|
}
|
|
139
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Cross-panel hook — invoked immediately after handleInput('a') returns true
|
|
195
|
+
* for a call still awaiting a permission decision. Jumps to the Approval
|
|
196
|
+
* panel; the actual PanelManager reference is only available here.
|
|
197
|
+
*/
|
|
198
|
+
handlePanelIntegrationAction(_key: string, ctx: PanelIntegrationContext): boolean {
|
|
199
|
+
if (!this._pendingApprovalJump) return false;
|
|
200
|
+
this._pendingApprovalJump = false;
|
|
201
|
+
ctx.panelManager.open('approval');
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
140
205
|
render(width: number, height: number): Line[] {
|
|
141
206
|
if (height <= 0 || width <= 0) return [];
|
|
142
207
|
|
|
@@ -157,8 +222,20 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
157
222
|
footerSegments.push([' c', DEFAULT_PANEL_PALETTE.info], [' clear', DEFAULT_PANEL_PALETTE.dim]);
|
|
158
223
|
footerSegments.push([' g', DEFAULT_PANEL_PALETTE.info], [this.autoScroll ? ' end (live)' : ' jump to end', DEFAULT_PANEL_PALETTE.dim]);
|
|
159
224
|
}
|
|
225
|
+
const selectedRecordForFooter = this._selectedRecord();
|
|
226
|
+
if (selectedRecordForFooter && selectedRecordForFooter.approved === undefined && selectedRecordForFooter.endMs === undefined) {
|
|
227
|
+
footerSegments.push([' a', DEFAULT_PANEL_PALETTE.info], [' review approval', DEFAULT_PANEL_PALETTE.dim]);
|
|
228
|
+
}
|
|
160
229
|
const footerLines = [buildPanelLine(width, footerSegments)];
|
|
161
230
|
|
|
231
|
+
if (this._confirm) {
|
|
232
|
+
return buildPanelWorkspace(width, height, {
|
|
233
|
+
title,
|
|
234
|
+
sections: [{ title: 'Confirmation', lines: renderConfirmLines(width, this._confirm) }],
|
|
235
|
+
palette: DEFAULT_PANEL_PALETTE,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
162
239
|
const flat = this._getFlat();
|
|
163
240
|
|
|
164
241
|
if (this.autoScroll) {
|
|
@@ -168,7 +245,7 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
168
245
|
if (flat.length === 0) {
|
|
169
246
|
return buildPanelWorkspace(width, height, {
|
|
170
247
|
title,
|
|
171
|
-
intro: 'Inspect chronological tool activity, arguments, results, errors, and running calls.',
|
|
248
|
+
intro: 'Inspect chronological tool activity, arguments, results, errors, and running calls — including calls made by delegated subagents.',
|
|
172
249
|
sections: [
|
|
173
250
|
{
|
|
174
251
|
title: 'Calls',
|
|
@@ -179,7 +256,7 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
179
256
|
: ' No tool calls yet',
|
|
180
257
|
this.records.length > 0 && this.filterMode !== 'all'
|
|
181
258
|
? '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.',
|
|
259
|
+
: 'Tool executions appear here as the agent works, including calls made by delegated subagents. Expand a call to inspect its arguments and result payload.',
|
|
183
260
|
this.records.length > 0 && this.filterMode !== 'all'
|
|
184
261
|
? [{ command: 'f', summary: 'cycle the tool filter back to all calls' }]
|
|
185
262
|
: [{ command: '/spawn <task>', summary: 'run an agent to populate the tool-call timeline' }],
|
|
@@ -215,22 +292,14 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
215
292
|
: this.records.filter(r => r.tool === this.filterMode);
|
|
216
293
|
const rec = filtered[selected.recordIndex];
|
|
217
294
|
if (rec) {
|
|
218
|
-
detailLines.push(buildPanelLine(width, [[' Tool ', DEFAULT_PANEL_PALETTE.label], [rec.tool, DEFAULT_PANEL_PALETTE.info], [' Started ', DEFAULT_PANEL_PALETTE.label], [shortTime(rec.startMs), DEFAULT_PANEL_PALETTE.value]]));
|
|
219
|
-
detailLines.push(buildPanelLine(width, [[' Status ', DEFAULT_PANEL_PALETTE.label], [rec
|
|
295
|
+
detailLines.push(buildPanelLine(width, [[' Tool ', DEFAULT_PANEL_PALETTE.label], [rec.tool, DEFAULT_PANEL_PALETTE.info], [' Started ', DEFAULT_PANEL_PALETTE.label], [shortTime(rec.execStartMs ?? rec.startMs), DEFAULT_PANEL_PALETTE.value]]));
|
|
296
|
+
detailLines.push(buildPanelLine(width, [[' Status ', DEFAULT_PANEL_PALETTE.label], [statusLabel(rec), statusColor(rec)]]));
|
|
220
297
|
detailLines.push(buildPanelLine(width, [
|
|
221
|
-
['
|
|
298
|
+
[' Class ', DEFAULT_PANEL_PALETTE.label],
|
|
222
299
|
[rec.outputClass ?? detectOutputClass(rec.tool, rec.args), DEFAULT_PANEL_PALETTE.warn],
|
|
223
300
|
[' Approved ', DEFAULT_PANEL_PALETTE.label],
|
|
224
301
|
[rec.approved === undefined ? 'unknown' : rec.approved ? 'yes' : 'no', rec.approved === false ? DEFAULT_PANEL_PALETTE.bad : DEFAULT_PANEL_PALETTE.value],
|
|
225
302
|
]));
|
|
226
|
-
if (rec.policyAction || rec.spillBackend) {
|
|
227
|
-
detailLines.push(buildPanelLine(width, [
|
|
228
|
-
[' Output ', DEFAULT_PANEL_PALETTE.label],
|
|
229
|
-
[rec.policyAction ?? 'none', rec.policyAction && rec.policyAction !== 'none' ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.value],
|
|
230
|
-
[' Spill ', DEFAULT_PANEL_PALETTE.label],
|
|
231
|
-
[rec.spillBackend ?? 'none', rec.spillBackend ? DEFAULT_PANEL_PALETTE.info : DEFAULT_PANEL_PALETTE.dim],
|
|
232
|
-
]));
|
|
233
|
-
}
|
|
234
303
|
if (rec.resultSummary) {
|
|
235
304
|
detailLines.push(buildPanelLine(width, [[' Summary ', DEFAULT_PANEL_PALETTE.label], [rec.resultSummary, DEFAULT_PANEL_PALETTE.value]]));
|
|
236
305
|
}
|
|
@@ -240,7 +309,7 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
240
309
|
|
|
241
310
|
const selectedSection: PanelWorkspaceSection = { title: 'Selected', lines: detailLines };
|
|
242
311
|
const callsSection = resolveScrollablePanelSection(width, height, {
|
|
243
|
-
intro: 'Inspect chronological tool activity, arguments, results, errors, and running calls.',
|
|
312
|
+
intro: 'Inspect chronological tool activity, arguments, results, errors, and running calls — including calls made by delegated subagents.',
|
|
244
313
|
footerLines,
|
|
245
314
|
palette: DEFAULT_PANEL_PALETTE,
|
|
246
315
|
beforeSections: [summary],
|
|
@@ -257,7 +326,7 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
257
326
|
|
|
258
327
|
return buildPanelWorkspace(width, height, {
|
|
259
328
|
title,
|
|
260
|
-
intro: 'Inspect chronological tool activity, arguments, results, errors, and running calls.',
|
|
329
|
+
intro: 'Inspect chronological tool activity, arguments, results, errors, and running calls — including calls made by delegated subagents.',
|
|
261
330
|
sections: [
|
|
262
331
|
summary,
|
|
263
332
|
callsSection.section,
|
|
@@ -291,25 +360,24 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
291
360
|
|
|
292
361
|
for (let i = 0; i < filtered.length; i++) {
|
|
293
362
|
const rec = filtered[i]!;
|
|
294
|
-
|
|
363
|
+
// Duration is measured from real execution start (TOOL_EXECUTING.startedAt) when
|
|
364
|
+
// known, not from queue/receive time, so it reflects actual run time rather than
|
|
365
|
+
// time spent waiting on permission/hook checks. Falls back to startMs for calls
|
|
366
|
+
// that never reached TOOL_EXECUTING (e.g. denied before execution).
|
|
367
|
+
const durBaseMs = rec.execStartMs ?? rec.startMs;
|
|
368
|
+
const dur = rec.cancelled
|
|
369
|
+
? ` (cancelled${rec.endMs !== undefined ? ` ${formatMs(rec.endMs - durBaseMs)}` : ''})`
|
|
370
|
+
: rec.endMs !== undefined ? ` (${formatMs(rec.endMs - durBaseMs)})` : ' (running)';
|
|
295
371
|
const expand = rec.expanded ? ' ▾' : ' ▸';
|
|
296
372
|
const ts = shortTime(rec.startMs);
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
const callText = `${ts} ${rec.tool} [${risk}]${action}${dur}${expand}`;
|
|
373
|
+
const cls = rec.outputClass ?? detectOutputClass(rec.tool, rec.args);
|
|
374
|
+
const callText = `${ts} ${rec.tool} [${cls}]${dur}${expand}`;
|
|
300
375
|
flat.push({ kind: 'call', recordIndex: i, text: callText });
|
|
301
376
|
|
|
302
377
|
if (rec.expanded) {
|
|
303
378
|
const argsStr = truncateJson(rec.args, 200);
|
|
304
379
|
flat.push({ kind: 'detail', text: `Args: ${argsStr}`, isError: false });
|
|
305
|
-
flat.push({ kind: 'detail', text: `
|
|
306
|
-
if (rec.policyAction || rec.spillBackend) {
|
|
307
|
-
flat.push({
|
|
308
|
-
kind: 'detail',
|
|
309
|
-
text: `Output policy: ${rec.policyAction ?? 'none'}${rec.spillBackend ? ` Spill: ${rec.spillBackend}` : ''}`,
|
|
310
|
-
isError: false,
|
|
311
|
-
});
|
|
312
|
-
}
|
|
380
|
+
flat.push({ kind: 'detail', text: `Class: ${cls}${rec.approved === undefined ? '' : ` Approved: ${rec.approved ? 'yes' : 'no'}`}`, isError: false });
|
|
313
381
|
if (rec.resultSummary) {
|
|
314
382
|
flat.push({ kind: 'detail', text: `Summary: ${rec.resultSummary}`, isError: false });
|
|
315
383
|
}
|
|
@@ -338,6 +406,17 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
338
406
|
}
|
|
339
407
|
}
|
|
340
408
|
|
|
409
|
+
/** The tool-call record backing the currently-selected flat row, if any. */
|
|
410
|
+
private _selectedRecord(): ToolCallRecord | undefined {
|
|
411
|
+
const flat = this._getFlat();
|
|
412
|
+
const row = flat[this.cursorIndex];
|
|
413
|
+
if (!row || row.kind !== 'call') return undefined;
|
|
414
|
+
const filtered = this.filterMode === 'all'
|
|
415
|
+
? this.records
|
|
416
|
+
: this.records.filter(r => r.tool === this.filterMode);
|
|
417
|
+
return filtered[row.recordIndex];
|
|
418
|
+
}
|
|
419
|
+
|
|
341
420
|
private _cycleFilter(): void {
|
|
342
421
|
const tools = [...new Set(this.records.map(r => r.tool))];
|
|
343
422
|
if (tools.length === 0) return;
|
|
@@ -350,6 +429,18 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
350
429
|
this.markDirty();
|
|
351
430
|
}
|
|
352
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Public counterpart to the 'f' cycle — sets the filter directly to a named
|
|
434
|
+
* tool. Used by cross-panel jumps (WO-136: DocsPanel Enter on a tool row)
|
|
435
|
+
* so the inspector opens already scoped to the tool the operator picked.
|
|
436
|
+
*/
|
|
437
|
+
public filterByTool(tool: string): void {
|
|
438
|
+
this.filterMode = tool;
|
|
439
|
+
this.cursorIndex = 0;
|
|
440
|
+
this.autoScroll = false;
|
|
441
|
+
this.markDirty();
|
|
442
|
+
}
|
|
443
|
+
|
|
353
444
|
private _move(delta: number): void {
|
|
354
445
|
const flat = this._getFlat();
|
|
355
446
|
if (flat.length === 0) return;
|
|
@@ -382,11 +473,17 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
382
473
|
this.markDirty();
|
|
383
474
|
}));
|
|
384
475
|
|
|
476
|
+
// Real execution start — recorded separately from TOOL_RECEIVED's queue time so
|
|
477
|
+
// displayed durations reflect actual run time, not time spent waiting on
|
|
478
|
+
// permission/hook checks ahead of execution.
|
|
479
|
+
this.unsubs.push(this.toolEvents.on('TOOL_EXECUTING', (data) => {
|
|
480
|
+
const rec = this.records.findLast(r => r.callId === data.callId);
|
|
481
|
+
if (rec) rec.execStartMs = data.startedAt;
|
|
482
|
+
this.markDirty();
|
|
483
|
+
}));
|
|
484
|
+
|
|
385
485
|
// NOTE: After SDK OBS-05 (0.21.31), TOOL_SUCCEEDED/TOOL_FAILED.result is a ToolResultSummary
|
|
386
|
-
// ({ kind, byteSize, preview? }) rather than the raw ToolResult object.
|
|
387
|
-
// `_policyAudit` extraction is no longer reachable via this event — policy audit metadata
|
|
388
|
-
// must be sourced from a different channel (approval broker / tool result store) if the
|
|
389
|
-
// Tool Inspector is to display it in future.
|
|
486
|
+
// ({ kind, byteSize, preview? }) rather than the raw ToolResult object.
|
|
390
487
|
this.unsubs.push(this.toolEvents.on('TOOL_SUCCEEDED', (data) => {
|
|
391
488
|
const rec = this.records.findLast(r => r.callId === data.callId);
|
|
392
489
|
if (rec) {
|
|
@@ -408,6 +505,19 @@ export class ToolInspectorPanel extends BasePanel {
|
|
|
408
505
|
this.markDirty();
|
|
409
506
|
}));
|
|
410
507
|
|
|
508
|
+
// Terminal state for a cancelled call — without this subscription, a call
|
|
509
|
+
// cancelled mid-flight (e.g. via Ctrl+C) never gets an endMs and shows as
|
|
510
|
+
// "running" forever.
|
|
511
|
+
this.unsubs.push(this.toolEvents.on('TOOL_CANCELLED', (data) => {
|
|
512
|
+
const rec = this.records.findLast(r => r.callId === data.callId);
|
|
513
|
+
if (rec) {
|
|
514
|
+
rec.endMs = Date.now();
|
|
515
|
+
rec.cancelled = true;
|
|
516
|
+
rec.resultSummary = data.reason ?? 'cancelled';
|
|
517
|
+
}
|
|
518
|
+
this.markDirty();
|
|
519
|
+
}));
|
|
520
|
+
|
|
411
521
|
this.unsubs.push(this.turnEvents.on('TURN_ERROR', (data) => {
|
|
412
522
|
// Mark any running calls as errored
|
|
413
523
|
for (const rec of this.records) {
|
package/src/panels/types.ts
CHANGED
|
@@ -43,7 +43,28 @@ export type NamedKey =
|
|
|
43
43
|
*/
|
|
44
44
|
export type KeyName = NamedKey | (string & {});
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* WO-152: the former single 'monitoring' bucket held 33 panels pre-merge —
|
|
48
|
+
* too coarse for the picker to be useful and too large for any one operator
|
|
49
|
+
* mental model. Split into named operator domains so each category holds a
|
|
50
|
+
* bounded, coherent set (no category may exceed 10 registrations):
|
|
51
|
+
* - providers: provider/model connectivity, cost, and token usage
|
|
52
|
+
* - security-policy: auth, policy governance, and isolation posture
|
|
53
|
+
* - automation-control: hooks, plugins, marketplace, automation jobs, worktrees
|
|
54
|
+
* - incidents-diagnostics: failure review, eval gates, API call debugging
|
|
55
|
+
* - runtime-ops: live operator consoles (cockpit, tasks, orchestration, comms)
|
|
56
|
+
* 'development' | 'agent' | 'session' | 'ai' are unchanged from before the split.
|
|
57
|
+
*/
|
|
58
|
+
export type PanelCategory =
|
|
59
|
+
| 'development'
|
|
60
|
+
| 'agent'
|
|
61
|
+
| 'session'
|
|
62
|
+
| 'ai'
|
|
63
|
+
| 'providers'
|
|
64
|
+
| 'security-policy'
|
|
65
|
+
| 'automation-control'
|
|
66
|
+
| 'incidents-diagnostics'
|
|
67
|
+
| 'runtime-ops';
|
|
47
68
|
|
|
48
69
|
export interface Panel {
|
|
49
70
|
id: string;
|
|
@@ -98,9 +119,24 @@ export interface PanelRegistration extends Pick<Panel, 'id' | 'name' | 'icon' |
|
|
|
98
119
|
factory: () => Panel;
|
|
99
120
|
description: string;
|
|
100
121
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
122
|
+
* WO-152 lifecycle flags. `preload` and `retainOnClose` are independent —
|
|
123
|
+
* a panel can eagerly instantiate without being kept alive on close, or be
|
|
124
|
+
* kept alive on close without eager bootstrap instantiation. They happened
|
|
125
|
+
* to be identical for all builtin panels before this split (both driven by
|
|
126
|
+
* a single `preload: true`); that existing 10-panel retained set is
|
|
127
|
+
* preserved unchanged by giving each of those 10 registrations both flags.
|
|
128
|
+
*
|
|
129
|
+
* - `preload` — instantiate this panel during `PanelManager.prewarmRegistered()`
|
|
130
|
+
* (called once at bootstrap) so its factory runs and it starts
|
|
131
|
+
* accumulating background data (subscriptions, timers) before the user
|
|
132
|
+
* ever opens the workspace. Panels without `preload` are instantiated
|
|
133
|
+
* lazily on first `open()`.
|
|
134
|
+
* - `retainOnClose` — when this panel is closed, keep the live instance in
|
|
135
|
+
* `PanelManager`'s retained-panel map (background subscriptions/timers
|
|
136
|
+
* keep running) instead of calling `onDestroy()`. Reopening returns the
|
|
137
|
+
* same instance with its accumulated state intact. Panels without
|
|
138
|
+
* `retainOnClose` are destroyed on close and rebuilt fresh next open.
|
|
104
139
|
*/
|
|
105
140
|
preload?: boolean;
|
|
141
|
+
retainOnClose?: boolean;
|
|
106
142
|
}
|