@pellux/goodvibes-agent 0.1.55 → 0.1.57

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.
Files changed (94) hide show
  1. package/.goodvibes/GOODVIBES.md +1 -1
  2. package/CHANGELOG.md +19 -8
  3. package/README.md +10 -3
  4. package/docs/README.md +1 -1
  5. package/docs/getting-started.md +10 -3
  6. package/docs/release-and-publishing.md +12 -3
  7. package/package.json +1 -3
  8. package/scripts/check-bun.sh +5 -1
  9. package/src/agent/routine-schedule-args.ts +219 -0
  10. package/src/agent/routine-schedule-format.ts +173 -0
  11. package/src/agent/routine-schedule-promotion.ts +3 -811
  12. package/src/agent/routine-schedule-receipts.ts +502 -0
  13. package/src/cli/agent-knowledge-command.ts +6 -6
  14. package/src/cli/help.ts +3 -25
  15. package/src/cli/package-verification.ts +23 -16
  16. package/src/cli/redaction.ts +4 -1
  17. package/src/cli/routines-command.ts +10 -6
  18. package/src/cli/service-posture.ts +47 -280
  19. package/src/cli/status.ts +0 -1
  20. package/src/config/secret-config.ts +0 -2
  21. package/src/input/agent-workspace-categories.ts +219 -0
  22. package/src/input/agent-workspace-editors.ts +143 -0
  23. package/src/input/agent-workspace-snapshot.ts +265 -0
  24. package/src/input/agent-workspace-types.ts +142 -0
  25. package/src/input/agent-workspace.ts +22 -766
  26. package/src/input/commands/agent-runtime-profile-runtime.ts +1 -1
  27. package/src/input/commands/delegation-runtime.ts +1 -1
  28. package/src/input/commands/experience-runtime.ts +3 -4
  29. package/src/input/commands/guidance-runtime.ts +1 -2
  30. package/src/input/commands/health-runtime.ts +3 -65
  31. package/src/input/commands/knowledge.ts +7 -7
  32. package/src/input/commands/local-setup-review.ts +0 -61
  33. package/src/input/commands/local-setup-transfer.ts +0 -3
  34. package/src/input/commands/local-setup.ts +2 -15
  35. package/src/input/commands/planning-runtime.ts +4 -1
  36. package/src/input/commands/platform-access-runtime.ts +1 -10
  37. package/src/input/commands/platform-services-runtime.ts +0 -1
  38. package/src/input/commands/recall-query.ts +1 -1
  39. package/src/input/commands/routines-runtime.ts +10 -6
  40. package/src/input/commands/schedule-runtime.ts +10 -6
  41. package/src/input/commands/session-workflow.ts +1 -1
  42. package/src/input/commands/tasks-runtime.ts +1 -14
  43. package/src/input/commands.ts +0 -4
  44. package/src/input/handler-onboarding.ts +10 -120
  45. package/src/input/onboarding/onboarding-wizard-apply.ts +5 -196
  46. package/src/input/onboarding/onboarding-wizard-constants.ts +8 -119
  47. package/src/input/onboarding/onboarding-wizard-helpers.ts +2 -53
  48. package/src/input/onboarding/onboarding-wizard-rules.ts +2 -236
  49. package/src/input/onboarding/onboarding-wizard-state.ts +1 -69
  50. package/src/input/onboarding/onboarding-wizard-steps.ts +584 -737
  51. package/src/input/onboarding/onboarding-wizard-types.ts +8 -26
  52. package/src/input/onboarding/onboarding-wizard.ts +4 -109
  53. package/src/input/settings-modal-agent-policy.ts +10 -0
  54. package/src/input/settings-modal-types.ts +2 -4
  55. package/src/input/settings-modal.ts +3 -1
  56. package/src/input/submission-router.ts +0 -1
  57. package/src/panels/approval-panel.ts +1 -2
  58. package/src/panels/builtin/operations.ts +1 -2
  59. package/src/panels/knowledge-panel.ts +2 -2
  60. package/src/panels/project-planning-panel.ts +4 -1
  61. package/src/panels/provider-health-domains.ts +0 -22
  62. package/src/panels/provider-health-panel.ts +1 -5
  63. package/src/panels/session-browser-panel.ts +0 -5
  64. package/src/panels/tasks-panel.ts +2 -64
  65. package/src/renderer/agent-workspace.ts +1 -1
  66. package/src/renderer/help-overlay.ts +1 -2
  67. package/src/renderer/semantic-diff.ts +1 -1
  68. package/src/renderer/settings-modal-helpers.ts +0 -16
  69. package/src/renderer/settings-modal.ts +3 -5
  70. package/src/runtime/bootstrap-hook-bridge.ts +0 -3
  71. package/src/runtime/bootstrap-shell.ts +2 -1
  72. package/src/runtime/bootstrap.ts +1 -1
  73. package/src/runtime/index.ts +0 -1
  74. package/src/runtime/onboarding/derivation.ts +1 -28
  75. package/src/runtime/onboarding/snapshot.ts +0 -1
  76. package/src/runtime/onboarding/types.ts +1 -4
  77. package/src/runtime/services.ts +4 -23
  78. package/src/runtime/ui-read-models.ts +4 -3
  79. package/src/shell/service-settings-sync.ts +15 -244
  80. package/src/tools/agent-context-policy.ts +1 -1
  81. package/src/tools/wrfc-agent-guard.ts +3 -3
  82. package/src/verification/live-verifier.ts +11 -5
  83. package/src/verification/verification-ledger.ts +3 -6
  84. package/src/version.ts +1 -1
  85. package/src/input/commands/agent-externalized-tui.ts +0 -73
  86. package/src/input/commands/cloudflare-runtime.ts +0 -385
  87. package/src/input/handler-onboarding-cloudflare.ts +0 -322
  88. package/src/input/onboarding/onboarding-runtime-status.ts +0 -87
  89. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +0 -494
  90. package/src/input/onboarding/onboarding-wizard-cloudflare.ts +0 -199
  91. package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +0 -130
  92. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +0 -762
  93. package/src/runtime/cloudflare-control-plane.ts +0 -350
  94. package/src/runtime/sandbox-public-gaps.ts +0 -358
@@ -4,30 +4,15 @@ import { openExternalUrl } from '@pellux/goodvibes-sdk/platform/utils';
4
4
  import { getProviderIdFromModel } from '../config/provider-model.ts';
5
5
  import { buildProviderAccountSnapshot } from '@/runtime/index.ts';
6
6
  import { OnboardingWizardController, type OnboardingWizardAction, type OnboardingWizardApplyFeedback } from './onboarding/onboarding-wizard.ts';
7
- import { handleCloudflareOnboardingActionForHandler, maybeProvisionCloudflareOnFinalApplyForHandler } from './handler-onboarding-cloudflare.ts';
8
7
  import { applyOnboardingRequest, collectOnboardingSnapshot, verifyOnboardingRequest } from '../runtime/onboarding/index.ts';
9
8
  import type { OnboardingApplyRequest, OnboardingVerificationItem } from '../runtime/onboarding/index.ts';
10
9
  import type { ModelPickerTarget } from './model-picker.ts';
11
10
  import { captureOnboardingWizardSnapshot, restoreOnboardingWizardSnapshot } from './handler-ui-state.ts';
12
11
  import type { InputHandler } from './handler.ts';
13
- import {
14
- formatRuntimeActiveSuccessMessage,
15
- getRuntimeEndpointStatus,
16
- isRuntimeEndpointActive,
17
- isRuntimeEndpointOccupyingConfiguredPort,
18
- runtimePortDiagnostic,
19
- type OnboardingExternalServiceState,
20
- type OnboardingRuntimeEndpoint,
21
- } from './onboarding/onboarding-runtime-status.ts';
22
12
 
23
13
  export interface OnboardingRuntimePosture {
24
- readonly serviceEnabled: boolean;
25
- readonly serviceAutostart: boolean;
26
- readonly restartOnFailure: boolean;
27
- readonly expectedDaemon: boolean;
28
- readonly expectedHttpListener: boolean;
29
- readonly serverBacked: boolean;
30
- readonly remoteExposure: boolean;
14
+ readonly externalServiceManaged: true;
15
+ readonly mutationAllowed: false;
31
16
  }
32
17
 
33
18
  function extractAuthorizationCode(input: string): string | null {
@@ -42,16 +27,6 @@ function extractAuthorizationCode(input: string): string | null {
42
27
  }
43
28
  }
44
29
 
45
- function isLoopbackHostValue(value: string | null | undefined): boolean {
46
- const normalized = (value ?? '').trim().toLowerCase();
47
- if (normalized.length === 0) return false;
48
- return normalized === 'localhost'
49
- || normalized === '::1'
50
- || normalized === '[::1]'
51
- || normalized === '0:0:0:0:0:0:0:1'
52
- || /^127(?:\.\d{1,3}){3}$/.test(normalized);
53
- }
54
-
55
30
  function onboardingVerificationStatusRank(item: OnboardingVerificationItem): number {
56
31
  if (item.status === 'fail') return 3;
57
32
  if (item.status === 'warn') return 2;
@@ -87,53 +62,6 @@ function formatOnboardingApplyCompletionMessage(items: readonly OnboardingVerifi
87
62
  ].join('\n');
88
63
  }
89
64
 
90
- function getRuntimeEndpointBinding(
91
- handler: InputHandler,
92
- request: OnboardingApplyRequest,
93
- endpoint: OnboardingRuntimeEndpoint,
94
- ): { readonly label: string; readonly host: string; readonly port: number } {
95
- const hostKey = endpoint === 'daemon' ? 'controlPlane.host' : 'httpListener.host';
96
- const portKey = endpoint === 'daemon' ? 'controlPlane.port' : 'httpListener.port';
97
- const fallbackHost = '127.0.0.1';
98
- const fallbackPort = endpoint === 'daemon' ? 3421 : 3422;
99
- const rawHost = handler.getOnboardingConfigValue(request, hostKey);
100
- const rawPort = handler.getOnboardingConfigValue(request, portKey);
101
- const parsedPort = typeof rawPort === 'number' ? rawPort : Number(rawPort);
102
- return {
103
- label: endpoint === 'daemon' ? 'GoodVibes daemon' : 'HTTP listener',
104
- host: String(rawHost ?? fallbackHost),
105
- port: Number.isFinite(parsedPort) ? parsedPort : fallbackPort,
106
- };
107
- }
108
-
109
- function formatRuntimeActiveFailureMessage(
110
- handler: InputHandler,
111
- request: OnboardingApplyRequest,
112
- endpoint: OnboardingRuntimeEndpoint,
113
- state: OnboardingExternalServiceState | undefined,
114
- ): string {
115
- const binding = getRuntimeEndpointBinding(handler, request, endpoint);
116
- const portInUse = endpoint === 'daemon' ? state?.daemonPortInUse : state?.httpListenerPortInUse;
117
- const status = getRuntimeEndpointStatus(state, endpoint);
118
- const impact = endpoint === 'daemon'
119
- ? 'browser, LAN, and service-backed GoodVibes surfaces may be unavailable until the daemon is running there.'
120
- : 'incoming webhooks and event surfaces will not receive traffic until the listener is running there.';
121
- return `${binding.label} is enabled for ${binding.host}:${binding.port}, but onboarding could not confirm an embedded or verified external service after restart. ${runtimePortDiagnostic(binding, portInUse, status)} Settings were saved; ${impact}`;
122
- }
123
-
124
- function formatRuntimeStoppedFailureMessage(
125
- handler: InputHandler,
126
- request: OnboardingApplyRequest,
127
- endpoint: OnboardingRuntimeEndpoint,
128
- state?: OnboardingExternalServiceState,
129
- ): string {
130
- const binding = getRuntimeEndpointBinding(handler, request, endpoint);
131
- const status = getRuntimeEndpointStatus(state, endpoint);
132
- const disabledSurface = endpoint === 'daemon' ? 'server-backed surfaces' : 'incoming event surfaces';
133
- const statusDetail = status ? ` ${runtimePortDiagnostic(binding, undefined, status)}` : '';
134
- return `${binding.label} was disabled for ${disabledSurface}, but ${binding.host}:${binding.port} is still occupied. Settings were saved; another GoodVibes process or external service may still be running on that port.${statusDetail}`;
135
- }
136
-
137
65
  function showOnboardingApplyFeedbackForHandler(handler: InputHandler, feedback: OnboardingWizardApplyFeedback): void {
138
66
  handler.onboardingWizard.setApplyFeedback(feedback);
139
67
  const reviewIndex = handler.onboardingWizard.steps.findIndex((step) => step.id === 'review');
@@ -241,18 +169,6 @@ export async function handleOnboardingActionForHandler(handler: InputHandler, ac
241
169
  continueOnboardingSection(handler);
242
170
  return;
243
171
  }
244
- if (action.startsWith('cloudflare-')) {
245
- await handleCloudflareOnboardingActionForHandler(handler, action as Extract<OnboardingWizardAction,
246
- | 'cloudflare-token-requirements'
247
- | 'cloudflare-create-operational-token'
248
- | 'cloudflare-discover'
249
- | 'cloudflare-validate'
250
- | 'cloudflare-provision'
251
- | 'cloudflare-verify'
252
- | 'cloudflare-disable'
253
- >);
254
- return;
255
- }
256
172
  if (action !== 'apply') return;
257
173
  if (handler.onboardingApplyPending) return;
258
174
  const blockers = handler.onboardingWizard.getBlockingFieldLabels();
@@ -298,8 +214,6 @@ export async function handleOnboardingActionForHandler(handler: InputHandler, ac
298
214
  ? { ...item, status: 'warn' }
299
215
  : item));
300
216
  verificationItems = dedupeOnboardingVerificationItems([...verificationItems, ...runtimeWarnings]);
301
- const cloudflareItems = await maybeProvisionCloudflareOnFinalApplyForHandler(handler);
302
- verificationItems = dedupeOnboardingVerificationItems([...verificationItems, ...cloudflareItems]);
303
217
  }
304
218
  } catch (error) {
305
219
  showOnboardingApplyFeedbackForHandler(handler, {
@@ -564,44 +478,20 @@ export function getOnboardingConfigValueForHandler(handler: InputHandler, reques
564
478
  }
565
479
 
566
480
  export function getOnboardingRuntimePostureForHandler(handler: InputHandler, request: OnboardingApplyRequest): OnboardingRuntimePosture {
567
- const getConfigValue = (key: string): unknown => handler.getOnboardingConfigValue(request, key);
568
- const serviceEnabled = getConfigValue('service.enabled') === true;
569
- const serviceAutostart = getConfigValue('service.autostart') === true;
570
- const restartOnFailure = getConfigValue('service.restartOnFailure') === true;
571
- const daemonEnabled = getConfigValue('danger.daemon') === true || getConfigValue('controlPlane.enabled') === true;
572
- const listenerEnabled = getConfigValue('danger.httpListener') === true;
573
- const webEnabled = getConfigValue('web.enabled') === true;
574
- const controlPlaneRemote = getConfigValue('controlPlane.hostMode') === 'network'
575
- || (getConfigValue('controlPlane.hostMode') === 'custom'
576
- && !isLoopbackHostValue(String(getConfigValue('controlPlane.host') ?? '')))
577
- || getConfigValue('controlPlane.allowRemote') === true;
578
- const listenerRemote = getConfigValue('httpListener.hostMode') === 'network'
579
- || (getConfigValue('httpListener.hostMode') === 'custom'
580
- && !isLoopbackHostValue(String(getConfigValue('httpListener.host') ?? '')));
581
- const webRemote = getConfigValue('web.hostMode') === 'network'
582
- || (getConfigValue('web.hostMode') === 'custom'
583
- && !isLoopbackHostValue(String(getConfigValue('web.host') ?? '')));
584
- const remoteExposure = controlPlaneRemote || listenerRemote || webRemote;
585
-
586
481
  return {
587
- serviceEnabled,
588
- serviceAutostart,
589
- restartOnFailure,
590
- expectedDaemon: daemonEnabled || webEnabled,
591
- expectedHttpListener: listenerEnabled,
592
- serverBacked: serviceEnabled || daemonEnabled || listenerEnabled || webEnabled,
593
- remoteExposure,
482
+ externalServiceManaged: true,
483
+ mutationAllowed: false,
594
484
  };
595
485
  }
596
486
 
597
487
  export async function restartOnboardingExternalServicesIfNeededForHandler(handler: InputHandler, request: OnboardingApplyRequest): Promise<OnboardingVerificationItem[]> {
598
488
  const externalServices = handler.uiServices.platform.externalServices;
599
489
  const state = externalServices?.inspect();
600
- const daemonStatus = state?.daemonStatus?.reason ?? (state?.daemonRunning ? 'external daemon appears active' : 'external daemon is not verified from this shell');
490
+ const serviceStatus = state?.daemonStatus?.reason ?? (state?.daemonRunning ? 'external GoodVibes service appears active' : 'external GoodVibes service is not verified from this shell');
601
491
  return [{
602
- id: 'runtime:external-daemon-owned',
492
+ id: 'runtime:external-service-owned',
603
493
  status: 'pass',
604
- message: `GoodVibes Agent did not start, stop, or restart daemon/listener services; daemon lifecycle is external. ${daemonStatus}`,
494
+ message: `GoodVibes Agent did not start, stop, restart, or reconfigure service lifecycle. ${serviceStatus}`,
605
495
  target: 'service',
606
496
  }];
607
497
  }
@@ -610,11 +500,11 @@ export function verifyOnboardingRuntimePostureForHandler(handler: InputHandler,
610
500
  const externalServices = handler.uiServices.platform.externalServices;
611
501
  const externalState = externalServices?.inspect();
612
502
  return [{
613
- id: 'runtime:external-daemon-owned',
503
+ id: 'runtime:external-service-owned',
614
504
  status: 'pass',
615
505
  message: externalState
616
- ? 'Daemon/listener lifecycle is externally managed; Agent onboarding did not request service shutdown, startup, restart, or bind changes.'
617
- : 'Daemon/listener lifecycle is externally managed; no local service controller is required for Agent onboarding.',
506
+ ? 'GoodVibes service lifecycle is externally managed; Agent onboarding did not request shutdown, startup, restart, bind, or surface changes.'
507
+ : 'GoodVibes service lifecycle is externally managed; no local service controller is required for Agent onboarding.',
618
508
  target: 'service',
619
509
  }];
620
510
  }
@@ -1,21 +1,5 @@
1
- import type { OnboardingAcknowledgementTarget, OnboardingApplyOperation, OnboardingApplyRequest } from '../../runtime/onboarding/index.ts';
1
+ import type { OnboardingApplyOperation, OnboardingApplyRequest } from '../../runtime/onboarding/index.ts';
2
2
  import { formatProviderModel } from '../../config/provider-model.ts';
3
- import { getServerSurfaceFeatureFlags } from '../../runtime/surface-feature-flags.ts';
4
- import {
5
- buildCloudflareApiTokenRef,
6
- buildCloudflareOperationalTokenRef,
7
- getCloudflareBatchMode,
8
- getCloudflareComponentSelection,
9
- getCloudflareSetupSource,
10
- shouldShowCloudflareStep,
11
- } from './onboarding-wizard-cloudflare.ts';
12
- import {
13
- EXTERNAL_SURFACE_SPECS,
14
- getExternalSurfaceAutoStartDefaultValue,
15
- getExternalSurfaceAutoStartFieldId,
16
- isExternalSurfaceSelectedByDefault,
17
- } from './onboarding-wizard-external-surfaces.ts';
18
- import { buildGoodVibesSecretKey, buildGoodVibesSecretRef, isSecretReferenceValue } from './onboarding-wizard-helpers.ts';
19
3
  import type { OnboardingWizardController } from './onboarding-wizard.ts';
20
4
 
21
5
  export function buildOnboardingApplyRequest(controller: OnboardingWizardController): OnboardingApplyRequest {
@@ -26,16 +10,6 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
26
10
  ): void => {
27
11
  operations.push({ kind: 'set-config', key, value });
28
12
  };
29
- const enableFeatureFlags = (flagIds: readonly string[]): void => {
30
- for (const flagId of flagIds) setConfig(`featureFlags.${flagId}`, 'enabled');
31
- };
32
- const acknowledge = (target: OnboardingAcknowledgementTarget, fieldId: string): void => {
33
- operations.push({
34
- kind: 'acknowledge',
35
- target,
36
- acknowledged: controller.getBooleanFieldValue(fieldId, false),
37
- });
38
- };
39
13
  const setSecret = (key: string, value: string): void => {
40
14
  if (value.length === 0) return;
41
15
  const medium = controller.getSelectedSecretMedium();
@@ -47,36 +21,10 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
47
21
  medium,
48
22
  });
49
23
  };
50
- const setMaskedConfig = (
51
- key: Extract<OnboardingApplyOperation, { kind: 'set-config' }>['key'],
52
- value: string,
53
- ): void => {
54
- if (value.length === 0 || isSecretReferenceValue(value)) {
55
- setConfig(key, value);
56
- return;
57
- }
58
-
59
- const secretKey = buildGoodVibesSecretKey(key);
60
- setSecret(secretKey, value);
61
- setConfig(key, buildGoodVibesSecretRef(secretKey));
62
- };
63
-
64
- const requestedAdminPassword = controller.getStringFieldValue('accounts.admin-password', '');
65
- const shouldEnsureAuthUser = controller.requiresAuthBootstrap() || requestedAdminPassword.length > 0;
66
- if (shouldEnsureAuthUser) {
67
- operations.push({
68
- kind: 'ensure-auth-user',
69
- username: controller.getStringFieldValue('accounts.admin-username', controller.getDefaultAdminUsername()),
70
- password: requestedAdminPassword,
71
- roles: ['admin'],
72
- createSession: true,
73
- retireBootstrapCredential: controller.hasBootstrapCredentialPresent(),
74
- });
75
- }
76
24
 
77
- // GoodVibes Agent consumes an externally managed daemon. Onboarding must not
78
- // enable, disable, bind, expose, start, stop, or restart daemon/listener/web
79
- // lifecycle settings; provider/auth/local Agent state is the owned surface.
25
+ // GoodVibes Agent onboarding owns only Agent-local setup and provider
26
+ // routing. Server lifecycle, non-Agent entrypoints, and non-Agent knowledge
27
+ // segments are managed elsewhere.
80
28
 
81
29
  const defaultModel = controller.modelSelectionState.get('main');
82
30
  if (defaultModel && defaultModel.enabled !== false && defaultModel.providerId.length > 0 && defaultModel.modelId.length > 0) {
@@ -86,152 +34,13 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
86
34
  setConfig('behavior.hitlMode', controller.getStringFieldValue('experience.hitl', controller.runtimeSnapshot?.runtimeDefaults.behavior.hitlMode ?? 'balanced'));
87
35
  setConfig('behavior.guidanceMode', controller.getStringFieldValue('experience.guidance', controller.runtimeSnapshot?.runtimeDefaults.behavior.guidanceMode ?? 'minimal'));
88
36
  setConfig('permissions.mode', controller.getStringFieldValue('experience.permissions', controller.runtimeSnapshot?.runtimeDefaults.permissionsMode ?? 'prompt'));
89
- setConfig('storage.secretPolicy', controller.getStringFieldValue('external-services.secret-policy', controller.runtimeSnapshot?.runtimeDefaults.secretStoragePolicy ?? 'preferred_secure'));
37
+ setConfig('storage.secretPolicy', controller.getStringFieldValue('agent-setup.secret-policy', controller.runtimeSnapshot?.runtimeDefaults.secretStoragePolicy ?? 'preferred_secure'));
90
38
 
91
39
  setSecret('OPENAI_API_KEY', controller.getStringFieldValue('providers.openai-api-key', ''));
92
40
 
93
- if (shouldShowCloudflareStep(controller)) {
94
- addCloudflareOperations(controller, operations, setSecret);
95
- }
96
-
97
- const externalIntegrations = controller.isCapabilitySelected('external-integrations');
98
- const enabledExternalSurfaceIds: string[] = [];
99
- for (const surface of EXTERNAL_SURFACE_SPECS) {
100
- const selected = externalIntegrations
101
- && controller.getBooleanFieldValue(
102
- surface.enabledFieldId,
103
- isExternalSurfaceSelectedByDefault(surface, controller.runtimeSnapshot),
104
- );
105
- const autoStart = selected
106
- && controller.getStringFieldValue(
107
- getExternalSurfaceAutoStartFieldId(surface),
108
- getExternalSurfaceAutoStartDefaultValue(surface, controller.runtimeSnapshot),
109
- ) === 'yes';
110
- setConfig(surface.enabledConfigKey, autoStart);
111
- if (!selected) continue;
112
- enabledExternalSurfaceIds.push(surface.id);
113
-
114
- for (const setupField of surface.fields) {
115
- const fallback = setupField.defaultValue(controller.runtimeSnapshot);
116
- if (setupField.valueType === 'number') {
117
- setConfig(
118
- setupField.configKey,
119
- controller.getNumberFieldValue(
120
- setupField.id,
121
- setupField.defaultNumber ?? (Number.parseInt(fallback, 10) || 0),
122
- setupField.min,
123
- setupField.max,
124
- ),
125
- );
126
- continue;
127
- }
128
-
129
- const value = controller.getStringFieldValue(setupField.id, fallback);
130
- if (setupField.kind === 'masked') setMaskedConfig(setupField.configKey, value);
131
- else setConfig(setupField.configKey, value);
132
- }
133
- }
134
- enableFeatureFlags(getServerSurfaceFeatureFlags({
135
- serverBacked: false,
136
- web: false,
137
- externalSurfaces: enabledExternalSurfaceIds,
138
- }));
139
-
140
- acknowledge('providers', 'providers.reviewed');
141
- acknowledge('subscriptions', 'accounts.subscriptions');
142
- acknowledge('auth', 'accounts.auth');
143
-
144
41
  return {
145
42
  mode: controller.mode,
146
43
  source: 'wizard',
147
44
  operations,
148
45
  };
149
46
  }
150
-
151
- function addCloudflareOperations(
152
- controller: OnboardingWizardController,
153
- operations: OnboardingApplyOperation[],
154
- setSecret: (key: string, value: string) => void,
155
- ): void {
156
- const setConfig = (
157
- key: Extract<OnboardingApplyOperation, { kind: 'set-config' }>['key'],
158
- value: unknown,
159
- ): void => {
160
- operations.push({ kind: 'set-config', key, value });
161
- };
162
-
163
- const config = controller.runtimeSnapshot?.config.cloudflare;
164
- const enabledDefault = controller.isCapabilitySelected('cloudflare-batch') || config?.enabled === true;
165
- const enabled = controller.getBooleanFieldValue('cloudflare.enabled', enabledDefault);
166
- const components = getCloudflareComponentSelection(controller);
167
- const batchMode = enabled ? getCloudflareBatchMode(controller) : 'off';
168
- const setupSource = getCloudflareSetupSource(controller);
169
- const existingApiTokenRef = config?.apiTokenRef ?? '';
170
- let apiTokenRef = existingApiTokenRef;
171
-
172
- if (!enabled) {
173
- setConfig('cloudflare.enabled', false);
174
- setConfig('batch.mode', 'off');
175
- setConfig('batch.queueBackend', 'local');
176
- return;
177
- }
178
-
179
- if (setupSource === 'operational-env') {
180
- apiTokenRef = buildCloudflareApiTokenRef(
181
- controller.getStringFieldValue('cloudflare.operational-env-name', 'CLOUDFLARE_API_TOKEN'),
182
- );
183
- } else if (setupSource === 'operational-token') {
184
- const token = controller.getStringFieldValue('cloudflare.operational-token', '');
185
- if (token.length > 0) {
186
- setSecret('CLOUDFLARE_API_TOKEN', token);
187
- apiTokenRef = buildCloudflareOperationalTokenRef();
188
- }
189
- }
190
-
191
- setConfig('cloudflare.enabled', true);
192
- setConfig('cloudflare.freeTierMode', controller.getStringFieldValue('cloudflare.free-tier-mode', config?.freeTierMode === false ? 'no' : 'yes') === 'yes');
193
- setConfig('cloudflare.accountId', controller.getStringFieldValue('cloudflare.account-id', config?.accountId ?? ''));
194
- setConfig('cloudflare.apiTokenRef', apiTokenRef);
195
- setConfig('cloudflare.zoneId', controller.getStringFieldValue('cloudflare.zone-id', config?.zoneId ?? ''));
196
- setConfig('cloudflare.zoneName', controller.getStringFieldValue('cloudflare.zone-name', config?.zoneName ?? ''));
197
- setConfig('cloudflare.workerName', controller.getStringFieldValue('cloudflare.worker-name', config?.workerName ?? 'goodvibes-batch-worker'));
198
- setConfig('cloudflare.workerSubdomain', controller.getStringFieldValue('cloudflare.worker-subdomain', config?.workerSubdomain ?? ''));
199
- setConfig('cloudflare.workerHostname', controller.getStringFieldValue('cloudflare.worker-hostname', config?.workerHostname ?? ''));
200
- setConfig('cloudflare.workerBaseUrl', controller.getStringFieldValue('cloudflare.worker-base-url', config?.workerBaseUrl ?? ''));
201
- setConfig('cloudflare.daemonBaseUrl', controller.getStringFieldValue('cloudflare.daemon-base-url', config?.daemonBaseUrl ?? ''));
202
- setConfig('cloudflare.daemonHostname', controller.getStringFieldValue('cloudflare.daemon-hostname', config?.daemonHostname ?? ''));
203
- setConfig('cloudflare.workerCron', controller.getStringFieldValue('cloudflare.worker-cron', config?.workerCron ?? '*/5 * * * *'));
204
- setConfig('cloudflare.queueName', controller.getStringFieldValue('cloudflare.queue-name', config?.queueName ?? 'goodvibes-batch'));
205
- setConfig('cloudflare.deadLetterQueueName', controller.getStringFieldValue('cloudflare.dead-letter-queue-name', config?.deadLetterQueueName ?? 'goodvibes-batch-dlq'));
206
- setConfig('cloudflare.tunnelName', controller.getStringFieldValue('cloudflare.tunnel-name', config?.tunnelName ?? 'goodvibes-agent-daemon'));
207
- setConfig('cloudflare.tunnelId', controller.getStringFieldValue('cloudflare.tunnel-id', config?.tunnelId ?? ''));
208
- setConfig('cloudflare.tunnelTokenRef', controller.getStringFieldValue('cloudflare.tunnel-token-ref', config?.tunnelTokenRef ?? ''));
209
- setConfig('cloudflare.accessAppId', controller.getStringFieldValue('cloudflare.access-app-id', config?.accessAppId ?? ''));
210
- setConfig('cloudflare.accessServiceTokenId', controller.getStringFieldValue('cloudflare.access-service-token-id', config?.accessServiceTokenId ?? ''));
211
- setConfig('cloudflare.accessServiceTokenRef', controller.getStringFieldValue('cloudflare.access-service-token-ref', config?.accessServiceTokenRef ?? ''));
212
- setConfig('cloudflare.kvNamespaceName', controller.getStringFieldValue('cloudflare.kv-namespace-name', config?.kvNamespaceName ?? 'goodvibes-runtime'));
213
- setConfig('cloudflare.kvNamespaceId', controller.getStringFieldValue('cloudflare.kv-namespace-id', config?.kvNamespaceId ?? ''));
214
- setConfig('cloudflare.durableObjectNamespaceName', controller.getStringFieldValue('cloudflare.do-namespace-name', config?.durableObjectNamespaceName ?? 'GoodVibesCoordinator'));
215
- setConfig('cloudflare.durableObjectNamespaceId', controller.getStringFieldValue('cloudflare.do-namespace-id', config?.durableObjectNamespaceId ?? ''));
216
- setConfig('cloudflare.r2BucketName', controller.getStringFieldValue('cloudflare.r2-bucket-name', config?.r2BucketName ?? 'goodvibes-artifacts'));
217
- setConfig('cloudflare.secretsStoreName', controller.getStringFieldValue('cloudflare.secrets-store-name', config?.secretsStoreName ?? 'goodvibes'));
218
- setConfig('cloudflare.secretsStoreId', controller.getStringFieldValue('cloudflare.secrets-store-id', config?.secretsStoreId ?? ''));
219
- setConfig('cloudflare.maxQueueOpsPerDay', controller.getNumberFieldValue('cloudflare.max-queue-ops-per-day', config?.maxQueueOpsPerDay ?? 10000, 1));
220
- setConfig('batch.mode', batchMode);
221
- setConfig('batch.queueBackend', batchMode !== 'off' && components.queues ? 'cloudflare' : 'local');
222
- }
223
-
224
- export function addNetworkOperations(
225
- _controller: OnboardingWizardController,
226
- _operations: OnboardingApplyOperation[],
227
- _customNetwork: boolean,
228
- _enabled: {
229
- readonly controlPlane: boolean;
230
- readonly controlPlaneRemote: boolean;
231
- readonly httpListener: boolean;
232
- readonly web: boolean;
233
- },
234
- ): void {
235
- // Agent onboarding intentionally never mutates daemon/listener/web bind posture.
236
- // Network fields are advisory for the external daemon owner.
237
- }
@@ -1,57 +1,20 @@
1
- import type { OnboardingStep1CapabilityItem } from '../../runtime/onboarding/index.ts';
2
1
  import type { OnboardingWizardRadioOption, OnboardingWizardStepId } from './onboarding-wizard-types.ts';
3
2
 
4
3
  export const STEP_ORDER: readonly OnboardingWizardStepId[] = [
5
- 'capabilities',
6
- 'network',
7
- 'access',
8
- 'external-services',
9
- 'cloudflare',
4
+ 'agent-setup',
10
5
  'provider-access',
11
6
  'default-model',
7
+ 'agent-communication',
8
+ 'agent-tools',
9
+ 'agent-knowledge',
10
+ 'agent-local-state',
11
+ 'agent-automation',
12
+ 'agent-voice-media',
13
+ 'agent-delegation',
12
14
  'experience',
13
15
  'review',
14
16
  ];
15
17
 
16
- export const DEFAULT_CAPABILITIES: readonly OnboardingStep1CapabilityItem[] = [
17
- {
18
- id: 'local-tui-only',
19
- label: 'Agent Local Only (External Daemon)',
20
- selected: true,
21
- detail: 'Use GoodVibes Agent in this terminal and connect only to an externally managed daemon. Agent does not enable service mode, HTTP listeners, external app surfaces, or network setup.',
22
- },
23
- {
24
- id: 'browser-access',
25
- label: 'Open GoodVibes in a Browser',
26
- selected: false,
27
- detail: 'Review browser access requirements for the externally managed daemon. Agent records intent but does not start web services.',
28
- },
29
- {
30
- id: 'network-access',
31
- label: 'Let other devices use GoodVibes',
32
- selected: false,
33
- detail: 'Review external daemon surfaces that are reachable from other devices on your LAN. Local authentication is required.',
34
- },
35
- {
36
- id: 'webhook-events',
37
- label: 'Receive webhooks or events from other tools',
38
- selected: false,
39
- detail: 'Review the external HTTP listener required for incoming webhooks, callbacks, and automation events.',
40
- },
41
- {
42
- id: 'external-integrations',
43
- label: 'Connect GoodVibes to external apps and services',
44
- selected: false,
45
- detail: 'Enable setup screens for Slack, Discord, Telegram, Home Assistant, Teams, Matrix, and other app surfaces you choose.',
46
- },
47
- {
48
- id: 'cloudflare-batch',
49
- label: 'Use Cloudflare for batch or remote daemon work',
50
- selected: false,
51
- detail: 'Optionally configure Cloudflare Workers and Queues for explicit or eligible background batch jobs. The external daemon still owns execution.',
52
- },
53
- ];
54
-
55
18
  export const REASONING_OPTIONS: readonly OnboardingWizardRadioOption[] = [
56
19
  { id: 'instant', label: 'Instant', hint: 'Lowest-latency routing.' },
57
20
  { id: 'low', label: 'Low', hint: 'Compact reasoning for quick work.' },
@@ -59,11 +22,6 @@ export const REASONING_OPTIONS: readonly OnboardingWizardRadioOption[] = [
59
22
  { id: 'high', label: 'High', hint: 'Deeper reasoning for complex tasks.' },
60
23
  ];
61
24
 
62
- export const NETWORK_MODE_OPTIONS: readonly OnboardingWizardRadioOption[] = [
63
- { id: 'local-network-default', label: 'Local Network (Default)', hint: 'Review the default LAN-facing external daemon setup for browser, control-plane, and event features.' },
64
- { id: 'custom', label: 'Custom', hint: 'Review IP addresses and ports for each external daemon surface.' },
65
- ];
66
-
67
25
  export const HITL_MODE_OPTIONS: readonly OnboardingWizardRadioOption[] = [
68
26
  { id: 'quiet', label: 'Quiet', hint: 'Only interrupt for important attention requests.' },
69
27
  { id: 'balanced', label: 'Balanced', hint: 'Show important activity without turning Agent into a log stream.' },
@@ -87,72 +45,3 @@ export const SECRET_POLICY_OPTIONS: readonly OnboardingWizardRadioOption[] = [
87
45
  { id: 'require_secure', label: 'Require secure storage', hint: 'Refuse plaintext secret persistence.' },
88
46
  { id: 'plaintext_allowed', label: 'Allow plaintext storage', hint: 'Permit local plaintext secret storage.' },
89
47
  ];
90
-
91
- export const TELEGRAM_MODE_OPTIONS: readonly OnboardingWizardRadioOption[] = [
92
- { id: 'webhook', label: 'Webhook', hint: 'Receive Telegram updates through a webhook.' },
93
- { id: 'polling', label: 'Polling', hint: 'The external daemon polls Telegram for updates.' },
94
- ];
95
-
96
- export const WHATSAPP_PROVIDER_OPTIONS: readonly OnboardingWizardRadioOption[] = [
97
- { id: 'meta-cloud', label: 'Meta Cloud', hint: 'Use the Meta Cloud API.' },
98
- { id: 'bridge', label: 'Bridge', hint: 'Use a configured bridge service.' },
99
- ];
100
-
101
- export const INBOUND_EXTERNAL_SURFACE_IDS = new Set<string>([
102
- 'bluebubbles',
103
- 'discord',
104
- 'googleChat',
105
- 'homeassistant',
106
- 'imessage',
107
- 'mattermost',
108
- 'matrix',
109
- 'msteams',
110
- 'ntfy',
111
- 'signal',
112
- 'slack',
113
- 'telegram',
114
- 'webhook',
115
- 'whatsapp',
116
- ]);
117
-
118
- export const REQUIRED_EXTERNAL_SETUP_FIELD_IDS = new Set<string>([
119
- 'external-services.slack.bot-token',
120
- 'external-services.slack.signing-secret',
121
- 'external-services.discord.bot-token',
122
- 'external-services.discord.application-id',
123
- 'external-services.discord.public-key',
124
- 'external-services.telegram.bot-token',
125
- 'external-services.telegram.webhook-secret',
126
- 'external-services.webhook.default-target',
127
- 'external-services.homeassistant.instance-url',
128
- 'external-services.homeassistant.access-token',
129
- 'external-services.homeassistant.webhook-secret',
130
- 'external-services.google-chat.webhook-url',
131
- 'external-services.google-chat.verification-token',
132
- 'external-services.signal.bridge-url',
133
- 'external-services.signal.account',
134
- 'external-services.signal.token',
135
- 'external-services.whatsapp.access-token',
136
- 'external-services.whatsapp.verify-token',
137
- 'external-services.whatsapp.phone-number-id',
138
- 'external-services.imessage.bridge-url',
139
- 'external-services.imessage.account',
140
- 'external-services.imessage.token',
141
- 'external-services.msteams.app-id',
142
- 'external-services.msteams.app-password',
143
- 'external-services.msteams.tenant-id',
144
- 'external-services.bluebubbles.server-url',
145
- 'external-services.bluebubbles.password',
146
- 'external-services.mattermost.base-url',
147
- 'external-services.mattermost.bot-token',
148
- 'external-services.matrix.homeserver-url',
149
- 'external-services.matrix.access-token',
150
- 'external-services.matrix.user-id',
151
- ]);
152
-
153
- export const NETWORK_HOST_FIELD_IDS = new Set<string>([
154
- 'network.shared-ip-address',
155
- 'network.service-ip',
156
- 'network.browser-ip',
157
- 'network.webhook-ip',
158
- ]);
@@ -1,7 +1,4 @@
1
- import { isIP } from 'node:net';
2
- import { deriveOnboardingStepState, type OnboardingStep1CapabilityItem, type OnboardingStepDerivationState } from '../../runtime/onboarding/index.ts';
3
- import { DEFAULT_CAPABILITIES } from './onboarding-wizard-constants.ts';
4
- import { EXTERNAL_SURFACE_SPECS, type ExternalSurfaceSetupFieldSpec, type ExternalSurfaceSpec } from './onboarding-wizard-external-surfaces.ts';
1
+ import type { OnboardingStepDerivationState } from '../../runtime/onboarding/index.ts';
5
2
  import type { OnboardingWizardAcknowledgementFieldDefinition, OnboardingWizardModelSelection, OnboardingWizardRuntimeHydration } from './onboarding-wizard-types.ts';
6
3
  export {
7
4
  buildGoodVibesSecretKey,
@@ -15,42 +12,10 @@ export function clamp(value: number, min: number, max: number): number {
15
12
  return Math.max(min, Math.min(max, value));
16
13
  }
17
14
 
18
- export function countSelected(items: readonly OnboardingStep1CapabilityItem[]): number {
19
- return items.filter((item) => item.selected).length;
20
- }
21
-
22
15
  export function normalizeText(value: string | null | undefined): string {
23
16
  return (value ?? '').trim();
24
17
  }
25
18
 
26
- export function isValidHostValue(value: string): boolean {
27
- const normalized = value.trim();
28
- if (normalized.length === 0) return false;
29
- if (/\s/.test(normalized)) return false;
30
- if (/^[a-z][a-z0-9+.-]*:\/\//i.test(normalized)) return false;
31
- if (normalized.includes('/')) return false;
32
- if (normalized.includes(':')) {
33
- const unwrapped = normalized.startsWith('[') && normalized.endsWith(']')
34
- ? normalized.slice(1, -1)
35
- : normalized;
36
- if (isIP(unwrapped) === 0) return false;
37
- }
38
- return true;
39
- }
40
-
41
- export function isLoopbackAddress(value: string): boolean {
42
- const normalized = value.trim().toLowerCase();
43
- return normalized === 'localhost'
44
- || normalized === '::1'
45
- || normalized === '[::1]'
46
- || normalized === '0:0:0:0:0:0:0:1'
47
- || /^127(?:\.\d{1,3}){3}$/.test(normalized);
48
- }
49
-
50
- export function uniqueNonEmpty(values: readonly string[]): readonly string[] {
51
- return [...new Set(values.map((value) => normalizeText(value)).filter((value) => value.length > 0))];
52
- }
53
-
54
19
  export function makeNotNeededAcknowledgement(detail: string): OnboardingWizardAcknowledgementFieldDefinition {
55
20
  return {
56
21
  kind: 'acknowledgement',
@@ -65,7 +30,7 @@ export function makeNotNeededAcknowledgement(detail: string): OnboardingWizardAc
65
30
 
66
31
  export function buildDefaultDerivedState(): OnboardingStepDerivationState {
67
32
  return {
68
- step1Capabilities: DEFAULT_CAPABILITIES,
33
+ step1Capabilities: [],
69
34
  step1_5NetworkMode: 'local-network-default',
70
35
  reopenEditAcknowledgements: {
71
36
  providers: {
@@ -125,21 +90,6 @@ export function modelSelectionLabel(selection: OnboardingWizardModelSelection |
125
90
  return `${provider}/${model}`;
126
91
  }
127
92
 
128
- export function getExternalSurfaceSetupFieldSpec(fieldId: string): ExternalSurfaceSetupFieldSpec | null {
129
- for (const surface of EXTERNAL_SURFACE_SPECS) {
130
- const field = surface.fields.find((entry) => entry.id === fieldId);
131
- if (field) return field;
132
- }
133
- return null;
134
- }
135
-
136
- export function getExternalSurfaceSpecByFieldId(fieldId: string): ExternalSurfaceSpec | null {
137
- for (const surface of EXTERNAL_SURFACE_SPECS) {
138
- if (surface.fields.some((entry) => entry.id === fieldId)) return surface;
139
- }
140
- return null;
141
- }
142
-
143
93
  export function getRuntimeDerivedState(hydration: OnboardingWizardRuntimeHydration): OnboardingStepDerivationState {
144
94
  if (hydration.derived) {
145
95
  const fallback = buildDefaultDerivedState();
@@ -154,7 +104,6 @@ export function getRuntimeDerivedState(hydration: OnboardingWizardRuntimeHydrati
154
104
  };
155
105
  }
156
106
 
157
- if (hydration.snapshot) return deriveOnboardingStepState(hydration.snapshot);
158
107
  return buildDefaultDerivedState();
159
108
  }
160
109