@pellux/goodvibes-agent 0.1.75 → 0.1.77
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 +1 -1
- package/src/agent/runtime-profile.ts +3 -0
- package/src/input/agent-workspace-categories.ts +1 -1
- package/src/input/agent-workspace-editors.ts +29 -1
- package/src/input/agent-workspace-snapshot.ts +30 -1
- package/src/input/agent-workspace-types.ts +21 -1
- package/src/input/agent-workspace.ts +35 -4
- package/src/input/onboarding/onboarding-wizard-apply.ts +10 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +37 -4
- package/src/renderer/agent-workspace.ts +48 -1
- package/src/runtime/onboarding/apply.ts +61 -1
- package/src/runtime/onboarding/types.ts +6 -1
- package/src/runtime/onboarding/verify.ts +21 -1
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GoodVibes Agent will be recorded here.
|
|
4
4
|
|
|
5
|
+
## 0.1.77 - 2026-06-01
|
|
6
|
+
|
|
7
|
+
- Added a first-class Agent profile creation form inside the fullscreen operator workspace instead of dispatching a placeholder command template.
|
|
8
|
+
- Surfaced profile and starter-template summaries directly in the Profiles workspace so day-one setup can pick an isolated Agent home from the TUI.
|
|
9
|
+
- Made Agent runtime profile creation refuse existing profile homes to protect local profile state across CLI, onboarding, and workspace flows.
|
|
10
|
+
|
|
11
|
+
## 0.1.76 - 2026-05-31
|
|
12
|
+
|
|
13
|
+
- Added optional starter profile creation to first-run onboarding so setup can create an isolated Agent home seeded with persona, skill, and routine templates.
|
|
14
|
+
- Added onboarding apply/verify support for Agent profile creation with prevalidation, rollback, and overwrite protection.
|
|
15
|
+
- Updated onboarding and renderer regressions so the first-run flow stays Agent-specific while exposing the starter profile path directly in the TUI.
|
|
16
|
+
|
|
5
17
|
## 0.1.75 - 2026-05-31
|
|
6
18
|
|
|
7
19
|
- Reworded Agent status, doctor, and external runtime diagnostics away from copied service/surface ownership language.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.77",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "GoodVibes personal operator assistant TUI with a proactive Agent product brain, isolated Agent Knowledge, local profiles, routines, skills, personas, and explicit build delegation.",
|
|
6
6
|
"type": "module",
|
|
@@ -727,6 +727,9 @@ export function listAgentRuntimeProfiles(baseHomeDirectory: string): readonly Ag
|
|
|
727
727
|
|
|
728
728
|
export function createAgentRuntimeProfile(baseHomeDirectory: string, profileName: string, options: CreateAgentRuntimeProfileOptions = {}): AgentRuntimeProfileInfo {
|
|
729
729
|
const resolution = resolveAgentRuntimeProfileHome(baseHomeDirectory, profileName);
|
|
730
|
+
if (existsSync(resolution.homeDirectory)) {
|
|
731
|
+
throw new Error(`Agent profile already exists: ${resolution.id}`);
|
|
732
|
+
}
|
|
730
733
|
mkdirSync(resolution.homeDirectory, { recursive: true });
|
|
731
734
|
const createdAt = new Date().toISOString();
|
|
732
735
|
const appliedTemplate = options.templateId
|
|
@@ -98,7 +98,7 @@ export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
|
|
|
98
98
|
{ id: 'profile-sync-list', label: 'Profile sync list', detail: 'Inspect saved config profiles available for export/import.', command: '/profilesync list', kind: 'command', safety: 'read-only' },
|
|
99
99
|
{ id: 'profile-sync-export', label: 'Export profile sync', detail: 'Export config profiles to a portable bundle. Requires a real path and explicit --yes.', command: '/profilesync export <path> --yes', kind: 'command', safety: 'safe' },
|
|
100
100
|
{ id: 'setup-transfer-export', label: 'Export setup transfer', detail: 'Export Agent setup transfer data from the current home. Requires a real path and explicit --yes.', command: '/setup transfer export <path> --yes', kind: 'command', safety: 'safe' },
|
|
101
|
-
{ id: 'runtime-profile-create', label: 'Create Agent profile', detail: '
|
|
101
|
+
{ id: 'runtime-profile-create', label: 'Create Agent profile', detail: 'Open an in-workspace form that creates an isolated Agent home from a built-in or local starter.', editorKind: 'profile', kind: 'editor', safety: 'safe' },
|
|
102
102
|
{ id: 'runtime-profile-template-edit', label: 'Customize starter', detail: 'Export a starter JSON file, edit it, import it as a local starter, then create a profile from it.', command: '/agent-profile template export <id> <path> --yes', kind: 'command', safety: 'safe' },
|
|
103
103
|
{ id: 'runtime-profile-switch', label: 'Switch Agent profile', detail: 'Launch goodvibes-agent --agent-profile <name> to use that isolated Agent home. This workspace cannot switch the current process home after startup.', kind: 'guidance', safety: 'safe' },
|
|
104
104
|
],
|
|
@@ -1,9 +1,36 @@
|
|
|
1
1
|
import type { AgentPersonaRecord } from '../agent/persona-registry.ts';
|
|
2
2
|
import type { AgentRoutineRecord } from '../agent/routine-registry.ts';
|
|
3
3
|
import type { AgentSkillRecord } from '../agent/skill-registry.ts';
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
AgentWorkspaceLocalEditor,
|
|
6
|
+
AgentWorkspaceLocalEditorKind,
|
|
7
|
+
AgentWorkspaceLocalLibraryItem,
|
|
8
|
+
AgentWorkspaceRuntimeStarterTemplateItem,
|
|
9
|
+
} from './agent-workspace-types.ts';
|
|
10
|
+
|
|
11
|
+
export function createProfileEditor(templates: readonly AgentWorkspaceRuntimeStarterTemplateItem[]): AgentWorkspaceLocalEditor {
|
|
12
|
+
const defaultTemplate = templates.find((template) => template.id === 'research')?.id ?? templates[0]?.id ?? 'none';
|
|
13
|
+
const preview = templates.length === 0
|
|
14
|
+
? 'No starter templates found; use none to create an empty isolated profile.'
|
|
15
|
+
: templates
|
|
16
|
+
.slice(0, 6)
|
|
17
|
+
.map((template) => `${template.id} (${template.name})`)
|
|
18
|
+
.join(', ');
|
|
19
|
+
return {
|
|
20
|
+
kind: 'profile',
|
|
21
|
+
mode: 'create',
|
|
22
|
+
title: 'Create Agent Profile',
|
|
23
|
+
selectedFieldIndex: 0,
|
|
24
|
+
message: 'Create an isolated Agent home seeded with a persona, skills, and routines. The current process keeps using its existing home until relaunched with --agent-profile.',
|
|
25
|
+
fields: [
|
|
26
|
+
{ id: 'name', label: 'Profile name', value: '', required: true, multiline: false, hint: 'Short profile name. It normalizes to lowercase letters, numbers, dots, underscores, and dashes.' },
|
|
27
|
+
{ id: 'template', label: 'Starter template', value: defaultTemplate, required: false, multiline: false, hint: `Template id or none. Available: ${preview}.` },
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
5
31
|
|
|
6
32
|
export function createLocalEditor(kind: AgentWorkspaceLocalEditorKind): AgentWorkspaceLocalEditor {
|
|
33
|
+
if (kind === 'profile') return createProfileEditor([]);
|
|
7
34
|
if (kind === 'persona') {
|
|
8
35
|
return {
|
|
9
36
|
kind,
|
|
@@ -137,6 +164,7 @@ export function isAffirmative(value: string): boolean {
|
|
|
137
164
|
}
|
|
138
165
|
|
|
139
166
|
export function editorCategoryId(kind: AgentWorkspaceLocalEditorKind): string {
|
|
167
|
+
if (kind === 'profile') return 'profiles';
|
|
140
168
|
if (kind === 'persona') return 'personas';
|
|
141
169
|
if (kind === 'skill') return 'skills';
|
|
142
170
|
return 'routines';
|
|
@@ -6,7 +6,12 @@ import { AgentSkillRegistry, type AgentSkillRecord } from '../agent/skill-regist
|
|
|
6
6
|
import { getAgentRuntimeProfilesRoot, listAgentRuntimeProfiles, listAgentRuntimeProfileTemplates } from '../agent/runtime-profile.ts';
|
|
7
7
|
import { buildAgentWorkspaceChannels } from './agent-workspace-channels.ts';
|
|
8
8
|
import { buildAgentWorkspaceSetupChecklist } from './agent-workspace-setup.ts';
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
AgentWorkspaceLocalLibraryItem,
|
|
11
|
+
AgentWorkspaceRuntimeProfileItem,
|
|
12
|
+
AgentWorkspaceRuntimeSnapshot,
|
|
13
|
+
AgentWorkspaceRuntimeStarterTemplateItem,
|
|
14
|
+
} from './agent-workspace-types.ts';
|
|
10
15
|
|
|
11
16
|
type AgentWorkspaceConfigReader = {
|
|
12
17
|
get(key: string): unknown;
|
|
@@ -94,6 +99,28 @@ function summarizeRoutineItem(routine: AgentRoutineRecord): AgentWorkspaceLocalL
|
|
|
94
99
|
};
|
|
95
100
|
}
|
|
96
101
|
|
|
102
|
+
function summarizeRuntimeProfile(profile: ReturnType<typeof listAgentRuntimeProfiles>[number]): AgentWorkspaceRuntimeProfileItem {
|
|
103
|
+
return {
|
|
104
|
+
id: profile.id,
|
|
105
|
+
homeDirectory: profile.homeDirectory,
|
|
106
|
+
createdAt: profile.createdAt,
|
|
107
|
+
starterTemplateId: profile.starterTemplateId,
|
|
108
|
+
starterTemplateName: profile.starterTemplateName,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function summarizeStarterTemplate(template: ReturnType<typeof listAgentRuntimeProfileTemplates>[number]): AgentWorkspaceRuntimeStarterTemplateItem {
|
|
113
|
+
return {
|
|
114
|
+
id: template.id,
|
|
115
|
+
name: template.name,
|
|
116
|
+
description: template.description,
|
|
117
|
+
personaName: template.personaName,
|
|
118
|
+
skillNames: template.skillNames,
|
|
119
|
+
routineNames: template.routineNames,
|
|
120
|
+
source: template.source,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
97
124
|
export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): AgentWorkspaceRuntimeSnapshot {
|
|
98
125
|
const host = readConfigString(context, 'controlPlane.host', '127.0.0.1');
|
|
99
126
|
const port = readConfigNumber(context, 'controlPlane.port', 3421);
|
|
@@ -255,9 +282,11 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
|
|
|
255
282
|
browserSurfacePublicBaseUrl: readConfigString(context, 'web.publicBaseUrl', '(not configured)'),
|
|
256
283
|
activeRuntimeProfile: inferActiveRuntimeProfile(context.workspace?.shellPaths?.homeDirectory ?? ''),
|
|
257
284
|
runtimeProfileCount: runtimeProfiles.length,
|
|
285
|
+
runtimeProfiles: runtimeProfiles.map(summarizeRuntimeProfile),
|
|
258
286
|
runtimeProfileRoot: getAgentRuntimeProfilesRoot(context.workspace?.shellPaths?.homeDirectory ?? ''),
|
|
259
287
|
runtimeStarterTemplateCount: runtimeStarterTemplates.length,
|
|
260
288
|
localStarterTemplateCount: runtimeStarterTemplates.filter((template) => template.source === 'local').length,
|
|
289
|
+
runtimeStarterTemplates: runtimeStarterTemplates.map(summarizeStarterTemplate),
|
|
261
290
|
configProfileCount,
|
|
262
291
|
setupChecklist,
|
|
263
292
|
warnings,
|
|
@@ -7,7 +7,7 @@ export type AgentWorkspaceFocusPane = 'categories' | 'actions';
|
|
|
7
7
|
|
|
8
8
|
export type AgentWorkspaceActionKind = 'command' | 'guidance' | 'workspace' | 'editor' | 'local-selection' | 'local-operation';
|
|
9
9
|
|
|
10
|
-
export type AgentWorkspaceLocalEditorKind = 'persona' | 'skill' | 'routine';
|
|
10
|
+
export type AgentWorkspaceLocalEditorKind = 'persona' | 'skill' | 'routine' | 'profile';
|
|
11
11
|
|
|
12
12
|
export type AgentWorkspaceLocalOperation =
|
|
13
13
|
| 'persona-edit'
|
|
@@ -94,6 +94,24 @@ export interface AgentWorkspaceLocalLibraryItem {
|
|
|
94
94
|
readonly startCount?: number;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
export interface AgentWorkspaceRuntimeProfileItem {
|
|
98
|
+
readonly id: string;
|
|
99
|
+
readonly homeDirectory: string;
|
|
100
|
+
readonly createdAt: string | null;
|
|
101
|
+
readonly starterTemplateId?: string;
|
|
102
|
+
readonly starterTemplateName?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface AgentWorkspaceRuntimeStarterTemplateItem {
|
|
106
|
+
readonly id: string;
|
|
107
|
+
readonly name: string;
|
|
108
|
+
readonly description: string;
|
|
109
|
+
readonly personaName: string;
|
|
110
|
+
readonly skillNames: readonly string[];
|
|
111
|
+
readonly routineNames: readonly string[];
|
|
112
|
+
readonly source: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
97
115
|
export interface AgentWorkspaceRuntimeSnapshot {
|
|
98
116
|
readonly provider: string;
|
|
99
117
|
readonly model: string;
|
|
@@ -133,9 +151,11 @@ export interface AgentWorkspaceRuntimeSnapshot {
|
|
|
133
151
|
readonly browserSurfacePublicBaseUrl: string;
|
|
134
152
|
readonly activeRuntimeProfile: string;
|
|
135
153
|
readonly runtimeProfileCount: number;
|
|
154
|
+
readonly runtimeProfiles: readonly AgentWorkspaceRuntimeProfileItem[];
|
|
136
155
|
readonly runtimeProfileRoot: string;
|
|
137
156
|
readonly runtimeStarterTemplateCount: number;
|
|
138
157
|
readonly localStarterTemplateCount: number;
|
|
158
|
+
readonly runtimeStarterTemplates: readonly AgentWorkspaceRuntimeStarterTemplateItem[];
|
|
139
159
|
readonly configProfileCount: number;
|
|
140
160
|
readonly setupChecklist: readonly AgentWorkspaceSetupChecklistItem[];
|
|
141
161
|
readonly warnings: readonly string[];
|
|
@@ -3,9 +3,10 @@ import type { ShellPathService } from '@/runtime/index.ts';
|
|
|
3
3
|
import type { CommandContext } from './command-registry.ts';
|
|
4
4
|
import { AgentPersonaRegistry } from '../agent/persona-registry.ts';
|
|
5
5
|
import { AgentRoutineRegistry } from '../agent/routine-registry.ts';
|
|
6
|
+
import { createAgentRuntimeProfile, type AgentRuntimeProfileInfo } from '../agent/runtime-profile.ts';
|
|
6
7
|
import { AgentSkillRegistry } from '../agent/skill-registry.ts';
|
|
7
8
|
import { AGENT_WORKSPACE_CATEGORIES } from './agent-workspace-categories.ts';
|
|
8
|
-
import { createDeleteEditor, createLocalEditor, createPersonaUpdateEditor, createRoutineUpdateEditor, createSkillUpdateEditor, editorCategoryId, isAffirmative, splitList } from './agent-workspace-editors.ts';
|
|
9
|
+
import { createDeleteEditor, createLocalEditor, createPersonaUpdateEditor, createProfileEditor, createRoutineUpdateEditor, createSkillUpdateEditor, editorCategoryId, isAffirmative, splitList } from './agent-workspace-editors.ts';
|
|
9
10
|
import { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
|
|
10
11
|
import type { AgentWorkspaceAction, AgentWorkspaceActionResult, AgentWorkspaceCategory, AgentWorkspaceCommandDispatcher, AgentWorkspaceEditorField, AgentWorkspaceFocusPane, AgentWorkspaceLocalEditor, AgentWorkspaceLocalEditorKind, AgentWorkspaceLocalLibraryItem, AgentWorkspaceLocalOperation, AgentWorkspaceRuntimeSnapshot } from './agent-workspace-types.ts';
|
|
11
12
|
|
|
@@ -25,7 +26,6 @@ export type {
|
|
|
25
26
|
} from './agent-workspace-types.ts';
|
|
26
27
|
export { AGENT_WORKSPACE_MODAL_NAME } from './agent-workspace-types.ts';
|
|
27
28
|
export { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
|
|
28
|
-
|
|
29
29
|
function parseCommand(command: string): { readonly name: string; readonly args: readonly string[] } {
|
|
30
30
|
const trimmed = command.trim().replace(/^\//, '');
|
|
31
31
|
if (!trimmed) return { name: '', args: [] };
|
|
@@ -46,6 +46,7 @@ export class AgentWorkspace {
|
|
|
46
46
|
persona: 0,
|
|
47
47
|
skill: 0,
|
|
48
48
|
routine: 0,
|
|
49
|
+
profile: 0,
|
|
49
50
|
};
|
|
50
51
|
private context: CommandContext | null = null;
|
|
51
52
|
private dispatchCommand: AgentWorkspaceCommandDispatcher | null = null;
|
|
@@ -228,7 +229,9 @@ export class AgentWorkspace {
|
|
|
228
229
|
const action = this.selectedAction;
|
|
229
230
|
if (!action) return;
|
|
230
231
|
if (action.kind === 'editor' && action.editorKind) {
|
|
231
|
-
this.localEditor =
|
|
232
|
+
this.localEditor = action.editorKind === 'profile'
|
|
233
|
+
? createProfileEditor(this.runtimeSnapshot?.runtimeStarterTemplates ?? [])
|
|
234
|
+
: createLocalEditor(action.editorKind);
|
|
232
235
|
this.status = `Editing ${this.localEditor.title}.`;
|
|
233
236
|
this.lastActionResult = {
|
|
234
237
|
kind: 'guidance',
|
|
@@ -347,6 +350,7 @@ export class AgentWorkspace {
|
|
|
347
350
|
private localLibraryItems(kind: AgentWorkspaceLocalEditorKind): readonly AgentWorkspaceLocalLibraryItem[] {
|
|
348
351
|
if (kind === 'persona') return this.runtimeSnapshot?.localPersonas ?? [];
|
|
349
352
|
if (kind === 'skill') return this.runtimeSnapshot?.localSkills ?? [];
|
|
353
|
+
if (kind === 'profile') return [];
|
|
350
354
|
return this.runtimeSnapshot?.localRoutines ?? [];
|
|
351
355
|
}
|
|
352
356
|
|
|
@@ -564,7 +568,14 @@ export class AgentWorkspace {
|
|
|
564
568
|
this.submitLocalDeleteEditor(shellPaths, editor);
|
|
565
569
|
return;
|
|
566
570
|
}
|
|
567
|
-
if (editor.kind === '
|
|
571
|
+
if (editor.kind === 'profile') {
|
|
572
|
+
const template = this.editorField('template');
|
|
573
|
+
const templateId = template && template.toLowerCase() !== 'none' ? template : undefined;
|
|
574
|
+
const profile = createAgentRuntimeProfile(shellPaths.homeDirectory, this.editorField('name'), {
|
|
575
|
+
...(templateId ? { templateId } : {}),
|
|
576
|
+
});
|
|
577
|
+
this.finishProfileEditor(profile);
|
|
578
|
+
} else if (editor.kind === 'persona') {
|
|
568
579
|
const registry = AgentPersonaRegistry.fromShellPaths(shellPaths);
|
|
569
580
|
if (editor.mode === 'update' && editor.recordId) {
|
|
570
581
|
const wasActive = registry.snapshot().activePersonaId === editor.recordId;
|
|
@@ -699,6 +710,26 @@ export class AgentWorkspace {
|
|
|
699
710
|
this.clampSelection();
|
|
700
711
|
}
|
|
701
712
|
|
|
713
|
+
private finishProfileEditor(profile: AgentRuntimeProfileInfo): void {
|
|
714
|
+
this.localEditor = null;
|
|
715
|
+
const categoryIndex = this.categories.findIndex((category) => category.id === 'profiles');
|
|
716
|
+
if (categoryIndex >= 0) {
|
|
717
|
+
this.selectedCategoryIndex = categoryIndex;
|
|
718
|
+
this.selectedActionIndex = this.categories[categoryIndex]?.actions.findIndex((action) => action.id === 'runtime-profile-create') ?? 0;
|
|
719
|
+
if (this.selectedActionIndex < 0) this.selectedActionIndex = 0;
|
|
720
|
+
}
|
|
721
|
+
this.runtimeSnapshot = this.context ? buildAgentWorkspaceRuntimeSnapshot(this.context) : this.runtimeSnapshot;
|
|
722
|
+
const starter = profile.starterTemplateId ? ` from ${profile.starterTemplateId}` : '';
|
|
723
|
+
this.status = `Created Agent profile: ${profile.id}.`;
|
|
724
|
+
this.lastActionResult = {
|
|
725
|
+
kind: 'refreshed',
|
|
726
|
+
title: 'Created Agent profile',
|
|
727
|
+
detail: `Created isolated Agent profile ${profile.id}${starter}. Launch it with goodvibes-agent --agent-profile ${profile.id}. The current TUI session was not switched.`,
|
|
728
|
+
safety: 'safe',
|
|
729
|
+
};
|
|
730
|
+
this.clampSelection();
|
|
731
|
+
}
|
|
732
|
+
|
|
702
733
|
private finishLocalDelete(kind: AgentWorkspaceLocalEditorKind, id: string, name: string): void {
|
|
703
734
|
this.localEditor = null;
|
|
704
735
|
const categoryId = editorCategoryId(kind);
|
|
@@ -38,6 +38,16 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
|
|
|
38
38
|
|
|
39
39
|
setSecret('OPENAI_API_KEY', controller.getStringFieldValue('providers.openai-api-key', ''));
|
|
40
40
|
|
|
41
|
+
const profileName = controller.getStringFieldValue('agent-setup.profile-name', '').trim();
|
|
42
|
+
if (profileName.length > 0) {
|
|
43
|
+
const selectedTemplate = controller.getStringFieldValue('agent-setup.profile-template', 'none').trim();
|
|
44
|
+
operations.push({
|
|
45
|
+
kind: 'create-agent-profile',
|
|
46
|
+
name: profileName,
|
|
47
|
+
...(selectedTemplate.length > 0 && selectedTemplate !== 'none' ? { templateId: selectedTemplate } : {}),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
return {
|
|
42
52
|
mode: controller.mode,
|
|
43
53
|
source: 'wizard',
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { REASONING_OPTIONS, HITL_MODE_OPTIONS, GUIDANCE_MODE_OPTIONS, PERMISSION_MODE_OPTIONS, SECRET_POLICY_OPTIONS } from './onboarding-wizard-constants.ts';
|
|
2
2
|
import { modelSelectionLabel, normalizeText } from './onboarding-wizard-helpers.ts';
|
|
3
|
+
import { listAgentRuntimeProfileTemplates } from '../../agent/runtime-profile.ts';
|
|
3
4
|
import type { OnboardingWizardController } from './onboarding-wizard.ts';
|
|
4
|
-
import type { OnboardingWizardActionFieldDefinition, OnboardingWizardFieldDefinition, OnboardingWizardModelPickerFieldDefinition, OnboardingWizardRadioFieldDefinition, OnboardingWizardStepDefinition } from './onboarding-wizard-types.ts';
|
|
5
|
+
import type { OnboardingWizardActionFieldDefinition, OnboardingWizardFieldDefinition, OnboardingWizardModelPickerFieldDefinition, OnboardingWizardRadioFieldDefinition, OnboardingWizardRadioOption, OnboardingWizardStepDefinition } from './onboarding-wizard-types.ts';
|
|
6
|
+
|
|
7
|
+
function buildStarterTemplateOptions(): readonly OnboardingWizardRadioOption[] {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
id: 'none',
|
|
11
|
+
label: 'No profile',
|
|
12
|
+
hint: 'Keep using the current Agent home. You can create isolated profiles later from the Agent workspace.',
|
|
13
|
+
},
|
|
14
|
+
...listAgentRuntimeProfileTemplates().map((template): OnboardingWizardRadioOption => ({
|
|
15
|
+
id: template.id,
|
|
16
|
+
label: template.name,
|
|
17
|
+
hint: `${template.description} Includes persona ${template.personaName}, skills ${template.skillNames.join(', ')}, and routines ${template.routineNames.join(', ')}.`,
|
|
18
|
+
})),
|
|
19
|
+
];
|
|
20
|
+
}
|
|
5
21
|
|
|
6
22
|
export function buildOnboardingWizardSteps(controller: OnboardingWizardController): readonly OnboardingWizardStepDefinition[] {
|
|
7
23
|
if (controller.hydrationPending || controller.hydrationError !== null) return [buildLoadingStep(controller)];
|
|
@@ -169,6 +185,7 @@ export function buildAgentSetupStep(controller: OnboardingWizardController): Onb
|
|
|
169
185
|
summaryTitle: 'Agent setup posture',
|
|
170
186
|
summaryLines: [
|
|
171
187
|
'Agent owns the operator TUI and local behavior registry.',
|
|
188
|
+
'Optional starter profile: create an isolated Agent home from setup.',
|
|
172
189
|
'GoodVibes runtime lifecycle is external to this product.',
|
|
173
190
|
`Secret policy: ${controller.getStringFieldValue('agent-setup.secret-policy', secretPolicy)}`,
|
|
174
191
|
collectionIssues > 0 ? `${collectionIssues} setup snapshot issue(s)` : 'Setup snapshot collected cleanly',
|
|
@@ -198,12 +215,28 @@ export function buildAgentSetupStep(controller: OnboardingWizardController): Onb
|
|
|
198
215
|
options: SECRET_POLICY_OPTIONS,
|
|
199
216
|
defaultValue: secretPolicy,
|
|
200
217
|
},
|
|
218
|
+
{
|
|
219
|
+
kind: 'text',
|
|
220
|
+
id: 'agent-setup.profile-name',
|
|
221
|
+
label: 'Create starter profile',
|
|
222
|
+
hint: 'Optional: enter a new Agent profile name to create an isolated home during setup. Leave blank to keep the current home.',
|
|
223
|
+
placeholder: 'research-desk',
|
|
224
|
+
defaultValue: '',
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
kind: 'radio',
|
|
228
|
+
id: 'agent-setup.profile-template',
|
|
229
|
+
label: 'Starter profile template',
|
|
230
|
+
hint: 'Choose the persona, skills, and routine bundle to seed into the optional new Agent profile.',
|
|
231
|
+
options: buildStarterTemplateOptions(),
|
|
232
|
+
defaultValue: 'none',
|
|
233
|
+
},
|
|
201
234
|
{
|
|
202
235
|
kind: 'status',
|
|
203
236
|
id: 'agent-setup.profile-guide',
|
|
204
|
-
label: '
|
|
205
|
-
hint: 'Use /agent-profile guide after setup to
|
|
206
|
-
defaultValue: '
|
|
237
|
+
label: 'Profile guidance',
|
|
238
|
+
hint: 'Use /agent-profile guide after setup to export, customize, import, and launch Agent profiles.',
|
|
239
|
+
defaultValue: 'Available',
|
|
207
240
|
},
|
|
208
241
|
],
|
|
209
242
|
};
|
|
@@ -142,6 +142,47 @@ function localLibraryLines(
|
|
|
142
142
|
return lines;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
function profileLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
|
|
146
|
+
const lines: ContextLine[] = [
|
|
147
|
+
{ text: 'Agent Profiles', fg: PALETTE.title, bold: true },
|
|
148
|
+
];
|
|
149
|
+
if (snapshot.runtimeProfiles.length === 0) {
|
|
150
|
+
lines.push({ text: 'No isolated Agent profiles yet. Use Create Agent profile in this workspace.', fg: PALETTE.warn });
|
|
151
|
+
return lines;
|
|
152
|
+
}
|
|
153
|
+
for (const profile of snapshot.runtimeProfiles.slice(0, 6)) {
|
|
154
|
+
const starter = profile.starterTemplateId ? ` starter=${profile.starterTemplateId}` : ' starter=none';
|
|
155
|
+
const created = profile.createdAt ? ` created=${profile.createdAt.slice(0, 10)}` : '';
|
|
156
|
+
lines.push({ text: `${profile.id}${starter}${created}`, fg: PALETTE.info, bold: profile.id === snapshot.activeRuntimeProfile });
|
|
157
|
+
lines.push({ text: ` home: ${profile.homeDirectory}`, fg: PALETTE.muted });
|
|
158
|
+
}
|
|
159
|
+
if (snapshot.runtimeProfiles.length > 6) {
|
|
160
|
+
lines.push({ text: `${snapshot.runtimeProfiles.length - 6} more profile(s).`, fg: PALETTE.dim });
|
|
161
|
+
}
|
|
162
|
+
return lines;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function starterTemplateLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
|
|
166
|
+
const lines: ContextLine[] = [
|
|
167
|
+
{ text: 'Starter Templates', fg: PALETTE.title, bold: true },
|
|
168
|
+
];
|
|
169
|
+
for (const template of snapshot.runtimeStarterTemplates.slice(0, 6)) {
|
|
170
|
+
lines.push({
|
|
171
|
+
text: `${template.id}: ${template.name} [${template.source}]`,
|
|
172
|
+
fg: template.source === 'local' ? PALETTE.good : PALETTE.info,
|
|
173
|
+
bold: template.id === 'research',
|
|
174
|
+
});
|
|
175
|
+
lines.push({
|
|
176
|
+
text: ` ${template.description} Persona ${template.personaName}; skills ${template.skillNames.join(', ')}; routines ${template.routineNames.join(', ')}.`,
|
|
177
|
+
fg: PALETTE.muted,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (snapshot.runtimeStarterTemplates.length > 6) {
|
|
181
|
+
lines.push({ text: `${snapshot.runtimeStarterTemplates.length - 6} more starter template(s).`, fg: PALETTE.dim });
|
|
182
|
+
}
|
|
183
|
+
return lines;
|
|
184
|
+
}
|
|
185
|
+
|
|
145
186
|
function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCategory, snapshot: AgentWorkspaceRuntimeSnapshot | null): ContextLine[] {
|
|
146
187
|
if (!snapshot) return [{ text: 'Runtime context is not loaded yet.', fg: PALETTE.warn }];
|
|
147
188
|
const base: ContextLine[] = [{ text: 'Live Agent Context', fg: PALETTE.title, bold: true }];
|
|
@@ -206,8 +247,14 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
206
247
|
{ text: `Agent profile root: ${snapshot.runtimeProfileRoot}`, fg: PALETTE.muted },
|
|
207
248
|
{ text: `Starter templates: ${snapshot.runtimeStarterTemplateCount}; local custom: ${snapshot.localStarterTemplateCount}`, fg: PALETTE.info },
|
|
208
249
|
{ text: `Config profiles: ${snapshot.configProfileCount}`, fg: PALETTE.info },
|
|
250
|
+
{ text: `Starter ids: ${snapshot.runtimeStarterTemplates.map((template) => template.id).join(', ') || 'none'}`, fg: PALETTE.info },
|
|
251
|
+
{ text: '' },
|
|
252
|
+
...profileLines(snapshot),
|
|
253
|
+
{ text: '' },
|
|
254
|
+
...starterTemplateLines(snapshot),
|
|
255
|
+
{ text: '' },
|
|
209
256
|
{ text: 'Named Agent profiles isolate local config, sessions, memory, personas, skills, routines, setup, and bundles.', fg: PALETTE.good },
|
|
210
|
-
{ text: 'Starter authoring: browse, export, edit, import, and create Agent profiles from inside this workspace
|
|
257
|
+
{ text: 'Starter authoring: browse, export, edit, import, and create Agent profiles from inside this workspace.', fg: PALETTE.info },
|
|
211
258
|
{ text: 'The external GoodVibes runtime remains shared unless the owning host is configured separately.', fg: PALETTE.warn },
|
|
212
259
|
{ text: 'Portable bundles require explicit export/import commands with real paths and --yes.', fg: PALETTE.muted },
|
|
213
260
|
);
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { isSecretRefInput } from '@pellux/goodvibes-sdk/platform/config';
|
|
4
4
|
import { CONFIG_SCHEMA, DEFAULT_CONFIG } from '../../config/index.ts';
|
|
5
|
+
import {
|
|
6
|
+
createAgentRuntimeProfile,
|
|
7
|
+
getAgentRuntimeProfileTemplate,
|
|
8
|
+
resolveAgentRuntimeProfileHome,
|
|
9
|
+
} from '../../agent/runtime-profile.ts';
|
|
5
10
|
import type { FeatureFlagConfigKey } from '../surface-feature-flags.ts';
|
|
6
11
|
import {
|
|
7
12
|
getOnboardingRuntimeStatePath,
|
|
@@ -250,6 +255,22 @@ function validateAcknowledgementOperation(
|
|
|
250
255
|
}
|
|
251
256
|
}
|
|
252
257
|
|
|
258
|
+
function validateCreateAgentProfileOperation(
|
|
259
|
+
deps: OnboardingApplyDependencies,
|
|
260
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-agent-profile' }>,
|
|
261
|
+
): void {
|
|
262
|
+
const name = operation.name.trim();
|
|
263
|
+
if (name.length === 0) throw new Error('Agent profile name is required.');
|
|
264
|
+
|
|
265
|
+
const resolution = resolveAgentRuntimeProfileHome(deps.shellPaths.homeDirectory, name);
|
|
266
|
+
if (existsSync(resolution.homeDirectory)) {
|
|
267
|
+
throw new Error(`Agent profile already exists: ${resolution.id}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const templateId = operation.templateId?.trim();
|
|
271
|
+
if (templateId) getAgentRuntimeProfileTemplate(templateId, deps.shellPaths.homeDirectory);
|
|
272
|
+
}
|
|
273
|
+
|
|
253
274
|
function applyConfigOperation(
|
|
254
275
|
deps: OnboardingApplyDependencies,
|
|
255
276
|
operation: Extract<OnboardingApplyOperation, { kind: 'set-config' }>,
|
|
@@ -441,6 +462,13 @@ async function buildRollbackAction(
|
|
|
441
462
|
);
|
|
442
463
|
}
|
|
443
464
|
|
|
465
|
+
if (operation.kind === 'create-agent-profile') {
|
|
466
|
+
const resolution = resolveAgentRuntimeProfileHome(deps.shellPaths.homeDirectory, operation.name);
|
|
467
|
+
return () => {
|
|
468
|
+
if (existsSync(resolution.homeDirectory)) rmSync(resolution.homeDirectory, { recursive: true, force: true });
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
444
472
|
const neverOperation: never = operation;
|
|
445
473
|
throw new Error(`Unsupported onboarding operation: ${JSON.stringify(neverOperation)}`);
|
|
446
474
|
}
|
|
@@ -465,6 +493,24 @@ function applyAcknowledgementOperation(
|
|
|
465
493
|
};
|
|
466
494
|
}
|
|
467
495
|
|
|
496
|
+
function applyCreateAgentProfileOperation(
|
|
497
|
+
deps: OnboardingApplyDependencies,
|
|
498
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-agent-profile' }>,
|
|
499
|
+
): OnboardingAppliedOperation {
|
|
500
|
+
validateCreateAgentProfileOperation(deps, operation);
|
|
501
|
+
const templateId = operation.templateId?.trim();
|
|
502
|
+
const profile = createAgentRuntimeProfile(deps.shellPaths.homeDirectory, operation.name, {
|
|
503
|
+
...(templateId ? { templateId } : {}),
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
return {
|
|
507
|
+
kind: operation.kind,
|
|
508
|
+
summary: profile.starterTemplateId
|
|
509
|
+
? `Created Agent profile ${profile.id} from ${profile.starterTemplateId}.`
|
|
510
|
+
: `Created Agent profile ${profile.id}.`,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
468
514
|
function orderApplyOperations(
|
|
469
515
|
operations: readonly OnboardingApplyOperation[],
|
|
470
516
|
): readonly OnboardingApplyOperation[] {
|
|
@@ -476,6 +522,7 @@ function orderApplyOperations(
|
|
|
476
522
|
const configOperations = operations.filter((operation) => (
|
|
477
523
|
operation.kind === 'set-config' && operation.key !== 'storage.secretPolicy'
|
|
478
524
|
));
|
|
525
|
+
const agentProfileOperations = operations.filter((operation) => operation.kind === 'create-agent-profile');
|
|
479
526
|
const finalOperations = operations.filter((operation) => (
|
|
480
527
|
operation.kind === 'acknowledge'
|
|
481
528
|
));
|
|
@@ -485,6 +532,7 @@ function orderApplyOperations(
|
|
|
485
532
|
...authOperations,
|
|
486
533
|
...secretOperations,
|
|
487
534
|
...configOperations,
|
|
535
|
+
...agentProfileOperations,
|
|
488
536
|
...finalOperations,
|
|
489
537
|
];
|
|
490
538
|
}
|
|
@@ -521,6 +569,11 @@ function prevalidateApplyRequest(
|
|
|
521
569
|
continue;
|
|
522
570
|
}
|
|
523
571
|
|
|
572
|
+
if (operation.kind === 'create-agent-profile') {
|
|
573
|
+
validateCreateAgentProfileOperation(deps, operation);
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
|
|
524
577
|
const neverOperation: never = operation;
|
|
525
578
|
throw new Error(`Unsupported onboarding operation: ${JSON.stringify(neverOperation)}`);
|
|
526
579
|
} catch (error) {
|
|
@@ -539,6 +592,7 @@ function getVerificationFailureKind(itemId: string): OnboardingApplyOperation['k
|
|
|
539
592
|
if (itemId.startsWith('secret:')) return 'set-secret';
|
|
540
593
|
if (itemId.startsWith('auth:')) return 'ensure-auth-user';
|
|
541
594
|
if (itemId.startsWith('acknowledge:')) return 'acknowledge';
|
|
595
|
+
if (itemId.startsWith('agent-profile:')) return 'create-agent-profile';
|
|
542
596
|
return 'set-config';
|
|
543
597
|
}
|
|
544
598
|
|
|
@@ -590,6 +644,12 @@ export async function applyOnboardingRequest(
|
|
|
590
644
|
continue;
|
|
591
645
|
}
|
|
592
646
|
|
|
647
|
+
if (operation.kind === 'create-agent-profile') {
|
|
648
|
+
applied.push(applyCreateAgentProfileOperation(deps, operation));
|
|
649
|
+
rollbacks.push(rollback);
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
|
|
593
653
|
const neverOperation: never = operation;
|
|
594
654
|
throw new Error(`Unsupported onboarding operation: ${JSON.stringify(neverOperation)}`);
|
|
595
655
|
} catch (error) {
|
|
@@ -264,6 +264,11 @@ export type OnboardingApplyOperation =
|
|
|
264
264
|
readonly kind: 'acknowledge';
|
|
265
265
|
readonly target: OnboardingAcknowledgementTarget;
|
|
266
266
|
readonly acknowledged: boolean;
|
|
267
|
+
}
|
|
268
|
+
| {
|
|
269
|
+
readonly kind: 'create-agent-profile';
|
|
270
|
+
readonly name: string;
|
|
271
|
+
readonly templateId?: string;
|
|
267
272
|
};
|
|
268
273
|
|
|
269
274
|
export interface OnboardingApplyRequest {
|
|
@@ -358,7 +363,7 @@ export interface OnboardingProviderAccountReadHelper {
|
|
|
358
363
|
|
|
359
364
|
export type OnboardingShellPaths = Pick<
|
|
360
365
|
ShellPathService,
|
|
361
|
-
'workingDirectory' | 'resolveProjectPath' | 'resolveUserPath'
|
|
366
|
+
'homeDirectory' | 'workingDirectory' | 'resolveProjectPath' | 'resolveUserPath'
|
|
362
367
|
>;
|
|
363
368
|
|
|
364
369
|
export interface OnboardingSnapshotDependencies {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { isSecretRefInput } from '@pellux/goodvibes-sdk/platform/config';
|
|
4
|
+
import { resolveAgentRuntimeProfileHome } from '../../agent/runtime-profile.ts';
|
|
2
5
|
import { readOnboardingRuntimeState } from './state.ts';
|
|
3
6
|
import type {
|
|
4
7
|
OnboardingApplyOperation,
|
|
@@ -147,6 +150,22 @@ function verifyAuthOperation(
|
|
|
147
150
|
};
|
|
148
151
|
}
|
|
149
152
|
|
|
153
|
+
function verifyCreateAgentProfileOperation(
|
|
154
|
+
deps: OnboardingVerificationDependencies,
|
|
155
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-agent-profile' }>,
|
|
156
|
+
): OnboardingVerificationItem {
|
|
157
|
+
const resolution = resolveAgentRuntimeProfileHome(deps.shellPaths.homeDirectory, operation.name);
|
|
158
|
+
const ok = existsSync(join(resolution.homeDirectory, 'profile.json'));
|
|
159
|
+
return {
|
|
160
|
+
id: `agent-profile:${resolution.id}`,
|
|
161
|
+
status: ok ? 'pass' : 'fail',
|
|
162
|
+
message: ok
|
|
163
|
+
? `${resolution.id} Agent profile exists.`
|
|
164
|
+
: `${resolution.id} Agent profile was not created.`,
|
|
165
|
+
target: resolution.id,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
150
169
|
async function verifyOperation(
|
|
151
170
|
deps: OnboardingVerificationDependencies,
|
|
152
171
|
operation: OnboardingApplyOperation,
|
|
@@ -154,7 +173,8 @@ async function verifyOperation(
|
|
|
154
173
|
if (operation.kind === 'set-config') return verifyConfigOperation(deps, operation);
|
|
155
174
|
if (operation.kind === 'set-secret') return verifySecretOperation(deps, operation);
|
|
156
175
|
if (operation.kind === 'ensure-auth-user') return verifyAuthOperation(deps, operation);
|
|
157
|
-
return verifyAcknowledgementOperation(deps, operation);
|
|
176
|
+
if (operation.kind === 'acknowledge') return verifyAcknowledgementOperation(deps, operation);
|
|
177
|
+
return verifyCreateAgentProfileOperation(deps, operation);
|
|
158
178
|
}
|
|
159
179
|
|
|
160
180
|
export async function verifyOnboardingRequest(
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.1.
|
|
9
|
+
let _version = '0.1.77';
|
|
10
10
|
let _sdkVersion = '0.33.35';
|
|
11
11
|
try {
|
|
12
12
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|