@pellux/goodvibes-agent 0.1.69 → 0.1.71
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 +12 -0
- package/package.json +42 -1
- package/src/agent/skill-discovery.ts +119 -0
- package/src/input/commands/delegation-runtime.ts +0 -8
- package/src/input/commands/experience-runtime.ts +0 -177
- package/src/input/commands/guidance-runtime.ts +9 -77
- package/src/input/commands/local-runtime.ts +1 -57
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/operator-runtime.ts +1 -145
- package/src/input/commands/platform-access-runtime.ts +2 -195
- package/src/input/commands/product-runtime.ts +0 -116
- package/src/input/commands/security-runtime.ts +88 -0
- package/src/input/commands/session-content.ts +0 -97
- package/src/input/commands/shell-core.ts +1 -22
- package/src/input/commands.ts +2 -43
- package/src/panels/builtin/operations.ts +3 -184
- package/src/panels/index.ts +0 -11
- package/src/version.ts +1 -1
- package/src/input/commands/branch-runtime.ts +0 -72
- package/src/input/commands/control-room-runtime.ts +0 -234
- package/src/input/commands/discovery-runtime.ts +0 -61
- package/src/input/commands/hooks-runtime.ts +0 -207
- package/src/input/commands/incident-runtime.ts +0 -106
- package/src/input/commands/integration-runtime.ts +0 -437
- package/src/input/commands/local-setup.ts +0 -288
- package/src/input/commands/managed-runtime.ts +0 -240
- package/src/input/commands/marketplace-runtime.ts +0 -305
- package/src/input/commands/memory-product-runtime.ts +0 -148
- package/src/input/commands/operator-panel-runtime.ts +0 -146
- package/src/input/commands/platform-services-runtime.ts +0 -271
- package/src/input/commands/profile-sync-runtime.ts +0 -110
- package/src/input/commands/provider.ts +0 -363
- package/src/input/commands/remote-runtime-pool.ts +0 -89
- package/src/input/commands/remote-runtime-setup.ts +0 -226
- package/src/input/commands/remote-runtime.ts +0 -432
- package/src/input/commands/replay-runtime.ts +0 -25
- package/src/input/commands/services-runtime.ts +0 -220
- package/src/input/commands/settings-sync-runtime.ts +0 -197
- package/src/input/commands/share-runtime.ts +0 -127
- package/src/input/commands/skills-runtime.ts +0 -226
- package/src/input/commands/teleport-runtime.ts +0 -68
- package/src/panels/cockpit-panel.ts +0 -183
- package/src/panels/communication-panel.ts +0 -153
- package/src/panels/control-plane-panel.ts +0 -211
- package/src/panels/forensics-panel.ts +0 -364
- package/src/panels/hooks-panel.ts +0 -239
- package/src/panels/incident-review-panel.ts +0 -197
- package/src/panels/marketplace-panel.ts +0 -212
- package/src/panels/ops-control-panel.ts +0 -150
- package/src/panels/ops-strategy-panel.ts +0 -235
- package/src/panels/orchestration-panel.ts +0 -272
- package/src/panels/plugins-panel.ts +0 -178
- package/src/panels/remote-panel.ts +0 -449
- package/src/panels/routes-panel.ts +0 -178
- package/src/panels/services-panel.ts +0 -231
- package/src/panels/settings-sync-panel.ts +0 -120
- package/src/panels/skills-panel.ts +0 -431
- package/src/panels/watchers-panel.ts +0 -193
- package/src/verification/live-verifier.ts +0 -588
- package/src/verification/verification-ledger.ts +0 -239
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ops Strategy Timeline Panel.
|
|
3
|
-
*
|
|
4
|
-
* Renders the Adaptive Execution Planner state: current strategy, reason
|
|
5
|
-
* code, mode, override status, and a scrollable history of past decisions.
|
|
6
|
-
*
|
|
7
|
-
* Registered as panel id 'ops' in builtin-panels.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { BasePanel } from './base-panel.ts';
|
|
11
|
-
import type { Line } from '../types/grid.ts';
|
|
12
|
-
import type { PlannerDecision, ExecutionStrategy } from '@pellux/goodvibes-sdk/platform/core';
|
|
13
|
-
import type { PlannerEvent } from '@/runtime/index.ts';
|
|
14
|
-
import type { UiEventFeed } from '../runtime/ui-events.ts';
|
|
15
|
-
import type { OpsStrategyQuery } from '../runtime/ui-service-queries.ts';
|
|
16
|
-
import {
|
|
17
|
-
buildEmptyState,
|
|
18
|
-
buildPanelLine,
|
|
19
|
-
buildStyledPanelLine,
|
|
20
|
-
buildPanelWorkspace,
|
|
21
|
-
resolveScrollablePanelSection,
|
|
22
|
-
DEFAULT_PANEL_PALETTE,
|
|
23
|
-
} from './polish.ts';
|
|
24
|
-
|
|
25
|
-
const STRATEGY_FG: Record<ExecutionStrategy, string> = {
|
|
26
|
-
auto: '#00cccc',
|
|
27
|
-
single: '#00cc66',
|
|
28
|
-
cohort: '#cccc00',
|
|
29
|
-
background: '#cc66cc',
|
|
30
|
-
remote: '#cccccc',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const STRATEGY_ICON: Record<ExecutionStrategy, string> = {
|
|
34
|
-
auto: '~',
|
|
35
|
-
single: '▸',
|
|
36
|
-
cohort: '◆',
|
|
37
|
-
background: '.',
|
|
38
|
-
remote: '▸',
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
// OpsStrategyPanel
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
|
|
45
|
-
export class OpsStrategyPanel extends BasePanel {
|
|
46
|
-
private unsubscribers: Array<() => void> = [];
|
|
47
|
-
private scrollOffset = 0;
|
|
48
|
-
private history: PlannerDecision[] = [];
|
|
49
|
-
private readonly adaptivePlanner: OpsStrategyQuery;
|
|
50
|
-
|
|
51
|
-
constructor(
|
|
52
|
-
private readonly plannerEvents: UiEventFeed<PlannerEvent>,
|
|
53
|
-
adaptivePlanner: OpsStrategyQuery,
|
|
54
|
-
) {
|
|
55
|
-
super('ops', 'Ops', 'O', 'agent');
|
|
56
|
-
this.adaptivePlanner = adaptivePlanner;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
override onActivate(): void {
|
|
60
|
-
super.onActivate();
|
|
61
|
-
this._syncHistory();
|
|
62
|
-
this.unsubscribers.push(
|
|
63
|
-
this.plannerEvents.on('PLAN_STRATEGY_SELECTED', () => {
|
|
64
|
-
this._syncHistory();
|
|
65
|
-
this.markDirty();
|
|
66
|
-
}),
|
|
67
|
-
this.plannerEvents.on('PLAN_STRATEGY_OVERRIDDEN', () => {
|
|
68
|
-
this._syncHistory();
|
|
69
|
-
this.markDirty();
|
|
70
|
-
}),
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
override onDestroy(): void {
|
|
75
|
-
for (const unsub of this.unsubscribers) unsub();
|
|
76
|
-
this.unsubscribers = [];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
handleInput(key: string): boolean {
|
|
80
|
-
if (key === 'up' || key === 'k') {
|
|
81
|
-
this.scrollOffset = Math.max(0, this.scrollOffset - 1);
|
|
82
|
-
this.markDirty();
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
if (key === 'down' || key === 'j') {
|
|
86
|
-
this.scrollOffset++;
|
|
87
|
-
this.markDirty();
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
if (key === 'g') {
|
|
91
|
-
this.scrollOffset = 0;
|
|
92
|
-
this.markDirty();
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
if (key === 'G') {
|
|
96
|
-
this.scrollOffset = Math.max(0, this.history.length - 1);
|
|
97
|
-
this.markDirty();
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
render(width: number, height: number): Line[] {
|
|
104
|
-
const latest = this.adaptivePlanner.getLatest();
|
|
105
|
-
const mode = this.adaptivePlanner.getMode();
|
|
106
|
-
const override = this.adaptivePlanner.getOverride();
|
|
107
|
-
const statusLines: Line[] = [
|
|
108
|
-
buildPanelLine(width, [
|
|
109
|
-
[' Mode ', DEFAULT_PANEL_PALETTE.label],
|
|
110
|
-
[mode.toUpperCase(), DEFAULT_PANEL_PALETTE.value],
|
|
111
|
-
[' Override ', DEFAULT_PANEL_PALETTE.label],
|
|
112
|
-
[override ? `${override.toUpperCase()} [ACTIVE]` : 'none', override ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.dim],
|
|
113
|
-
]),
|
|
114
|
-
];
|
|
115
|
-
|
|
116
|
-
if (latest) {
|
|
117
|
-
statusLines.push(buildPanelLine(width, [
|
|
118
|
-
[' Last ', DEFAULT_PANEL_PALETTE.label],
|
|
119
|
-
[`${STRATEGY_ICON[latest.selected]} ${latest.selected.toUpperCase()}`, STRATEGY_FG[latest.selected]],
|
|
120
|
-
[' Reason ', DEFAULT_PANEL_PALETTE.label],
|
|
121
|
-
[latest.reasonCode, DEFAULT_PANEL_PALETTE.dim],
|
|
122
|
-
]));
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (this.history.length === 0) {
|
|
126
|
-
return buildPanelWorkspace(width, height, {
|
|
127
|
-
title: ' Ops Strategy',
|
|
128
|
-
intro: 'Review adaptive execution planner decisions, overrides, and recent strategy history.',
|
|
129
|
-
sections: [
|
|
130
|
-
{ title: 'Status', lines: statusLines },
|
|
131
|
-
{
|
|
132
|
-
lines: buildEmptyState(
|
|
133
|
-
width,
|
|
134
|
-
' No decisions recorded yet',
|
|
135
|
-
'Adaptive planner decisions appear here once the planner begins selecting strategies.',
|
|
136
|
-
[],
|
|
137
|
-
DEFAULT_PANEL_PALETTE,
|
|
138
|
-
),
|
|
139
|
-
},
|
|
140
|
-
],
|
|
141
|
-
footerLines: [
|
|
142
|
-
buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' scroll history', DEFAULT_PANEL_PALETTE.dim], [' g/G', DEFAULT_PANEL_PALETTE.info], [' top/bottom', DEFAULT_PANEL_PALETTE.dim]]),
|
|
143
|
-
],
|
|
144
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const historyLines = this._renderHistory(width);
|
|
149
|
-
const statusSection = { title: 'Status', lines: statusLines } as const;
|
|
150
|
-
const historySection = resolveScrollablePanelSection(width, height, {
|
|
151
|
-
intro: 'Review adaptive execution planner decisions, overrides, and recent strategy history.',
|
|
152
|
-
footerLines: [
|
|
153
|
-
buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' scroll history', DEFAULT_PANEL_PALETTE.dim], [' g/G', DEFAULT_PANEL_PALETTE.info], [' top/bottom', DEFAULT_PANEL_PALETTE.dim]]),
|
|
154
|
-
],
|
|
155
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
156
|
-
beforeSections: [statusSection],
|
|
157
|
-
section: {
|
|
158
|
-
title: 'Decision History',
|
|
159
|
-
scrollableLines: historyLines,
|
|
160
|
-
scrollOffset: Math.min(this.scrollOffset, Math.max(0, historyLines.length - 1)),
|
|
161
|
-
minRows: 8,
|
|
162
|
-
},
|
|
163
|
-
});
|
|
164
|
-
this.scrollOffset = historySection.scrollOffset;
|
|
165
|
-
|
|
166
|
-
return buildPanelWorkspace(width, height, {
|
|
167
|
-
title: ' Ops Strategy',
|
|
168
|
-
intro: 'Review adaptive execution planner decisions, overrides, and recent strategy history.',
|
|
169
|
-
sections: [
|
|
170
|
-
statusSection,
|
|
171
|
-
historySection.section,
|
|
172
|
-
],
|
|
173
|
-
footerLines: [
|
|
174
|
-
buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' scroll history', DEFAULT_PANEL_PALETTE.dim], [' g/G', DEFAULT_PANEL_PALETTE.info], [' top/bottom', DEFAULT_PANEL_PALETTE.dim]]),
|
|
175
|
-
],
|
|
176
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// -------------------------------------------------------------------------
|
|
181
|
-
// Private helpers
|
|
182
|
-
// -------------------------------------------------------------------------
|
|
183
|
-
|
|
184
|
-
private _syncHistory(): void {
|
|
185
|
-
this.history = this.adaptivePlanner.getHistory(50);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private _renderHistory(width: number): Line[] {
|
|
189
|
-
if (this.history.length === 0) {
|
|
190
|
-
return [buildStyledPanelLine(width, [{ text: ' No history yet.', fg: DEFAULT_PANEL_PALETTE.dim, dim: true }])];
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const lines: Line[] = [];
|
|
194
|
-
lines.push(buildStyledPanelLine(width, [{ text: ' Decision History', fg: DEFAULT_PANEL_PALETTE.value, bold: true }]));
|
|
195
|
-
|
|
196
|
-
const reversed = [...this.history].reverse();
|
|
197
|
-
for (let i = 0; i < reversed.length; i++) {
|
|
198
|
-
const d = reversed[i]!;
|
|
199
|
-
const ts = new Date(d.timestamp).toLocaleTimeString();
|
|
200
|
-
const fg = STRATEGY_FG[d.selected];
|
|
201
|
-
const icon = STRATEGY_ICON[d.selected];
|
|
202
|
-
const num = String(i + 1).padStart(3);
|
|
203
|
-
const overrideMark = d.overrideActive ? ' [O]' : '';
|
|
204
|
-
|
|
205
|
-
// Row 1: index + icon + strategy + timestamp (right-aligned)
|
|
206
|
-
const leftBase = ` ${num}. ${icon} ${d.selected.toUpperCase()}${overrideMark}`;
|
|
207
|
-
const rightText = ` ${ts}`;
|
|
208
|
-
const pad = Math.max(1, width - leftBase.length - rightText.length);
|
|
209
|
-
lines.push(buildStyledPanelLine(width, [
|
|
210
|
-
{ text: ` ${num}. `, fg: DEFAULT_PANEL_PALETTE.dim, dim: true },
|
|
211
|
-
{ text: `${icon} ${d.selected.toUpperCase()}`, fg, bold: true },
|
|
212
|
-
{ text: overrideMark, fg: DEFAULT_PANEL_PALETTE.warn },
|
|
213
|
-
{ text: ' '.repeat(pad), fg: DEFAULT_PANEL_PALETTE.dim },
|
|
214
|
-
{ text: rightText, fg: DEFAULT_PANEL_PALETTE.dim, dim: true },
|
|
215
|
-
]));
|
|
216
|
-
|
|
217
|
-
// Row 2: reason code
|
|
218
|
-
lines.push(buildStyledPanelLine(width, [{ text: ` ${d.reasonCode}`, fg: DEFAULT_PANEL_PALETTE.dim, dim: true }]));
|
|
219
|
-
|
|
220
|
-
// Row 3+: top-2 scored candidates (auto mode only)
|
|
221
|
-
if (!d.overrideActive && d.candidates.length > 1) {
|
|
222
|
-
const top2 = d.candidates.slice(0, 2);
|
|
223
|
-
for (const c of top2) {
|
|
224
|
-
lines.push(buildStyledPanelLine(width, [
|
|
225
|
-
{ text: ' ', fg: DEFAULT_PANEL_PALETTE.dim, dim: true },
|
|
226
|
-
{ text: c.strategy.padEnd(12), fg: STRATEGY_FG[c.strategy] },
|
|
227
|
-
{ text: ` score ${c.score}`, fg: DEFAULT_PANEL_PALETTE.dim, dim: true },
|
|
228
|
-
]));
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return lines;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OrchestrationPanel — displays task graphs, node contracts, and recursion guards.
|
|
3
|
-
*
|
|
4
|
-
* Migrated (Wave B2): extends ScrollableListPanel<OrchestrationGraphRecord>.
|
|
5
|
-
* Navigation (up/down/j/k) is handled by the base class.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { Line } from '../types/grid.ts';
|
|
9
|
-
import { createEmptyLine } from '../types/grid.ts';
|
|
10
|
-
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
11
|
-
import type { UiOrchestrationSnapshot, UiReadModel } from '../runtime/ui-read-models.ts';
|
|
12
|
-
import type { OrchestrationGraphRecord } from '@/runtime/index.ts';
|
|
13
|
-
import {
|
|
14
|
-
buildEmptyState,
|
|
15
|
-
buildGuidanceLine,
|
|
16
|
-
buildKeyValueLine,
|
|
17
|
-
buildPanelLine,
|
|
18
|
-
buildPanelWorkspace,
|
|
19
|
-
resolveScrollablePanelSection,
|
|
20
|
-
DEFAULT_PANEL_PALETTE,
|
|
21
|
-
extendPalette,
|
|
22
|
-
type PanelPalette,
|
|
23
|
-
type PanelWorkspaceSection,
|
|
24
|
-
} from './polish.ts';
|
|
25
|
-
|
|
26
|
-
const C = extendPalette(DEFAULT_PANEL_PALETTE, {
|
|
27
|
-
header: '#94a3b8',
|
|
28
|
-
headerBg: '#1e293b',
|
|
29
|
-
running: '#22c55e',
|
|
30
|
-
ready: '#38bdf8',
|
|
31
|
-
blocked: '#f59e0b',
|
|
32
|
-
failed: '#ef4444',
|
|
33
|
-
completed: '#a78bfa',
|
|
34
|
-
selectBg: '#0f172a',
|
|
35
|
-
} as const);
|
|
36
|
-
|
|
37
|
-
function statusColor(status: string): string {
|
|
38
|
-
switch (status) {
|
|
39
|
-
case 'ready': return C.ready;
|
|
40
|
-
case 'running': return C.running;
|
|
41
|
-
case 'blocked': return C.blocked;
|
|
42
|
-
case 'failed': return C.failed;
|
|
43
|
-
case 'completed': return C.completed;
|
|
44
|
-
default: return C.dim;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export class OrchestrationPanel extends ScrollableListPanel<OrchestrationGraphRecord> {
|
|
49
|
-
private readonly readModel?: UiReadModel<UiOrchestrationSnapshot>;
|
|
50
|
-
private readonly unsub: (() => void) | null;
|
|
51
|
-
|
|
52
|
-
public constructor(readModel?: UiReadModel<UiOrchestrationSnapshot>) {
|
|
53
|
-
super('orchestration', 'Orchestration', 'Q', 'monitoring');
|
|
54
|
-
this.readModel = readModel;
|
|
55
|
-
this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
public override onDestroy(): void {
|
|
59
|
-
this.unsub?.();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
// ScrollableListPanel contract
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
|
|
66
|
-
protected getItems(): readonly OrchestrationGraphRecord[] {
|
|
67
|
-
if (!this.readModel) return [];
|
|
68
|
-
return [...this.readModel.getSnapshot().graphs].sort((a, b) => b.createdAt - a.createdAt);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
protected renderItem(
|
|
72
|
-
graph: OrchestrationGraphRecord,
|
|
73
|
-
index: number,
|
|
74
|
-
selected: boolean,
|
|
75
|
-
width: number,
|
|
76
|
-
): Line {
|
|
77
|
-
const bg = selected ? C.selectBg : undefined;
|
|
78
|
-
return buildPanelLine(width, [
|
|
79
|
-
[' ', C.label, bg],
|
|
80
|
-
[graph.status.padEnd(10), statusColor(graph.status), bg],
|
|
81
|
-
[` ${graph.mode.padEnd(17)}`, C.value, bg],
|
|
82
|
-
[` ${graph.id.slice(0, 8)} `, C.dim, bg],
|
|
83
|
-
[graph.title.slice(0, Math.max(0, width - 39)), C.value, bg],
|
|
84
|
-
]);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
protected override getPalette(): PanelPalette {
|
|
88
|
-
return C;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
protected override getEmptyStateMessage(): string {
|
|
92
|
-
return ' No orchestration graphs recorded yet.';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
// Input — base class handles all navigation (up/down/j/k/pageup/pagedown/g/G)
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
// Render — multi-section layout (posture + scrollable graphs + detail + nodes)
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
|
-
|
|
103
|
-
public render(width: number, height: number): Line[] {
|
|
104
|
-
const intro = 'Read-only task graph posture, node contracts, and recursion guard state. Agent does not start local worker chains.';
|
|
105
|
-
|
|
106
|
-
if (!this.readModel) {
|
|
107
|
-
this.needsRender = false;
|
|
108
|
-
const workspace = buildPanelWorkspace(width, height, {
|
|
109
|
-
title: 'Orchestration Control Room',
|
|
110
|
-
intro,
|
|
111
|
-
sections: [{
|
|
112
|
-
lines: buildEmptyState(
|
|
113
|
-
width,
|
|
114
|
-
' Runtime store not wired into this panel yet.',
|
|
115
|
-
'The orchestration workspace needs the live runtime store before it can show graphs, nodes, and recursion guard events.',
|
|
116
|
-
[{ command: '/orchestration', summary: 'reopen from the shell-owned runtime once orchestration is active' }],
|
|
117
|
-
C,
|
|
118
|
-
),
|
|
119
|
-
}],
|
|
120
|
-
palette: C,
|
|
121
|
-
});
|
|
122
|
-
while (workspace.length < height) workspace.push(createEmptyLine(width));
|
|
123
|
-
return workspace;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const snapshot = this.readModel.getSnapshot();
|
|
127
|
-
const graphs = this.getItems();
|
|
128
|
-
const postureLines = [
|
|
129
|
-
buildKeyValueLine(width, [
|
|
130
|
-
{ label: 'graphs', value: String(snapshot.totalGraphs), valueColor: snapshot.totalGraphs > 0 ? C.value : C.dim },
|
|
131
|
-
{ label: 'active', value: String(snapshot.activeGraphIds.length), valueColor: snapshot.activeGraphIds.length > 0 ? C.running : C.dim },
|
|
132
|
-
{ label: 'completed', value: String(snapshot.totalCompletedGraphs), valueColor: snapshot.totalCompletedGraphs > 0 ? C.completed : C.dim },
|
|
133
|
-
{ label: 'failed', value: String(snapshot.totalFailedGraphs), valueColor: snapshot.totalFailedGraphs > 0 ? C.failed : C.dim },
|
|
134
|
-
{ label: 'guards', value: String(snapshot.recursionGuardTrips), valueColor: snapshot.recursionGuardTrips > 0 ? C.blocked : C.dim },
|
|
135
|
-
], C),
|
|
136
|
-
buildGuidanceLine(width, '/orchestration', 'inspect graph health without spawning local workers', C),
|
|
137
|
-
];
|
|
138
|
-
if (graphs.length === 0) {
|
|
139
|
-
this.needsRender = false;
|
|
140
|
-
const workspace = buildPanelWorkspace(width, height, {
|
|
141
|
-
title: 'Orchestration Control Room',
|
|
142
|
-
intro,
|
|
143
|
-
sections: [{
|
|
144
|
-
title: 'Orchestration posture',
|
|
145
|
-
lines: [
|
|
146
|
-
...postureLines,
|
|
147
|
-
...buildEmptyState(
|
|
148
|
-
width,
|
|
149
|
-
this.getEmptyStateMessage(),
|
|
150
|
-
'Graphs, nodes, child contracts, and recursion guard trips appear here only if a runtime record already exists.',
|
|
151
|
-
[
|
|
152
|
-
{ command: '/tasks', summary: 'create or inspect task flows that feed orchestration graphs' },
|
|
153
|
-
{ command: '/communication', summary: 'review structured agent communication alongside graph execution' },
|
|
154
|
-
],
|
|
155
|
-
C,
|
|
156
|
-
),
|
|
157
|
-
],
|
|
158
|
-
}],
|
|
159
|
-
palette: C,
|
|
160
|
-
});
|
|
161
|
-
while (workspace.length < height) workspace.push(createEmptyLine(width));
|
|
162
|
-
return workspace;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
this.clampSelection();
|
|
166
|
-
const selected = graphs[this.selectedIndex]!;
|
|
167
|
-
const detailLines: Line[] = [
|
|
168
|
-
buildPanelLine(width, [
|
|
169
|
-
[' Title: ', C.label],
|
|
170
|
-
[selected.title, C.value],
|
|
171
|
-
[' Status: ', C.label],
|
|
172
|
-
[selected.status, statusColor(selected.status)],
|
|
173
|
-
[' Mode: ', C.label],
|
|
174
|
-
[selected.mode, C.value],
|
|
175
|
-
]),
|
|
176
|
-
buildPanelLine(width, [
|
|
177
|
-
[' Nodes: ', C.label],
|
|
178
|
-
[String(selected.nodeOrder.length), C.value],
|
|
179
|
-
[' Started: ', C.label],
|
|
180
|
-
[selected.startedAt ? new Date(selected.startedAt).toLocaleTimeString() : 'n/a', C.dim],
|
|
181
|
-
[' Ended: ', C.label],
|
|
182
|
-
[selected.endedAt ? new Date(selected.endedAt).toLocaleTimeString() : 'n/a', C.dim],
|
|
183
|
-
]),
|
|
184
|
-
];
|
|
185
|
-
if (selected.lastRecursionGuard) {
|
|
186
|
-
detailLines.push(buildPanelLine(width, [
|
|
187
|
-
[' Recursion guard: ', C.label],
|
|
188
|
-
[`depth ${selected.lastRecursionGuard.depth} active ${selected.lastRecursionGuard.activeAgents} ${selected.lastRecursionGuard.reason}`, C.blocked],
|
|
189
|
-
]));
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const nodes = selected.nodeOrder
|
|
193
|
-
.map((nodeId) => selected.nodes.get(nodeId))
|
|
194
|
-
.filter((node): node is NonNullable<typeof node> => Boolean(node));
|
|
195
|
-
const focusNode = nodes[0];
|
|
196
|
-
if (focusNode?.contract) {
|
|
197
|
-
const toolCount = focusNode.contract.allowedTools?.length ?? 0;
|
|
198
|
-
const evidenceCount = focusNode.contract.requiredEvidence?.length ?? 0;
|
|
199
|
-
const scopeCount = focusNode.contract.writeScope?.length ?? 0;
|
|
200
|
-
detailLines.push(buildPanelLine(width, [
|
|
201
|
-
[' Contract: ', C.label],
|
|
202
|
-
[`tools ${toolCount}`, C.value],
|
|
203
|
-
[' evidence ', C.label],
|
|
204
|
-
[String(evidenceCount), C.value],
|
|
205
|
-
[' write scope ', C.label],
|
|
206
|
-
[String(scopeCount), C.value],
|
|
207
|
-
]));
|
|
208
|
-
detailLines.push(buildPanelLine(width, [
|
|
209
|
-
[' Flow: ', C.label],
|
|
210
|
-
[focusNode.contract.executionProtocol ?? 'direct', C.value],
|
|
211
|
-
[' Review: ', C.label],
|
|
212
|
-
[focusNode.contract.reviewMode ?? 'none', C.value],
|
|
213
|
-
[' Lane: ', C.label],
|
|
214
|
-
[focusNode.contract.communicationLane ?? 'default', C.value],
|
|
215
|
-
[' Inherits: ', C.label],
|
|
216
|
-
[focusNode.contract.inheritsParentConstraints ? 'yes' : 'no', C.value],
|
|
217
|
-
]));
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const nodeLines: Line[] = nodes.length === 0
|
|
221
|
-
? [buildPanelLine(width, [[' No nodes recorded yet.', C.dim]])]
|
|
222
|
-
: nodes.map((node) => {
|
|
223
|
-
const depends = node.dependencyNodeIds.length > 0 ? ` deps:${node.dependencyNodeIds.length}` : '';
|
|
224
|
-
return buildPanelLine(width, [
|
|
225
|
-
[' ', C.label],
|
|
226
|
-
[node.status.padEnd(10), statusColor(node.status)],
|
|
227
|
-
[` ${node.role.padEnd(10)}`, C.value],
|
|
228
|
-
[` ${node.id.slice(0, 8)} `, C.dim],
|
|
229
|
-
[`${node.title}${depends}`.slice(0, Math.max(0, width - 34)), C.value],
|
|
230
|
-
]);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
const scrollableLines: Line[] = graphs.map((graph, index) =>
|
|
234
|
-
this.renderItem(graph, index, index === this.selectedIndex, width),
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
const postureSection: PanelWorkspaceSection = { title: 'Orchestration posture', lines: postureLines };
|
|
238
|
-
const selectedGraphSection: PanelWorkspaceSection = { title: 'Selected Graph', lines: detailLines };
|
|
239
|
-
const nodesSection: PanelWorkspaceSection = { title: 'Nodes', lines: nodeLines };
|
|
240
|
-
const graphsSection = resolveScrollablePanelSection(width, height, {
|
|
241
|
-
intro,
|
|
242
|
-
palette: C,
|
|
243
|
-
beforeSections: [postureSection],
|
|
244
|
-
section: {
|
|
245
|
-
title: 'Graphs',
|
|
246
|
-
scrollableLines,
|
|
247
|
-
selectedIndex: this.selectedIndex,
|
|
248
|
-
scrollOffset: this.scrollStart,
|
|
249
|
-
minRows: 4,
|
|
250
|
-
appendWindowSummary: { dimColor: C.dim },
|
|
251
|
-
},
|
|
252
|
-
afterSections: [selectedGraphSection, nodesSection],
|
|
253
|
-
});
|
|
254
|
-
this.scrollStart = graphsSection.scrollOffset;
|
|
255
|
-
|
|
256
|
-
const sections: PanelWorkspaceSection[] = [
|
|
257
|
-
postureSection,
|
|
258
|
-
graphsSection.section,
|
|
259
|
-
selectedGraphSection,
|
|
260
|
-
nodesSection,
|
|
261
|
-
];
|
|
262
|
-
this.needsRender = false;
|
|
263
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
264
|
-
title: 'Orchestration Control Room',
|
|
265
|
-
intro,
|
|
266
|
-
sections,
|
|
267
|
-
palette: C,
|
|
268
|
-
});
|
|
269
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
270
|
-
return lines.slice(0, height);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { createEmptyLine } from '../types/grid.ts';
|
|
3
|
-
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
4
|
-
import type { PluginManagerObserver, PluginStatus } from '@pellux/goodvibes-sdk/platform/plugins';
|
|
5
|
-
import {
|
|
6
|
-
buildEmptyState,
|
|
7
|
-
buildPanelLine,
|
|
8
|
-
buildPanelWorkspace,
|
|
9
|
-
DEFAULT_PANEL_PALETTE,
|
|
10
|
-
type PanelPalette,
|
|
11
|
-
} from './polish.ts';
|
|
12
|
-
|
|
13
|
-
const C = {
|
|
14
|
-
...DEFAULT_PANEL_PALETTE,
|
|
15
|
-
header: '#94a3b8',
|
|
16
|
-
headerBg: '#1e293b',
|
|
17
|
-
ok: '#22c55e',
|
|
18
|
-
warn: '#eab308',
|
|
19
|
-
error: '#ef4444',
|
|
20
|
-
info: '#38bdf8',
|
|
21
|
-
selectBg: '#0f172a',
|
|
22
|
-
} as const;
|
|
23
|
-
|
|
24
|
-
function trustColor(tier: PluginStatus['trustTier']): string {
|
|
25
|
-
switch (tier) {
|
|
26
|
-
case 'trusted':
|
|
27
|
-
return C.ok;
|
|
28
|
-
case 'limited':
|
|
29
|
-
return C.warn;
|
|
30
|
-
case 'untrusted':
|
|
31
|
-
return C.error;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function statusColor(status: PluginStatus): string {
|
|
36
|
-
if (status.quarantined) return C.error;
|
|
37
|
-
if (status.active) return C.ok;
|
|
38
|
-
if (status.enabled) return C.warn;
|
|
39
|
-
return C.dim;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function statusLabel(status: PluginStatus): string {
|
|
43
|
-
if (status.quarantined) return 'QUARANTINED';
|
|
44
|
-
if (status.active) return 'ACTIVE';
|
|
45
|
-
if (status.enabled) return 'ENABLED';
|
|
46
|
-
return 'DISABLED';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export class PluginsPanel extends ScrollableListPanel<PluginStatus> {
|
|
50
|
-
private readonly manager: PluginManagerObserver;
|
|
51
|
-
private readonly unsub: (() => void) | null;
|
|
52
|
-
|
|
53
|
-
public constructor(manager: PluginManagerObserver) {
|
|
54
|
-
super('plugins', 'Plugins', 'P', 'monitoring');
|
|
55
|
-
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
56
|
-
this.manager = manager;
|
|
57
|
-
this.unsub = manager.subscribe(() => this.markDirty());
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public override onActivate(): void {
|
|
61
|
-
super.onActivate();
|
|
62
|
-
this.selectedIndex = 0;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public override onDestroy(): void {
|
|
66
|
-
this.unsub?.();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
protected override getPalette(): PanelPalette {
|
|
70
|
-
return C;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
protected getItems(): readonly PluginStatus[] {
|
|
74
|
-
return this.manager.list();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
protected renderItem(plugin: PluginStatus, _index: number, selected: boolean, width: number): Line {
|
|
78
|
-
const bg = selected ? C.selectBg : undefined;
|
|
79
|
-
return buildPanelLine(width, [
|
|
80
|
-
[' ', C.label, bg],
|
|
81
|
-
[plugin.name.padEnd(22), C.value, bg],
|
|
82
|
-
[` ${statusLabel(plugin).padEnd(11)}`, statusColor(plugin), bg],
|
|
83
|
-
[` ${plugin.trustTier.toUpperCase().padEnd(10)}`, trustColor(plugin.trustTier), bg],
|
|
84
|
-
[` ${plugin.version}`, C.dim, bg],
|
|
85
|
-
]);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
protected override getEmptyStateMessage(): string {
|
|
89
|
-
return ' No plugins discovered.';
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
|
|
93
|
-
return [
|
|
94
|
-
{ command: '/plugin list', summary: 'inspect plugin discovery paths and current registry state' },
|
|
95
|
-
{ command: '/marketplace', summary: 'review curated ecosystem entries and provenance posture' },
|
|
96
|
-
];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public render(width: number, height: number): Line[] {
|
|
100
|
-
const intro = 'Plugin trust, capabilities, signatures, and quarantine posture for the active ecosystem surface.';
|
|
101
|
-
const plugins = this.getItems();
|
|
102
|
-
|
|
103
|
-
if (plugins.length === 0) {
|
|
104
|
-
const workspace = buildPanelWorkspace(width, height, {
|
|
105
|
-
title: 'Plugin Control Room',
|
|
106
|
-
intro,
|
|
107
|
-
sections: [{
|
|
108
|
-
lines: buildEmptyState(
|
|
109
|
-
width,
|
|
110
|
-
' No plugins discovered.',
|
|
111
|
-
'Use /plugin list for search paths, install hints, and trust review before activating plugin-backed flows.',
|
|
112
|
-
[
|
|
113
|
-
{ command: '/plugin list', summary: 'inspect plugin discovery paths and current registry state' },
|
|
114
|
-
{ command: '/marketplace', summary: 'review curated ecosystem entries and provenance posture' },
|
|
115
|
-
],
|
|
116
|
-
C,
|
|
117
|
-
),
|
|
118
|
-
}],
|
|
119
|
-
palette: C,
|
|
120
|
-
});
|
|
121
|
-
while (workspace.length < height) workspace.push(createEmptyLine(width));
|
|
122
|
-
return workspace;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
this.clampSelection();
|
|
126
|
-
const selected = plugins[this.selectedIndex]!;
|
|
127
|
-
const selectedCaps = this.manager.capabilities(selected.name);
|
|
128
|
-
const trustRecord = this.manager.getTrustRecord(selected.name);
|
|
129
|
-
const quarantineRecord = this.manager.getQuarantineRecord(selected.name);
|
|
130
|
-
const detailLines: Line[] = [
|
|
131
|
-
buildPanelLine(width, [
|
|
132
|
-
[' Plugin: ', C.label],
|
|
133
|
-
[selected.name, C.value],
|
|
134
|
-
[' State: ', C.label],
|
|
135
|
-
[statusLabel(selected), statusColor(selected)],
|
|
136
|
-
[' Trust: ', C.label],
|
|
137
|
-
[selected.trustTier, trustColor(selected.trustTier)],
|
|
138
|
-
]),
|
|
139
|
-
buildPanelLine(width, [
|
|
140
|
-
[' Description: ', C.label],
|
|
141
|
-
[selected.description.slice(0, Math.max(0, width - 15)), C.dim],
|
|
142
|
-
]),
|
|
143
|
-
];
|
|
144
|
-
|
|
145
|
-
if (selectedCaps) {
|
|
146
|
-
detailLines.push(buildPanelLine(width, [
|
|
147
|
-
[' Capabilities: ', C.label],
|
|
148
|
-
[String(selectedCaps.requested.length), C.value],
|
|
149
|
-
[' High-risk: ', C.label],
|
|
150
|
-
[String(selectedCaps.highRisk.length), selectedCaps.highRisk.length > 0 ? C.warn : C.ok],
|
|
151
|
-
[' Blocked: ', C.label],
|
|
152
|
-
[String(selectedCaps.blocked.length), selectedCaps.blocked.length > 0 ? C.error : C.ok],
|
|
153
|
-
]));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (trustRecord?.signatureFingerprint) {
|
|
157
|
-
detailLines.push(buildPanelLine(width, [
|
|
158
|
-
[' Signature: ', C.label],
|
|
159
|
-
[trustRecord.signatureFingerprint, C.info],
|
|
160
|
-
]));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (quarantineRecord) {
|
|
164
|
-
detailLines.push(buildPanelLine(width, [
|
|
165
|
-
[' Quarantine: ', C.label],
|
|
166
|
-
[quarantineRecord.reason.slice(0, Math.max(0, width - 14)), C.error],
|
|
167
|
-
]));
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
detailLines.push(buildPanelLine(width, [[' Inspect trust and capability state here, then use /plugin to take action.', C.dim]]));
|
|
171
|
-
detailLines.push(buildPanelLine(width, [[' Up/Down move through discovered plugins', C.dim]]));
|
|
172
|
-
|
|
173
|
-
return this.renderList(width, height, {
|
|
174
|
-
title: 'Plugin Control Room',
|
|
175
|
-
footer: detailLines,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|