@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,183 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { createEmptyLine } from '../types/grid.ts';
|
|
3
|
-
import { BasePanel } from './base-panel.ts';
|
|
4
|
-
import type { UiCockpitSnapshot, UiReadModel } from '../runtime/ui-read-models.ts';
|
|
5
|
-
import {
|
|
6
|
-
buildGuidanceLine,
|
|
7
|
-
buildKeyValueLine,
|
|
8
|
-
buildPanelLine,
|
|
9
|
-
buildPanelWorkspace,
|
|
10
|
-
buildStatPill,
|
|
11
|
-
DEFAULT_PANEL_PALETTE,
|
|
12
|
-
type PanelWorkspaceSection,
|
|
13
|
-
} from './polish.ts';
|
|
14
|
-
|
|
15
|
-
const C = {
|
|
16
|
-
...DEFAULT_PANEL_PALETTE,
|
|
17
|
-
header: '#cbd5e1',
|
|
18
|
-
headerBg: '#0f172a',
|
|
19
|
-
} as const;
|
|
20
|
-
|
|
21
|
-
function pickColor(value: number, warnAt = 1, badAt = 3): string {
|
|
22
|
-
if (value >= badAt) return C.bad;
|
|
23
|
-
if (value >= warnAt) return C.warn;
|
|
24
|
-
return C.good;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const WORKSPACE_IDS = ['flow', 'governance', 'health', 'domains'] as const;
|
|
28
|
-
|
|
29
|
-
export class CockpitPanel extends BasePanel {
|
|
30
|
-
private readonly unsub: (() => void) | null;
|
|
31
|
-
private selectedWorkspaceIndex = 0;
|
|
32
|
-
|
|
33
|
-
public constructor(private readonly readModel?: UiReadModel<UiCockpitSnapshot>) {
|
|
34
|
-
super('cockpit', 'Cockpit', 'O', 'monitoring');
|
|
35
|
-
this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public override onDestroy(): void {
|
|
39
|
-
this.unsub?.();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public handleInput(key: string): boolean {
|
|
43
|
-
if (key === 'left' || key === 'h') {
|
|
44
|
-
this.selectedWorkspaceIndex = Math.max(0, this.selectedWorkspaceIndex - 1);
|
|
45
|
-
this.markDirty();
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
if (key === 'right' || key === 'l') {
|
|
49
|
-
this.selectedWorkspaceIndex = Math.min(WORKSPACE_IDS.length - 1, this.selectedWorkspaceIndex + 1);
|
|
50
|
-
this.markDirty();
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
if (key === 'home') {
|
|
54
|
-
this.selectedWorkspaceIndex = 0;
|
|
55
|
-
this.markDirty();
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
if (key === 'end') {
|
|
59
|
-
this.selectedWorkspaceIndex = WORKSPACE_IDS.length - 1;
|
|
60
|
-
this.markDirty();
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public render(width: number, height: number): Line[] {
|
|
67
|
-
this.needsRender = false;
|
|
68
|
-
|
|
69
|
-
if (!this.readModel) {
|
|
70
|
-
const lines: Line[] = [buildPanelLine(width, [[' Runtime read model not wired into this panel yet.', C.empty]])];
|
|
71
|
-
const workspace = buildPanelWorkspace(width, height, {
|
|
72
|
-
title: 'Operator Cockpit',
|
|
73
|
-
sections: [{ lines }],
|
|
74
|
-
palette: C,
|
|
75
|
-
});
|
|
76
|
-
while (workspace.length < height) workspace.push(createEmptyLine(width));
|
|
77
|
-
return workspace;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const snapshot = this.readModel.getSnapshot();
|
|
81
|
-
const selectedWorkspace = WORKSPACE_IDS[this.selectedWorkspaceIndex] ?? 'flow';
|
|
82
|
-
|
|
83
|
-
const flowLines: Line[] = [
|
|
84
|
-
buildPanelLine(width, [
|
|
85
|
-
...buildStatPill('graphs', String(snapshot.activeGraphs), C.label, pickColor(snapshot.activeGraphs, 1, 4)),
|
|
86
|
-
...buildStatPill('running', String(snapshot.runningTasks), C.label, C.value),
|
|
87
|
-
...buildStatPill('blocked', String(snapshot.blockedTasks), C.label, pickColor(snapshot.blockedTasks)),
|
|
88
|
-
...buildStatPill('failed', String(snapshot.failedTasks), C.label, pickColor(snapshot.failedTasks)),
|
|
89
|
-
...buildStatPill('guards', String(snapshot.guardTrips), C.label, pickColor(snapshot.guardTrips)),
|
|
90
|
-
]),
|
|
91
|
-
buildPanelLine(width, [
|
|
92
|
-
...buildStatPill('blocked comms', String(snapshot.blockedMessages), C.label, pickColor(snapshot.blockedMessages)),
|
|
93
|
-
...buildStatPill('pending approvals', String(snapshot.pendingPermissions), C.label, pickColor(snapshot.pendingPermissions)),
|
|
94
|
-
...buildStatPill('denied', String(snapshot.deniedPermissions), C.label, pickColor(snapshot.deniedPermissions)),
|
|
95
|
-
]),
|
|
96
|
-
];
|
|
97
|
-
const governanceLines: Line[] = [
|
|
98
|
-
buildPanelLine(width, [
|
|
99
|
-
...buildStatPill('preflight', snapshot.preflightStatus.toUpperCase(), C.label, snapshot.preflightStatus === 'block' ? C.bad : snapshot.preflightStatus === 'warn' ? C.warn : snapshot.preflightStatus === 'pass' ? C.good : C.dim),
|
|
100
|
-
...buildStatPill('issues', String(snapshot.preflightIssueCount), C.label, pickColor(snapshot.preflightIssueCount)),
|
|
101
|
-
...buildStatPill('lint', String(snapshot.lintFindingCount), C.label, pickColor(snapshot.lintFindingCount)),
|
|
102
|
-
...buildStatPill('allow-all MCP', String(snapshot.elevatedMcp), C.label, pickColor(snapshot.elevatedMcp)),
|
|
103
|
-
...buildStatPill('unhealthy MCP', String(snapshot.unhealthyMcp), C.label, pickColor(snapshot.unhealthyMcp)),
|
|
104
|
-
]),
|
|
105
|
-
buildPanelLine(width, [
|
|
106
|
-
...buildStatPill('token blocked', String(snapshot.tokenBlockedCount), C.label, pickColor(snapshot.tokenBlockedCount)),
|
|
107
|
-
...buildStatPill('overdue', String(snapshot.tokenRotationOverdueCount), C.label, pickColor(snapshot.tokenRotationOverdueCount)),
|
|
108
|
-
...buildStatPill('scope violations', String(snapshot.tokenScopeViolationCount), C.label, pickColor(snapshot.tokenScopeViolationCount)),
|
|
109
|
-
...buildStatPill('warnings', String(snapshot.tokenRotationWarningCount), C.label, pickColor(snapshot.tokenRotationWarningCount)),
|
|
110
|
-
]),
|
|
111
|
-
];
|
|
112
|
-
const healthLines: Line[] = [
|
|
113
|
-
buildPanelLine(width, [
|
|
114
|
-
...buildStatPill('incidents', String(snapshot.incidentCount), C.label, pickColor(snapshot.incidentCount)),
|
|
115
|
-
...buildStatPill('plugins', String(snapshot.erroredPlugins), C.label, pickColor(snapshot.erroredPlugins)),
|
|
116
|
-
...buildStatPill('integrations', String(snapshot.failingIntegrations), C.label, pickColor(snapshot.failingIntegrations)),
|
|
117
|
-
]),
|
|
118
|
-
];
|
|
119
|
-
if (snapshot.latestIncident) {
|
|
120
|
-
healthLines.push(buildPanelLine(width, [
|
|
121
|
-
[' latest incident ', C.label],
|
|
122
|
-
[snapshot.latestIncident.classification, C.bad],
|
|
123
|
-
[' ', C.label],
|
|
124
|
-
[snapshot.latestIncident.summary.slice(0, Math.max(0, width - 19 - snapshot.latestIncident.classification.length)), C.dim],
|
|
125
|
-
]));
|
|
126
|
-
}
|
|
127
|
-
const domainLines: Line[] = [buildPanelLine(width, [[
|
|
128
|
-
`tasks:${snapshot.taskCount} agents:${snapshot.agentCount} graphs:${snapshot.totalGraphs} comms:${snapshot.communicationCount} mcp:${snapshot.mcpServerCount} plugins:${snapshot.pluginCount}`,
|
|
129
|
-
C.dim,
|
|
130
|
-
]])];
|
|
131
|
-
const workspaceLines: Line[] = [];
|
|
132
|
-
if (selectedWorkspace === 'flow') {
|
|
133
|
-
workspaceLines.push(buildKeyValueLine(width, [
|
|
134
|
-
{ label: 'running', value: String(snapshot.runningTasks), valueColor: C.value },
|
|
135
|
-
{ label: 'blocked', value: String(snapshot.blockedTasks), valueColor: pickColor(snapshot.blockedTasks) },
|
|
136
|
-
{ label: 'graphs', value: String(snapshot.activeGraphs), valueColor: pickColor(snapshot.activeGraphs, 1, 4) },
|
|
137
|
-
], C));
|
|
138
|
-
workspaceLines.push(buildGuidanceLine(width, '/orchestration', 'inspect graph state, retries, and subtree controls', C));
|
|
139
|
-
workspaceLines.push(buildGuidanceLine(width, '/tasks', 'review active task pressure and task-specific output', C));
|
|
140
|
-
} else if (selectedWorkspace === 'governance') {
|
|
141
|
-
workspaceLines.push(buildKeyValueLine(width, [
|
|
142
|
-
{ label: 'preflight', value: snapshot.preflightStatus.toUpperCase(), valueColor: snapshot.preflightStatus === 'block' ? C.bad : snapshot.preflightStatus === 'warn' ? C.warn : snapshot.preflightStatus === 'pass' ? C.good : C.dim },
|
|
143
|
-
{ label: 'lint', value: String(snapshot.lintFindingCount), valueColor: pickColor(snapshot.lintFindingCount) },
|
|
144
|
-
{ label: 'allow-all mcp', value: String(snapshot.elevatedMcp), valueColor: pickColor(snapshot.elevatedMcp) },
|
|
145
|
-
], C));
|
|
146
|
-
workspaceLines.push(buildGuidanceLine(width, '/policy', 'run simulation, preflight, and bundle review', C));
|
|
147
|
-
workspaceLines.push(buildGuidanceLine(width, '/security', 'inspect trust, tokens, quarantines, and incident pressure', C));
|
|
148
|
-
} else if (selectedWorkspace === 'health') {
|
|
149
|
-
workspaceLines.push(buildKeyValueLine(width, [
|
|
150
|
-
{ label: 'incidents', value: String(snapshot.incidentCount), valueColor: pickColor(snapshot.incidentCount) },
|
|
151
|
-
{ label: 'plugins', value: String(snapshot.erroredPlugins), valueColor: pickColor(snapshot.erroredPlugins) },
|
|
152
|
-
{ label: 'integrations', value: String(snapshot.failingIntegrations), valueColor: pickColor(snapshot.failingIntegrations) },
|
|
153
|
-
], C));
|
|
154
|
-
workspaceLines.push(buildGuidanceLine(width, '/incident latest', 'inspect the latest incident bundle and replay fallout', C));
|
|
155
|
-
workspaceLines.push(buildGuidanceLine(width, '/plugins', 'review errored plugins and provenance posture', C));
|
|
156
|
-
} else {
|
|
157
|
-
workspaceLines.push(buildKeyValueLine(width, [
|
|
158
|
-
{ label: 'tasks', value: String(snapshot.taskCount), valueColor: C.value },
|
|
159
|
-
{ label: 'comms', value: String(snapshot.communicationCount), valueColor: C.value },
|
|
160
|
-
{ label: 'mcp', value: String(snapshot.mcpServerCount), valueColor: C.value },
|
|
161
|
-
], C));
|
|
162
|
-
workspaceLines.push(buildGuidanceLine(width, '/mcp', 'inspect trust, quarantine, and risky server posture', C));
|
|
163
|
-
workspaceLines.push(buildGuidanceLine(width, '/communication', 'review blocked lanes and agent message flow', C));
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const sections: PanelWorkspaceSection[] = [
|
|
167
|
-
{ title: 'Flow', lines: flowLines },
|
|
168
|
-
{ title: 'Governance', lines: governanceLines },
|
|
169
|
-
{ title: 'Health', lines: healthLines },
|
|
170
|
-
{ title: 'Domains', lines: domainLines },
|
|
171
|
-
{ title: 'Selected Workspace', lines: workspaceLines },
|
|
172
|
-
];
|
|
173
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
174
|
-
title: 'Operator Cockpit',
|
|
175
|
-
intro: 'Live runtime pressure across orchestration, approvals, governance, integrations, and provider trust posture.',
|
|
176
|
-
sections,
|
|
177
|
-
footerLines: [buildPanelLine(width, [[` Left/Right move workspace focus Home/End jump focus=${selectedWorkspace}`, C.dim]])],
|
|
178
|
-
palette: C,
|
|
179
|
-
});
|
|
180
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
181
|
-
return lines.slice(0, height);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
3
|
-
import type { UiCommunicationSnapshot, UiReadModel } from '../runtime/ui-read-models.ts';
|
|
4
|
-
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
5
|
-
import {
|
|
6
|
-
buildBodyText,
|
|
7
|
-
buildEmptyState,
|
|
8
|
-
buildGuidanceLine,
|
|
9
|
-
buildKeyValueLine,
|
|
10
|
-
buildPanelLine,
|
|
11
|
-
buildPanelWorkspace,
|
|
12
|
-
DEFAULT_PANEL_PALETTE,
|
|
13
|
-
type PanelPalette,
|
|
14
|
-
} from './polish.ts';
|
|
15
|
-
import { createEmptyLine } from '../types/grid.ts';
|
|
16
|
-
|
|
17
|
-
const C = {
|
|
18
|
-
...DEFAULT_PANEL_PALETTE,
|
|
19
|
-
header: '#94a3b8',
|
|
20
|
-
headerBg: '#1e293b',
|
|
21
|
-
ok: '#22c55e',
|
|
22
|
-
warn: '#eab308',
|
|
23
|
-
error: '#ef4444',
|
|
24
|
-
selectBg: '#0f172a',
|
|
25
|
-
} as const;
|
|
26
|
-
|
|
27
|
-
type CommunicationRecord = UiCommunicationSnapshot['records'][number];
|
|
28
|
-
|
|
29
|
-
export class CommunicationPanel extends ScrollableListPanel<CommunicationRecord> {
|
|
30
|
-
private readonly readModel?: UiReadModel<UiCommunicationSnapshot>;
|
|
31
|
-
private readonly unsub: (() => void) | null;
|
|
32
|
-
|
|
33
|
-
public constructor(readModel?: UiReadModel<UiCommunicationSnapshot>) {
|
|
34
|
-
super('communication', 'Communication', 'Y', 'monitoring');
|
|
35
|
-
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
36
|
-
this.readModel = readModel;
|
|
37
|
-
this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public override onDestroy(): void {
|
|
41
|
-
this.unsub?.();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
protected override getPalette(): PanelPalette {
|
|
45
|
-
return C;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
protected getItems(): readonly CommunicationRecord[] {
|
|
49
|
-
if (!this.readModel) return [];
|
|
50
|
-
return this.readModel.getSnapshot().records;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
protected renderItem(record: CommunicationRecord, index: number, selected: boolean, width: number): Line {
|
|
54
|
-
const bg = selected ? C.selectBg : undefined;
|
|
55
|
-
const color = record.status === 'blocked' ? C.error : record.status === 'delivered' ? C.ok : C.info;
|
|
56
|
-
return buildPanelLine(width, [
|
|
57
|
-
[' ', C.label, bg],
|
|
58
|
-
[record.status.padEnd(10), color, bg],
|
|
59
|
-
[` ${record.kind.padEnd(10)}`, C.info, bg],
|
|
60
|
-
[` ${truncateDisplay(`${record.fromId} -> ${record.toId}`, 28).padEnd(28)}`, C.value, bg],
|
|
61
|
-
[` ${truncateDisplay(record.content, Math.max(0, width - 53))}`, C.dim, bg],
|
|
62
|
-
]);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
protected override getEmptyStateMessage(): string {
|
|
66
|
-
return ' No structured communication recorded yet.';
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
|
|
70
|
-
return [
|
|
71
|
-
{ command: '/orchestration', summary: 'review graphs and recursive agent activity' },
|
|
72
|
-
{ command: '/communication', summary: 'reopen this workspace once the runtime emits message traffic' },
|
|
73
|
-
];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public render(width: number, height: number): Line[] {
|
|
77
|
-
const intro = 'Structured agent communication, routing policy outcomes, and delivery status across orchestration trees.';
|
|
78
|
-
|
|
79
|
-
if (!this.readModel) {
|
|
80
|
-
const workspace = buildPanelWorkspace(width, height, {
|
|
81
|
-
title: 'Communication Control Room',
|
|
82
|
-
intro,
|
|
83
|
-
sections: [{
|
|
84
|
-
lines: buildEmptyState(
|
|
85
|
-
width,
|
|
86
|
-
' Runtime store not wired into this panel yet.',
|
|
87
|
-
'This workspace needs the live runtime store before it can show communication history and policy outcomes.',
|
|
88
|
-
[{ command: '/communication', summary: 'reopen the workspace from the shell-owned runtime' }],
|
|
89
|
-
C,
|
|
90
|
-
),
|
|
91
|
-
}],
|
|
92
|
-
palette: C,
|
|
93
|
-
});
|
|
94
|
-
while (workspace.length < height) workspace.push(createEmptyLine(width));
|
|
95
|
-
return workspace;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const snapshot = this.readModel.getSnapshot();
|
|
99
|
-
const records = this.getItems();
|
|
100
|
-
|
|
101
|
-
const postureLines: Line[] = [
|
|
102
|
-
buildPanelLine(width, [[' Communication posture', C.label]]),
|
|
103
|
-
buildKeyValueLine(width, [
|
|
104
|
-
{ label: 'sent', value: String(snapshot.totalSent), valueColor: snapshot.totalSent > 0 ? C.info : C.dim },
|
|
105
|
-
{ label: 'delivered', value: String(snapshot.totalDelivered), valueColor: snapshot.totalDelivered > 0 ? C.ok : C.dim },
|
|
106
|
-
{ label: 'blocked', value: String(snapshot.totalBlocked), valueColor: snapshot.totalBlocked > 0 ? C.error : C.dim },
|
|
107
|
-
], C),
|
|
108
|
-
buildGuidanceLine(width, '/orchestration', 'inspect recursive routing, message handoff, and blocked broadcast posture', C),
|
|
109
|
-
];
|
|
110
|
-
|
|
111
|
-
if (records.length === 0) {
|
|
112
|
-
return this.renderList(width, height, {
|
|
113
|
-
title: 'Communication Control Room',
|
|
114
|
-
header: postureLines,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
this.clampSelection();
|
|
119
|
-
const selected = records[this.selectedIndex];
|
|
120
|
-
|
|
121
|
-
// Update posture with selected info
|
|
122
|
-
const postureWithSelected: Line[] = [
|
|
123
|
-
buildPanelLine(width, [[' Communication posture', C.label]]),
|
|
124
|
-
buildKeyValueLine(width, [
|
|
125
|
-
{ label: 'sent', value: String(snapshot.totalSent), valueColor: snapshot.totalSent > 0 ? C.info : C.dim },
|
|
126
|
-
{ label: 'delivered', value: String(snapshot.totalDelivered), valueColor: snapshot.totalDelivered > 0 ? C.ok : C.dim },
|
|
127
|
-
{ label: 'blocked', value: String(snapshot.totalBlocked), valueColor: snapshot.totalBlocked > 0 ? C.error : C.dim },
|
|
128
|
-
{ label: 'selected', value: `${selected?.fromId ?? 'n/a'} -> ${selected?.toId ?? 'n/a'}`, valueColor: C.value },
|
|
129
|
-
], C),
|
|
130
|
-
buildGuidanceLine(width, '/orchestration', 'inspect recursive routing, message handoff, and blocked broadcast posture', C),
|
|
131
|
-
];
|
|
132
|
-
|
|
133
|
-
const footerLines: Line[] = [];
|
|
134
|
-
if (selected) {
|
|
135
|
-
footerLines.push(
|
|
136
|
-
buildPanelLine(width, [[' Route: ', C.label], [`${selected.scope} / ${selected.kind}`, C.value], [' Status: ', C.label], [selected.status, selected.status === 'blocked' ? C.error : C.ok]]),
|
|
137
|
-
buildPanelLine(width, [[' From: ', C.label], [selected.fromId, C.value], [' To: ', C.label], [selected.toId, C.value]]),
|
|
138
|
-
buildPanelLine(width, [[' Roles: ', C.label], [`${selected.fromRole ?? 'unknown'} -> ${selected.toRole ?? 'unknown'}`, C.dim]]),
|
|
139
|
-
);
|
|
140
|
-
if (selected.reason) {
|
|
141
|
-
footerLines.push(buildPanelLine(width, [[' Reason: ', C.label], [truncateDisplay(selected.reason, Math.max(0, width - 11)), C.warn]]));
|
|
142
|
-
}
|
|
143
|
-
footerLines.push(...buildBodyText(width, ` Content: ${selected.content}`, C));
|
|
144
|
-
}
|
|
145
|
-
footerLines.push(buildPanelLine(width, [[' Up/Down move through messages', C.dim]]));
|
|
146
|
-
|
|
147
|
-
return this.renderList(width, height, {
|
|
148
|
-
title: 'Communication Control Room',
|
|
149
|
-
header: postureWithSelected,
|
|
150
|
-
footer: footerLines,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
}
|
|
@@ -1,211 +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 { UiControlPlaneSnapshot, UiReadModel } from '../runtime/ui-read-models.ts';
|
|
5
|
-
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
6
|
-
import {
|
|
7
|
-
buildEmptyState,
|
|
8
|
-
buildGuidanceLine,
|
|
9
|
-
buildKeyValueLine,
|
|
10
|
-
buildPanelLine,
|
|
11
|
-
buildPanelWorkspace,
|
|
12
|
-
DEFAULT_PANEL_PALETTE,
|
|
13
|
-
type PanelPalette,
|
|
14
|
-
} from './polish.ts';
|
|
15
|
-
|
|
16
|
-
const C = {
|
|
17
|
-
...DEFAULT_PANEL_PALETTE,
|
|
18
|
-
header: '#94a3b8',
|
|
19
|
-
headerBg: '#1e293b',
|
|
20
|
-
ok: '#22c55e',
|
|
21
|
-
warn: '#eab308',
|
|
22
|
-
error: '#ef4444',
|
|
23
|
-
info: '#38bdf8',
|
|
24
|
-
selectBg: '#0f172a',
|
|
25
|
-
} as const;
|
|
26
|
-
|
|
27
|
-
function formatTime(value?: number): string {
|
|
28
|
-
if (!value) return 'n/a';
|
|
29
|
-
return new Date(value).toLocaleString();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function connectionColor(state: string): string {
|
|
33
|
-
if (state === 'connected' || state === 'healthy') return C.ok;
|
|
34
|
-
if (state === 'degraded' || state === 'connecting' || state === 'initializing') return C.warn;
|
|
35
|
-
if (state === 'terminal_failure') return C.error;
|
|
36
|
-
return C.dim;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
type ControlPlaneClient = UiControlPlaneSnapshot['clients'][number];
|
|
40
|
-
|
|
41
|
-
export class ControlPlanePanel extends ScrollableListPanel<ControlPlaneClient> {
|
|
42
|
-
private readonly unsub: (() => void) | null;
|
|
43
|
-
|
|
44
|
-
public constructor(private readonly readModel?: UiReadModel<UiControlPlaneSnapshot>) {
|
|
45
|
-
super('control-plane', 'Runtime Status', 'C', 'monitoring');
|
|
46
|
-
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
47
|
-
this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public override onDestroy(): void {
|
|
51
|
-
this.unsub?.();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
protected override getPalette(): PanelPalette {
|
|
55
|
-
return C;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
protected getItems(): readonly ControlPlaneClient[] {
|
|
59
|
-
if (!this.readModel) return [];
|
|
60
|
-
return this.readModel.getSnapshot().clients;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
protected renderItem(client: ControlPlaneClient, _index: number, selected: boolean, width: number): Line {
|
|
64
|
-
const bg = selected ? C.selectBg : undefined;
|
|
65
|
-
return buildPanelLine(width, [
|
|
66
|
-
[' ', C.label, bg],
|
|
67
|
-
[client.kind.padEnd(10), C.info, bg],
|
|
68
|
-
[` ${truncateDisplay(client.label, 20).padEnd(20)}`, C.value, bg],
|
|
69
|
-
[` ${client.transport.padEnd(12)}`, C.dim, bg],
|
|
70
|
-
[` ${truncateDisplay(formatTime(client.lastSeenAt), Math.max(0, width - 46))}`, C.dim, bg],
|
|
71
|
-
]);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
protected override getEmptyStateMessage(): string {
|
|
75
|
-
return ' No runtime activity recorded.';
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
|
|
79
|
-
return [
|
|
80
|
-
{ command: '/cockpit', summary: 'watch operator posture from the terminal' },
|
|
81
|
-
{ command: '/schedule list', summary: 'inspect externally managed automation without starting jobs here' },
|
|
82
|
-
];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
public render(width: number, height: number): Line[] {
|
|
86
|
-
const intro = 'Shared runtime state, live clients, approval pressure, and recent channel session posture.';
|
|
87
|
-
|
|
88
|
-
if (!this.readModel) {
|
|
89
|
-
const workspace = buildPanelWorkspace(width, height, {
|
|
90
|
-
title: 'Runtime Status',
|
|
91
|
-
intro,
|
|
92
|
-
sections: [{
|
|
93
|
-
lines: buildEmptyState(
|
|
94
|
-
width,
|
|
95
|
-
' Runtime read model not wired.',
|
|
96
|
-
'This panel needs the shared runtime read model to inspect clients, requests, and approvals.',
|
|
97
|
-
[{ command: '/cockpit', summary: 'use the cockpit while runtime wiring is unavailable' }],
|
|
98
|
-
C,
|
|
99
|
-
),
|
|
100
|
-
}],
|
|
101
|
-
palette: C,
|
|
102
|
-
});
|
|
103
|
-
while (workspace.length < height) workspace.push(createEmptyLine(width));
|
|
104
|
-
return workspace;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const snapshot = this.readModel.getSnapshot();
|
|
108
|
-
const approvals = snapshot.approvals;
|
|
109
|
-
const sessions = snapshot.sessions;
|
|
110
|
-
const recentEvents = snapshot.recentEvents;
|
|
111
|
-
const clients = this.getItems();
|
|
112
|
-
|
|
113
|
-
const headerLines: Line[] = [
|
|
114
|
-
buildKeyValueLine(width, [
|
|
115
|
-
{ label: 'state', value: snapshot.connectionState, valueColor: connectionColor(snapshot.connectionState) },
|
|
116
|
-
{ label: 'clients', value: String(snapshot.activeClientIds.length), valueColor: snapshot.activeClientIds.length > 0 ? C.ok : C.dim },
|
|
117
|
-
{ label: 'requests', value: String(snapshot.requestCount), valueColor: snapshot.requestCount > 0 ? C.info : C.dim },
|
|
118
|
-
{ label: 'errors', value: String(snapshot.errorCount), valueColor: snapshot.errorCount > 0 ? C.error : C.dim },
|
|
119
|
-
], C),
|
|
120
|
-
buildKeyValueLine(width, [
|
|
121
|
-
{ label: 'host', value: `${snapshot.host}:${snapshot.port}`, valueColor: C.value },
|
|
122
|
-
{ label: 'approvals', value: String(approvals.filter((entry) => entry.status === 'pending').length), valueColor: approvals.some((entry) => entry.status === 'pending') ? C.warn : C.dim },
|
|
123
|
-
{ label: 'sessions', value: String(sessions.length), valueColor: sessions.length > 0 ? C.info : C.dim },
|
|
124
|
-
{ label: 'events', value: String(recentEvents.length), valueColor: recentEvents.length > 0 ? C.info : C.dim },
|
|
125
|
-
], C),
|
|
126
|
-
buildGuidanceLine(width, '/cockpit', 'use confirmed runtime actions for interventions while this panel tracks overall posture', C),
|
|
127
|
-
];
|
|
128
|
-
|
|
129
|
-
if (clients.length === 0 && approvals.length === 0 && sessions.length === 0) {
|
|
130
|
-
return this.renderList(width, height, {
|
|
131
|
-
title: 'Runtime Status',
|
|
132
|
-
header: headerLines,
|
|
133
|
-
emptyMessage: ' No runtime activity recorded.',
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
this.clampSelection();
|
|
138
|
-
const selected = clients[this.selectedIndex];
|
|
139
|
-
|
|
140
|
-
const footerLines: Line[] = [];
|
|
141
|
-
if (selected) {
|
|
142
|
-
footerLines.push(
|
|
143
|
-
buildPanelLine(width, [
|
|
144
|
-
[' Client: ', C.label],
|
|
145
|
-
[selected.label, C.value],
|
|
146
|
-
[' Kind: ', C.label],
|
|
147
|
-
[selected.kind, C.info],
|
|
148
|
-
]),
|
|
149
|
-
buildPanelLine(width, [
|
|
150
|
-
[' Transport: ', C.label],
|
|
151
|
-
[selected.transport, C.value],
|
|
152
|
-
[' Connected: ', C.label],
|
|
153
|
-
[selected.connected ? 'yes' : 'no', selected.connected ? C.ok : C.warn],
|
|
154
|
-
]),
|
|
155
|
-
buildPanelLine(width, [
|
|
156
|
-
[' Route: ', C.label],
|
|
157
|
-
[selected.routeId ?? 'n/a', C.dim],
|
|
158
|
-
[' Session: ', C.label],
|
|
159
|
-
[selected.sessionId ?? 'n/a', C.dim],
|
|
160
|
-
]),
|
|
161
|
-
buildPanelLine(width, [
|
|
162
|
-
[' Last seen: ', C.label],
|
|
163
|
-
[formatTime(selected.lastSeenAt), C.dim],
|
|
164
|
-
[' Remote: ', C.label],
|
|
165
|
-
[truncateDisplay(selected.remoteAddress ?? 'n/a', Math.max(0, width - 36)), C.dim],
|
|
166
|
-
]),
|
|
167
|
-
);
|
|
168
|
-
} else {
|
|
169
|
-
footerLines.push(buildPanelLine(width, [[' No connected client selected.', C.dim]]));
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (approvals.length > 0) {
|
|
173
|
-
footerLines.push(
|
|
174
|
-
...approvals.slice(0, 6).map((approval) => buildPanelLine(width, [
|
|
175
|
-
[' ', C.label],
|
|
176
|
-
[approval.status.padEnd(10), approval.status === 'pending' ? C.warn : approval.status === 'approved' ? C.ok : approval.status === 'denied' ? C.error : C.dim],
|
|
177
|
-
[` ${truncateDisplay(approval.request.tool, 16).padEnd(16)}`, C.value],
|
|
178
|
-
[` ${truncateDisplay(approval.sessionId ?? approval.id, Math.max(0, width - 30))}`, C.dim],
|
|
179
|
-
])),
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (sessions.length > 0) {
|
|
184
|
-
footerLines.push(
|
|
185
|
-
...sessions.slice(0, 6).map((session) => buildPanelLine(width, [
|
|
186
|
-
[' ', C.label],
|
|
187
|
-
[session.status.padEnd(10), session.status === 'active' ? C.ok : C.dim],
|
|
188
|
-
[` ${truncateDisplay(session.title, 20).padEnd(20)}`, C.value],
|
|
189
|
-
[` ${truncateDisplay(session.activeAgentId ?? session.id, Math.max(0, width - 34))}`, C.dim],
|
|
190
|
-
])),
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (recentEvents.length > 0) {
|
|
195
|
-
footerLines.push(
|
|
196
|
-
...recentEvents.slice(0, 6).map((event) => buildPanelLine(width, [
|
|
197
|
-
[' ', C.label],
|
|
198
|
-
[truncateDisplay(event.event, 16).padEnd(16), C.info],
|
|
199
|
-
[` ${truncateDisplay(typeof event.payload === 'string' ? event.payload : JSON.stringify(event.payload) ?? '', Math.max(0, width - 19))}`, C.dim],
|
|
200
|
-
])),
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
footerLines.push(buildPanelLine(width, [[' Up/Down move through connected clients', C.dim]]));
|
|
204
|
-
|
|
205
|
-
return this.renderList(width, height, {
|
|
206
|
-
title: 'Runtime Status',
|
|
207
|
-
header: headerLines,
|
|
208
|
-
footer: footerLines,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
}
|