@pellux/goodvibes-agent 0.1.1 → 0.1.3
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 +14 -0
- package/README.md +12 -1
- package/docs/README.md +2 -0
- package/docs/getting-started.md +19 -1
- package/docs/release-and-publishing.md +3 -1
- package/package.json +10 -1
- package/src/agent/persona-registry.ts +379 -0
- package/src/agent/skill-registry.ts +360 -0
- package/src/audio/spoken-turn-model-routing.ts +2 -1
- package/src/cli/agent-knowledge-command.ts +525 -0
- package/src/cli/help.ts +35 -0
- package/src/cli/management-commands.ts +3 -1
- package/src/cli/management.ts +33 -9
- package/src/cli/parser.ts +7 -0
- package/src/cli/types.ts +3 -0
- package/src/config/surface.ts +1 -0
- package/src/input/agent-workspace.ts +33 -3
- package/src/input/command-registry.ts +4 -1
- package/src/input/commands/agent-skills-runtime.ts +216 -0
- package/src/input/commands/delegation-runtime.ts +129 -0
- package/src/input/commands/knowledge.ts +18 -18
- package/src/input/commands/personas-runtime.ts +219 -0
- package/src/input/commands/shell-core.ts +9 -6
- package/src/input/commands/skills-runtime.ts +7 -2
- package/src/input/commands.ts +6 -0
- package/src/input/panel-integration-actions.ts +0 -52
- package/src/input/submission-router.ts +1 -1
- package/src/main.ts +2 -1
- package/src/panels/builtin/agent.ts +0 -14
- package/src/panels/builtin/session.ts +4 -3
- package/src/panels/index.ts +0 -5
- package/src/panels/orchestration-panel.ts +4 -5
- package/src/panels/qr-panel.ts +3 -2
- package/src/panels/tasks-panel.ts +4 -4
- package/src/renderer/agent-workspace.ts +2 -0
- package/src/runtime/bootstrap-command-context.ts +3 -0
- package/src/runtime/bootstrap-command-parts.ts +6 -2
- package/src/runtime/bootstrap-core.ts +8 -4
- package/src/runtime/bootstrap-shell.ts +5 -2
- package/src/runtime/bootstrap.ts +10 -2
- package/src/runtime/cloudflare-control-plane.ts +2 -1
- package/src/version.ts +1 -1
- package/src/daemon/cli.ts +0 -55
- package/src/daemon/safe-serve.ts +0 -61
- package/src/panels/diff-panel.ts +0 -520
- package/src/panels/file-explorer-panel.ts +0 -584
- package/src/panels/file-preview-panel.ts +0 -434
- package/src/panels/git-panel.ts +0 -638
- package/src/panels/sandbox-panel.ts +0 -283
- package/src/panels/symbol-outline-panel.ts +0 -486
- package/src/panels/worktree-panel.ts +0 -182
- package/src/panels/wrfc-panel.ts +0 -609
|
@@ -1,283 +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 { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
5
|
-
import { buildSandboxReview, listSandboxPresets, listSandboxProfiles } from '@/runtime/index.ts';
|
|
6
|
-
import type { SandboxSessionRegistry } from '@/runtime/index.ts';
|
|
7
|
-
import {
|
|
8
|
-
buildBodyText,
|
|
9
|
-
buildEmptyState,
|
|
10
|
-
buildGuidanceLine,
|
|
11
|
-
buildKeyValueLine,
|
|
12
|
-
buildPanelLine,
|
|
13
|
-
buildPanelWorkspace,
|
|
14
|
-
resolveStackedScrollableSections,
|
|
15
|
-
DEFAULT_PANEL_PALETTE,
|
|
16
|
-
type PanelWorkspaceSection,
|
|
17
|
-
} from './polish.ts';
|
|
18
|
-
|
|
19
|
-
const C = {
|
|
20
|
-
...DEFAULT_PANEL_PALETTE,
|
|
21
|
-
header: '#e2e8f0',
|
|
22
|
-
headerBg: '#0f172a',
|
|
23
|
-
} as const;
|
|
24
|
-
|
|
25
|
-
export class SandboxPanel extends BasePanel {
|
|
26
|
-
private selectedIndex = 0;
|
|
27
|
-
private scrollOffset = 0;
|
|
28
|
-
private readonly config: ConfigManager;
|
|
29
|
-
private readonly sessions: SandboxSessionRegistry;
|
|
30
|
-
|
|
31
|
-
public constructor(
|
|
32
|
-
config: ConfigManager,
|
|
33
|
-
sessions: SandboxSessionRegistry,
|
|
34
|
-
) {
|
|
35
|
-
super('sandbox', 'Sandbox', 'X', 'monitoring');
|
|
36
|
-
this.config = config;
|
|
37
|
-
this.sessions = sessions;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public handleInput(key: string): boolean {
|
|
41
|
-
const profileCount = listSandboxProfiles(this.config).length;
|
|
42
|
-
const sessionCount = this.sessions.list().length;
|
|
43
|
-
const itemCount = profileCount + sessionCount;
|
|
44
|
-
if (itemCount === 0) return false;
|
|
45
|
-
if (key === 'up' || key === 'k') {
|
|
46
|
-
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
47
|
-
this.markDirty();
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
if (key === 'down' || key === 'j') {
|
|
51
|
-
this.selectedIndex = Math.min(itemCount - 1, this.selectedIndex + 1);
|
|
52
|
-
this.markDirty();
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
if (key === 'home') {
|
|
56
|
-
this.selectedIndex = 0;
|
|
57
|
-
this.markDirty();
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
if (key === 'end') {
|
|
61
|
-
this.selectedIndex = sessionCount > 0 ? profileCount : Math.max(0, itemCount - 1);
|
|
62
|
-
this.markDirty();
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public render(width: number, height: number): Line[] {
|
|
69
|
-
this.needsRender = false;
|
|
70
|
-
const review = buildSandboxReview(this.config);
|
|
71
|
-
const profiles = listSandboxProfiles(this.config);
|
|
72
|
-
const presets = listSandboxPresets();
|
|
73
|
-
const sessions = this.sessions.list();
|
|
74
|
-
const selectable = [
|
|
75
|
-
...profiles.map((profile) => ({ kind: 'profile' as const, id: profile.id })),
|
|
76
|
-
...sessions.map((session) => ({ kind: 'session' as const, id: session.id })),
|
|
77
|
-
];
|
|
78
|
-
this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, selectable.length - 1));
|
|
79
|
-
const selected = selectable[this.selectedIndex] ?? null;
|
|
80
|
-
const selectedProfile = selected?.kind === 'profile'
|
|
81
|
-
? profiles.find((profile) => profile.id === selected.id) ?? null
|
|
82
|
-
: null;
|
|
83
|
-
const selectedSession = selected?.kind === 'session'
|
|
84
|
-
? sessions.find((session) => session.id === selected.id) ?? null
|
|
85
|
-
: null;
|
|
86
|
-
|
|
87
|
-
const intro = 'External sandbox posture. Agent can inspect copied sandbox configuration, but GoodVibes TUI owns QEMU setup, profiles, sessions, and command execution.';
|
|
88
|
-
const overviewLines: Line[] = [
|
|
89
|
-
buildKeyValueLine(width, [
|
|
90
|
-
{ label: 'host', value: review.host.platform, valueColor: C.value },
|
|
91
|
-
{ label: 'backend', value: review.config.vmBackend, valueColor: C.info },
|
|
92
|
-
{ label: 'windows mode', value: review.config.windowsMode, valueColor: review.host.secureSandboxReady || !review.host.windows ? C.good : C.warn },
|
|
93
|
-
], C),
|
|
94
|
-
buildKeyValueLine(width, [
|
|
95
|
-
{ label: 'repl isolation', value: review.config.replIsolation, valueColor: C.value },
|
|
96
|
-
{ label: 'mcp isolation', value: review.config.mcpIsolation, valueColor: review.config.mcpIsolation === 'disabled' ? C.warn : C.good },
|
|
97
|
-
], C),
|
|
98
|
-
buildKeyValueLine(width, [
|
|
99
|
-
{ label: 'virtualization', value: review.config.vmBackend === 'local' ? 'disabled' : (review.host.secureSandboxReady ? 'ready' : 'host blocked'), valueColor: review.config.vmBackend === 'local' ? C.dim : (review.host.secureSandboxReady ? C.good : C.warn) },
|
|
100
|
-
{ label: 'warnings', value: String(review.host.warnings.length), valueColor: review.host.warnings.length > 0 ? C.warn : C.dim },
|
|
101
|
-
{ label: 'sessions', value: String(sessions.length), valueColor: sessions.length > 0 ? C.info : C.dim },
|
|
102
|
-
], C),
|
|
103
|
-
buildKeyValueLine(width, [
|
|
104
|
-
{ label: 'qemu binary', value: review.config.qemuBinary || '(default)', valueColor: C.value },
|
|
105
|
-
{ label: 'qemu image', value: review.config.qemuImagePath || '(not configured)', valueColor: review.config.qemuImagePath ? C.info : C.warn },
|
|
106
|
-
], C),
|
|
107
|
-
buildKeyValueLine(width, [
|
|
108
|
-
{ label: 'qemu wrapper', value: review.config.qemuExecWrapper || '(not configured)', valueColor: review.config.qemuExecWrapper ? C.info : C.warn },
|
|
109
|
-
{ label: 'guest host', value: review.config.qemuGuestHost || '(not configured)', valueColor: review.config.qemuGuestHost ? C.info : C.warn },
|
|
110
|
-
], C),
|
|
111
|
-
buildKeyValueLine(width, [
|
|
112
|
-
{ label: 'guest port', value: String(review.config.qemuGuestPort), valueColor: C.value },
|
|
113
|
-
{ label: 'guest user', value: review.config.qemuGuestUser || '(not configured)', valueColor: review.config.qemuGuestUser ? C.info : C.warn },
|
|
114
|
-
{ label: 'session mode', value: review.config.qemuSessionMode, valueColor: review.config.qemuSessionMode === 'attach' ? C.dim : C.info },
|
|
115
|
-
], C),
|
|
116
|
-
buildKeyValueLine(width, [
|
|
117
|
-
{ label: 'guest workspace', value: review.config.qemuWorkspacePath || '(not configured)', valueColor: review.config.qemuWorkspacePath ? C.value : C.warn },
|
|
118
|
-
], C),
|
|
119
|
-
buildGuidanceLine(width, '/sandbox', 'externalized here; use GoodVibes TUI for sandbox/QEMU setup or execution', C),
|
|
120
|
-
buildGuidanceLine(width, '/delegate --wrfc <task>', 'send explicit build/fix/review work that needs sandboxing to GoodVibes TUI', C),
|
|
121
|
-
buildGuidanceLine(width, '/setup sandbox', 'show Agent boundary guidance without changing sandbox configuration', C),
|
|
122
|
-
buildPanelLine(width, [[` Up/Down move Home/End jump focus=profiles+sessions`, C.dim]]),
|
|
123
|
-
];
|
|
124
|
-
|
|
125
|
-
const selectionLines: Line[] = [];
|
|
126
|
-
if (selectedProfile) {
|
|
127
|
-
selectionLines.push(buildKeyValueLine(width, [
|
|
128
|
-
{ label: 'id', value: selectedProfile.id, valueColor: C.info },
|
|
129
|
-
{ label: 'kind', value: selectedProfile.kind, valueColor: C.value },
|
|
130
|
-
{ label: 'isolation', value: selectedProfile.isolation, valueColor: C.value },
|
|
131
|
-
{ label: 'vm', value: selectedProfile.requiresVm ? 'required' : 'optional', valueColor: selectedProfile.requiresVm ? C.good : C.warn },
|
|
132
|
-
], C));
|
|
133
|
-
selectionLines.push(...buildBodyText(width, selectedProfile.notes.join(' | '), C, C.dim));
|
|
134
|
-
} else if (selectedSession) {
|
|
135
|
-
selectionLines.push(buildKeyValueLine(width, [
|
|
136
|
-
{ label: 'profile', value: selectedSession.profileId, valueColor: C.info },
|
|
137
|
-
{ label: 'state', value: selectedSession.state, valueColor: selectedSession.state === 'running' ? C.good : selectedSession.state === 'failed' ? C.bad : C.warn },
|
|
138
|
-
{ label: 'backend', value: selectedSession.resolvedBackend ?? selectedSession.backend, valueColor: C.value },
|
|
139
|
-
{ label: 'sharing', value: selectedSession.shared ? 'shared' : 'dedicated', valueColor: C.value },
|
|
140
|
-
], C));
|
|
141
|
-
if (selectedSession.startupStatus) {
|
|
142
|
-
selectionLines.push(buildKeyValueLine(width, [
|
|
143
|
-
{ label: 'startup', value: selectedSession.startupStatus, valueColor: selectedSession.startupStatus === 'verified' ? C.good : selectedSession.startupStatus === 'failed' ? C.bad : C.warn },
|
|
144
|
-
], C));
|
|
145
|
-
}
|
|
146
|
-
if (selectedSession.launchPlan) {
|
|
147
|
-
selectionLines.push(...buildBodyText(width, `Launch plan: ${selectedSession.launchPlan.summary}`, C, C.dim));
|
|
148
|
-
}
|
|
149
|
-
if (selectedSession.startupDetail) {
|
|
150
|
-
selectionLines.push(...buildBodyText(width, `Startup: ${selectedSession.startupDetail}`, C, selectedSession.startupStatus === 'failed' ? C.bad : C.dim));
|
|
151
|
-
}
|
|
152
|
-
if (selectedSession.lastCommandSummary) {
|
|
153
|
-
selectionLines.push(...buildBodyText(width, `Last command: ${selectedSession.lastCommandSummary}`, C, C.dim));
|
|
154
|
-
}
|
|
155
|
-
if (selectedSession.lastRunAt !== undefined) {
|
|
156
|
-
selectionLines.push(buildKeyValueLine(width, [
|
|
157
|
-
{ label: 'runs', value: String(selectedSession.executionCount ?? 0), valueColor: C.info },
|
|
158
|
-
{ label: 'last exit', value: String(selectedSession.lastExitStatus ?? 'n/a'), valueColor: selectedSession.lastExitStatus === 0 ? C.good : C.warn },
|
|
159
|
-
{ label: 'last run', value: new Date(selectedSession.lastRunAt).toISOString(), valueColor: C.dim },
|
|
160
|
-
], C));
|
|
161
|
-
}
|
|
162
|
-
if (selectedSession.lastStdoutPreview) {
|
|
163
|
-
selectionLines.push(...buildBodyText(width, `stdout: ${selectedSession.lastStdoutPreview}`, C, C.dim));
|
|
164
|
-
}
|
|
165
|
-
if (selectedSession.lastStderrPreview) {
|
|
166
|
-
selectionLines.push(...buildBodyText(width, `stderr: ${selectedSession.lastStderrPreview}`, C, C.bad));
|
|
167
|
-
}
|
|
168
|
-
selectionLines.push(...buildBodyText(width, selectedSession.notes.join(' | '), C, C.dim));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const sessionLines: Line[] = [];
|
|
172
|
-
if (sessions.length === 0) {
|
|
173
|
-
sessionLines.push(...buildEmptyState(
|
|
174
|
-
width,
|
|
175
|
-
' No Agent-owned sandbox sessions.',
|
|
176
|
-
'GoodVibes Agent does not start sandbox sessions. Delegate build/fix/review work to GoodVibes TUI when sandbox execution is required.',
|
|
177
|
-
[{ command: '/delegate --wrfc <task>', summary: 'delegate explicit sandbox-backed build work to GoodVibes TUI' }],
|
|
178
|
-
C,
|
|
179
|
-
));
|
|
180
|
-
} else {
|
|
181
|
-
for (const session of sessions) {
|
|
182
|
-
const bg = selectedSession?.id === session.id ? C.headerBg : undefined;
|
|
183
|
-
sessionLines.push(buildPanelLine(width, [
|
|
184
|
-
[' ', C.label],
|
|
185
|
-
[session.profileId.padEnd(15), C.info, bg],
|
|
186
|
-
[session.state.padEnd(10), session.state === 'running' ? C.good : session.state === 'failed' ? C.bad : C.warn, bg],
|
|
187
|
-
[(session.shared ? 'shared' : 'dedicated').padEnd(12), C.value, bg],
|
|
188
|
-
[String(session.resolvedBackend ?? session.backend).padEnd(8), C.dim, bg],
|
|
189
|
-
[` ${(session.startupStatus ?? 'n/a').slice(0, 8).padEnd(8)}`, session.startupStatus === 'verified' ? C.good : session.startupStatus === 'failed' ? C.bad : C.warn, bg],
|
|
190
|
-
[` ${String(session.executionCount ?? 0).padStart(3)}x`, C.info, bg],
|
|
191
|
-
[` ${session.id.slice(0, Math.max(8, Math.min(14, width - 64)))}`, C.dim, bg],
|
|
192
|
-
]));
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const presetLines: Line[] = [];
|
|
197
|
-
for (const preset of presets.slice(0, 2)) {
|
|
198
|
-
presetLines.push(buildPanelLine(width, [
|
|
199
|
-
[' ', C.label],
|
|
200
|
-
[preset.id.padEnd(18), C.info],
|
|
201
|
-
[preset.config.replIsolation.padEnd(16), C.value],
|
|
202
|
-
[preset.config.mcpIsolation.padEnd(16), C.dim],
|
|
203
|
-
[preset.config.windowsMode, C.warn],
|
|
204
|
-
]));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const profileLines: Line[] = [];
|
|
208
|
-
for (const profile of profiles) {
|
|
209
|
-
const bg = selectedProfile?.id === profile.id ? C.headerBg : undefined;
|
|
210
|
-
profileLines.push(buildPanelLine(width, [
|
|
211
|
-
[' ', C.label],
|
|
212
|
-
[profile.id.padEnd(15), C.info, bg],
|
|
213
|
-
[profile.isolation.padEnd(14), C.value, bg],
|
|
214
|
-
[profile.kind.padEnd(12), C.dim, bg],
|
|
215
|
-
[` vm=${profile.requiresVm ? 'yes' : 'no'}`, profile.requiresVm ? C.good : C.warn, bg],
|
|
216
|
-
]));
|
|
217
|
-
}
|
|
218
|
-
const postureSection: PanelWorkspaceSection = { title: 'Sandbox posture', lines: overviewLines };
|
|
219
|
-
const selectedSection: PanelWorkspaceSection = { title: selectedProfile ? 'Selected Profile' : 'Selected Session', lines: selectionLines };
|
|
220
|
-
const presetsSection: PanelWorkspaceSection = { title: 'Presets', lines: presetLines };
|
|
221
|
-
const [sessionsSection, profilesSection] = resolveStackedScrollableSections(width, height, {
|
|
222
|
-
intro,
|
|
223
|
-
footerLines: [
|
|
224
|
-
buildGuidanceLine(width, '/sandbox', 'blocked here; sandbox lifecycle belongs to GoodVibes TUI', C),
|
|
225
|
-
buildGuidanceLine(width, '/delegate --wrfc <task>', 'delegate explicit sandbox-backed build work to GoodVibes TUI', C),
|
|
226
|
-
],
|
|
227
|
-
palette: C,
|
|
228
|
-
beforeSections: [
|
|
229
|
-
postureSection,
|
|
230
|
-
...(selectionLines.length > 0 ? [selectedSection] : []),
|
|
231
|
-
],
|
|
232
|
-
sections: [
|
|
233
|
-
{
|
|
234
|
-
title: 'Sessions',
|
|
235
|
-
scrollableLines: sessionLines,
|
|
236
|
-
selectedIndex: selectedSession ? Math.max(0, sessions.findIndex((session) => session.id === selectedSession.id)) : undefined,
|
|
237
|
-
scrollOffset: this.scrollOffset,
|
|
238
|
-
minRows: 2,
|
|
239
|
-
weight: 1,
|
|
240
|
-
appendWindowSummary: sessions.length > 0 ? {
|
|
241
|
-
dimColor: C.dim,
|
|
242
|
-
formatter: (window) => buildPanelLine(width, [[` [${window.start + 1}-${window.end} of ${sessions.length}]`, C.dim]]),
|
|
243
|
-
} : undefined,
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
title: 'Profiles',
|
|
247
|
-
scrollableLines: profileLines,
|
|
248
|
-
selectedIndex: selectedProfile ? Math.max(0, profiles.findIndex((profile) => profile.id === selectedProfile.id)) : undefined,
|
|
249
|
-
scrollOffset: this.scrollOffset,
|
|
250
|
-
minRows: 2,
|
|
251
|
-
weight: 1,
|
|
252
|
-
appendWindowSummary: profiles.length > 0 ? {
|
|
253
|
-
dimColor: C.dim,
|
|
254
|
-
formatter: (window) => buildPanelLine(width, [[` [${window.start + 1}-${window.end} of ${profiles.length}]`, C.dim]]),
|
|
255
|
-
} : undefined,
|
|
256
|
-
},
|
|
257
|
-
],
|
|
258
|
-
afterSections: [presetsSection],
|
|
259
|
-
});
|
|
260
|
-
this.scrollOffset = sessionsSection?.scrollOffset ?? this.scrollOffset;
|
|
261
|
-
|
|
262
|
-
const sections: PanelWorkspaceSection[] = [
|
|
263
|
-
postureSection,
|
|
264
|
-
...(selectionLines.length > 0 ? [selectedSection] : []),
|
|
265
|
-
sessionsSection?.section ?? { title: 'Sessions', lines: sessionLines },
|
|
266
|
-
presetsSection,
|
|
267
|
-
profilesSection?.section ?? { title: 'Profiles', lines: profileLines },
|
|
268
|
-
];
|
|
269
|
-
|
|
270
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
271
|
-
title: 'External Sandbox Boundary',
|
|
272
|
-
intro,
|
|
273
|
-
sections,
|
|
274
|
-
footerLines: [
|
|
275
|
-
buildGuidanceLine(width, '/sandbox', 'blocked here; sandbox lifecycle belongs to GoodVibes TUI', C),
|
|
276
|
-
buildGuidanceLine(width, '/delegate --wrfc <task>', 'delegate explicit sandbox-backed build work to GoodVibes TUI', C),
|
|
277
|
-
],
|
|
278
|
-
palette: C,
|
|
279
|
-
});
|
|
280
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
281
|
-
return lines.slice(0, height);
|
|
282
|
-
}
|
|
283
|
-
}
|