@pellux/goodvibes-agent 0.1.75 → 0.1.76

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 CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to GoodVibes Agent will be recorded here.
4
4
 
5
+ ## 0.1.76 - 2026-05-31
6
+
7
+ - Added optional starter profile creation to first-run onboarding so setup can create an isolated Agent home seeded with persona, skill, and routine templates.
8
+ - Added onboarding apply/verify support for Agent profile creation with prevalidation, rollback, and overwrite protection.
9
+ - Updated onboarding and renderer regressions so the first-run flow stays Agent-specific while exposing the starter profile path directly in the TUI.
10
+
5
11
  ## 0.1.75 - 2026-05-31
6
12
 
7
13
  - 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.75",
3
+ "version": "0.1.76",
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",
@@ -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: 'Agent profiles',
205
- hint: 'Use /agent-profile guide after setup to create household, research, travel, operations, or custom Agent profiles.',
206
- defaultValue: 'Profiles',
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
  };
@@ -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.75';
9
+ let _version = '0.1.76';
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 {