@pellux/goodvibes-agent 0.1.116 → 1.0.0
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 +48 -661
- package/README.md +64 -57
- package/bin/goodvibes-agent.ts +16 -2
- package/dist/package/main.js +199180 -192185
- package/docs/README.md +15 -8
- package/docs/channels-remote-and-api.md +50 -0
- package/docs/connected-host.md +59 -0
- package/docs/getting-started.md +61 -50
- package/docs/knowledge-artifacts-and-multimodal.md +91 -0
- package/docs/project-planning.md +79 -0
- package/docs/providers-and-routing.md +46 -0
- package/docs/release-and-publishing.md +48 -11
- package/docs/tools-and-commands.md +123 -0
- package/docs/voice-and-live-tts.md +51 -0
- package/package.json +3 -6
- package/src/agent/behavior-discovery-summary.ts +1 -3
- package/src/agent/channel-delivery.ts +201 -0
- package/src/agent/media-generation.ts +159 -0
- package/src/agent/memory-prompt.ts +0 -1
- package/src/agent/note-registry.ts +329 -0
- package/src/agent/operator-actions.ts +343 -0
- package/src/agent/persona-discovery.ts +1 -3
- package/src/agent/persona-registry.ts +19 -16
- package/src/agent/record-labels.ts +107 -0
- package/src/agent/reminder-schedule-format.ts +33 -24
- package/src/agent/reminder-schedule.ts +27 -26
- package/src/agent/routine-registry.ts +54 -14
- package/src/agent/routine-schedule-args.ts +2 -1
- package/src/agent/routine-schedule-format.ts +77 -53
- package/src/agent/routine-schedule-promotion.ts +35 -33
- package/src/agent/routine-schedule-receipts.ts +29 -27
- package/src/agent/runtime-profile-starters.ts +5 -5
- package/src/agent/runtime-profile.ts +91 -12
- package/src/agent/skill-registry.ts +210 -32
- package/src/cli/agent-knowledge-args.ts +5 -1
- package/src/cli/agent-knowledge-command.ts +40 -34
- package/src/cli/agent-knowledge-format.ts +80 -67
- package/src/cli/agent-knowledge-methods.ts +1 -1
- package/src/cli/agent-knowledge-runtime.ts +32 -26
- package/src/cli/bundle-command.ts +14 -9
- package/src/cli/config-overrides.ts +37 -36
- package/src/cli/entrypoint.ts +16 -7
- package/src/cli/external-runtime.ts +10 -4
- package/src/cli/help.ts +54 -29
- package/src/cli/local-library-command.ts +203 -83
- package/src/cli/management-commands.ts +105 -68
- package/src/cli/management.ts +55 -74
- package/src/cli/memory-command.ts +66 -32
- package/src/cli/parser.ts +37 -3
- package/src/cli/profiles-command.ts +166 -31
- package/src/cli/provider-auth-routes.ts +2 -1
- package/src/cli/routines-command.ts +176 -40
- package/src/cli/service-posture.ts +16 -16
- package/src/cli/status.ts +56 -135
- package/src/core/conversation-message-snapshot.ts +131 -0
- package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
- package/src/input/agent-workspace-access-command-editors.ts +170 -0
- package/src/input/agent-workspace-activation.ts +35 -8
- package/src/input/agent-workspace-basic-command-editor-submission.ts +351 -94
- package/src/input/agent-workspace-basic-command-editors.ts +502 -9
- package/src/input/agent-workspace-categories.ts +423 -51
- package/src/input/agent-workspace-channel-command-editor-submission.ts +83 -0
- package/src/input/agent-workspace-channel-command-editors.ts +65 -0
- package/src/input/agent-workspace-channels.ts +35 -2
- package/src/input/agent-workspace-command-editor.ts +41 -3
- package/src/input/agent-workspace-config-reader.ts +16 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
- package/src/input/agent-workspace-editors.ts +166 -1
- package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
- package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
- package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
- package/src/input/agent-workspace-learned-behavior.ts +67 -0
- package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
- package/src/input/agent-workspace-library-command-editors.ts +35 -0
- package/src/input/agent-workspace-local-operations.ts +218 -0
- package/src/input/agent-workspace-local-selection.ts +75 -0
- package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
- package/src/input/agent-workspace-media-command-editors.ts +27 -0
- package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
- package/src/input/agent-workspace-memory-command-editors.ts +180 -0
- package/src/input/agent-workspace-memory-editor.ts +2 -2
- package/src/input/agent-workspace-navigation.ts +81 -0
- package/src/input/agent-workspace-notify-editor-submission.ts +121 -0
- package/src/input/agent-workspace-operations-command-editor-submission.ts +208 -0
- package/src/input/agent-workspace-operations-command-editors.ts +196 -0
- package/src/input/agent-workspace-panel-route.ts +43 -0
- package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
- package/src/input/agent-workspace-provider-command-editors.ts +93 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
- package/src/input/agent-workspace-requirements.ts +11 -0
- package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
- package/src/input/agent-workspace-search.ts +169 -0
- package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
- package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
- package/src/input/agent-workspace-session-command-editors.ts +249 -0
- package/src/input/agent-workspace-setup.ts +37 -16
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
- package/src/input/agent-workspace-snapshot.ts +162 -21
- package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
- package/src/input/agent-workspace-task-command-editors.ts +47 -0
- package/src/input/agent-workspace-token.ts +18 -2
- package/src/input/agent-workspace-types.ts +212 -4
- package/src/input/agent-workspace-voice-media.ts +4 -7
- package/src/input/agent-workspace-web-research-editor.ts +104 -0
- package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
- package/src/input/agent-workspace.ts +178 -240
- package/src/input/command-registry.ts +5 -2
- package/src/input/commands/agent-runtime-profile-runtime.ts +126 -37
- package/src/input/commands/agent-skills-runtime.ts +149 -72
- package/src/input/commands/agent-workspace-runtime.ts +23 -6
- package/src/input/commands/brief-runtime.ts +55 -25
- package/src/input/commands/channels-runtime.ts +147 -33
- package/src/input/commands/compat-runtime.ts +32 -0
- package/src/input/commands/delegation-runtime.ts +29 -23
- package/src/input/commands/experience-runtime.ts +6 -4
- package/src/input/commands/guidance-runtime.ts +4 -4
- package/src/input/commands/health-runtime.ts +149 -126
- package/src/input/commands/knowledge.ts +82 -60
- package/src/input/commands/local-provider-runtime.ts +43 -21
- package/src/input/commands/local-runtime.ts +138 -16
- package/src/input/commands/local-setup-review.ts +10 -20
- package/src/input/commands/mcp-runtime.ts +63 -46
- package/src/input/commands/notify-runtime.ts +38 -9
- package/src/input/commands/operator-actions-runtime.ts +138 -0
- package/src/input/commands/operator-runtime.ts +6 -76
- package/src/input/commands/personas-runtime.ts +46 -31
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +35 -98
- package/src/input/commands/product-runtime.ts +2 -2
- package/src/input/commands/provider-accounts-runtime.ts +60 -39
- package/src/input/commands/qrcode-runtime.ts +50 -9
- package/src/input/commands/recall-bundle.ts +11 -11
- package/src/input/commands/recall-capture.ts +13 -11
- package/src/input/commands/recall-query.ts +29 -21
- package/src/input/commands/recall-review.ts +2 -1
- package/src/input/commands/routines-runtime.ts +99 -45
- package/src/input/commands/runtime-services.ts +1 -14
- package/src/input/commands/schedule-runtime.ts +33 -20
- package/src/input/commands/security-runtime.ts +6 -11
- package/src/input/commands/session-content.ts +80 -78
- package/src/input/commands/session-workflow.ts +134 -104
- package/src/input/commands/session.ts +3 -174
- package/src/input/commands/shell-core.ts +38 -17
- package/src/input/commands/subscription-runtime.ts +54 -185
- package/src/input/commands/support-bundle-runtime.ts +53 -0
- package/src/input/commands/tasks-runtime.ts +21 -21
- package/src/input/commands/work-plan-runtime.ts +33 -8
- package/src/input/commands.ts +6 -2
- package/src/input/feed-context-factory.ts +2 -2
- package/src/input/file-picker.ts +3 -2
- package/src/input/handler-command-route.ts +4 -7
- package/src/input/handler-content-actions.ts +89 -26
- package/src/input/handler-feed-routes.ts +19 -12
- package/src/input/handler-feed.ts +6 -5
- package/src/input/handler-interactions.ts +9 -36
- package/src/input/handler-modal-stack.ts +3 -3
- package/src/input/handler-onboarding.ts +52 -91
- package/src/input/handler-shortcuts.ts +16 -7
- package/src/input/handler.ts +50 -23
- package/src/input/input-history.ts +5 -6
- package/src/input/keybindings.ts +27 -16
- package/src/input/mcp-workspace.ts +90 -26
- package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +62 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +223 -55
- package/src/input/onboarding/onboarding-wizard-steps.ts +59 -35
- package/src/input/onboarding/onboarding-wizard-types.ts +4 -1
- package/src/input/onboarding/onboarding-wizard.ts +87 -2
- package/src/input/profile-picker-modal.ts +31 -12
- package/src/input/session-picker-modal.ts +21 -4
- package/src/input/settings-modal-agent-policy.ts +6 -6
- package/src/input/settings-modal-behavior.ts +0 -3
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +2 -13
- package/src/input/settings-modal.ts +20 -66
- package/src/input/submission-intent.ts +0 -1
- package/src/input/submission-router.ts +3 -3
- package/src/main.ts +18 -10
- package/src/panels/approval-panel.ts +8 -8
- package/src/panels/automation-control-panel.ts +4 -4
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +3 -12
- package/src/panels/builtin/session.ts +32 -24
- package/src/panels/builtin/shared.ts +7 -7
- package/src/panels/cost-tracker-panel.ts +1 -1
- package/src/panels/docs-panel.ts +5 -3
- package/src/panels/index.ts +0 -1
- package/src/panels/knowledge-panel.ts +6 -5
- package/src/panels/memory-panel.ts +7 -6
- package/src/panels/panel-list-panel.ts +36 -80
- package/src/panels/project-planning-panel.ts +19 -12
- package/src/panels/provider-account-snapshot.ts +51 -25
- package/src/panels/provider-accounts-panel.ts +33 -18
- package/src/panels/provider-health-domains.ts +48 -7
- package/src/panels/provider-health-panel.ts +5 -4
- package/src/panels/qr-panel.ts +44 -20
- package/src/panels/schedule-panel.ts +9 -17
- package/src/panels/security-panel.ts +8 -8
- package/src/panels/session-browser-panel.ts +10 -10
- package/src/panels/subscription-panel.ts +6 -6
- package/src/panels/system-messages-panel.ts +3 -3
- package/src/panels/tasks-panel.ts +21 -14
- package/src/panels/tool-inspector-panel.ts +19 -12
- package/src/panels/work-plan-panel.ts +5 -5
- package/src/planning/project-planning-coordinator.ts +3 -3
- package/src/provider-auth-route-display.ts +9 -0
- package/src/renderer/agent-workspace-style.ts +34 -0
- package/src/renderer/agent-workspace.ts +254 -57
- package/src/renderer/autocomplete-overlay.ts +25 -6
- package/src/renderer/block-actions.ts +1 -3
- package/src/renderer/bookmark-modal.ts +19 -4
- package/src/renderer/buffer.ts +4 -2
- package/src/renderer/context-inspector.ts +50 -13
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +19 -6
- package/src/renderer/help-overlay.ts +112 -34
- package/src/renderer/history-search-overlay.ts +19 -4
- package/src/renderer/live-tail-modal.ts +27 -5
- package/src/renderer/mcp-workspace.ts +176 -43
- package/src/renderer/model-picker-overlay.ts +58 -3
- package/src/renderer/model-workspace.ts +104 -22
- package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
- package/src/renderer/process-modal.ts +27 -6
- package/src/renderer/profile-picker-modal.ts +21 -5
- package/src/renderer/search-overlay.ts +25 -5
- package/src/renderer/selection-modal-overlay.ts +46 -14
- package/src/renderer/session-picker-modal.ts +18 -1
- package/src/renderer/settings-modal-helpers.ts +2 -40
- package/src/renderer/settings-modal.ts +88 -55
- package/src/renderer/system-message.ts +1 -1
- package/src/renderer/tool-call.ts +20 -11
- package/src/runtime/agent-runtime-events.ts +129 -0
- package/src/runtime/bootstrap-command-context.ts +7 -1
- package/src/runtime/bootstrap-command-parts.ts +11 -8
- package/src/runtime/bootstrap-core.ts +23 -65
- package/src/runtime/bootstrap-hook-bridge.ts +7 -18
- package/src/runtime/bootstrap-shell.ts +46 -8
- package/src/runtime/bootstrap.ts +36 -46
- package/src/runtime/connected-host-auth.ts +47 -0
- package/src/runtime/diagnostics/panels/index.ts +2 -2
- package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
- package/src/runtime/diagnostics/panels/policy.ts +7 -7
- package/src/runtime/index.ts +2 -5
- package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
- package/src/runtime/onboarding/apply.ts +288 -74
- package/src/runtime/onboarding/derivation.ts +10 -13
- package/src/runtime/onboarding/snapshot.ts +2 -17
- package/src/runtime/onboarding/types.ts +34 -20
- package/src/runtime/onboarding/verify.ts +105 -4
- package/src/runtime/operator-token-cleanup.ts +3 -3
- package/src/runtime/services.ts +95 -11
- package/src/runtime/store/selectors/index.ts +3 -3
- package/src/runtime/store/state.ts +1 -4
- package/src/runtime/surface-feature-flags.ts +41 -6
- package/src/runtime/ui-read-models.ts +3 -4
- package/src/runtime/ui-services.ts +6 -6
- package/src/shell/blocking-input.ts +2 -1
- package/src/shell/service-settings-sync.ts +7 -7
- package/src/shell/ui-openers.ts +17 -38
- package/src/tools/agent-analysis-registry-policy.ts +0 -1
- package/src/tools/agent-channel-send-tool.ts +133 -0
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
- package/src/tools/agent-knowledge-tool.ts +170 -0
- package/src/tools/agent-local-registry-tool.ts +279 -73
- package/src/tools/agent-media-generate-tool.ts +133 -0
- package/src/tools/agent-notify-tool.ts +143 -0
- package/src/tools/agent-operator-action-tool.ts +137 -0
- package/src/tools/agent-operator-briefing-tool.ts +217 -0
- package/src/tools/agent-reminder-schedule-tool.ts +237 -0
- package/src/tools/agent-tool-policy-guard.ts +13 -15
- package/src/tools/agent-work-plan-tool.ts +256 -0
- package/src/version.ts +1 -1
- package/docs/connected-services.md +0 -51
- package/src/cli/package-verification.ts +0 -274
- package/src/input/commands/policy-dispatch.ts +0 -339
- package/src/input/commands/policy.ts +0 -17
- package/src/panels/panel-picker.ts +0 -105
- package/src/panels/policy-panel.ts +0 -308
- package/src/renderer/panel-picker-overlay.ts +0 -202
package/src/runtime/index.ts
CHANGED
|
@@ -97,7 +97,6 @@ export const restoreRuntimeModel = bootstrap.restoreRuntimeModel;
|
|
|
97
97
|
export const restoreSavedModel = bootstrap.restoreRuntimeModel;
|
|
98
98
|
export const synchronizeConfiguredServices = bootstrap.synchronizeConfiguredServices;
|
|
99
99
|
export const syncConfiguredServices = bootstrap.synchronizeConfiguredServices;
|
|
100
|
-
export const registerBootstrapRuntimeEvents = bootstrap.registerBootstrapRuntimeEvents;
|
|
101
100
|
export const registerHostRuntimeEvents = bootstrap.registerHostRuntimeEvents;
|
|
102
101
|
function agentExternalHostStatus(
|
|
103
102
|
config: Bootstrap.HostServicesConfig,
|
|
@@ -113,7 +112,7 @@ function agentExternalHostStatus(
|
|
|
113
112
|
port,
|
|
114
113
|
baseUrl: `http://${host}:${port}`,
|
|
115
114
|
reason: service === 'daemon'
|
|
116
|
-
? 'GoodVibes Agent connects to GoodVibes
|
|
115
|
+
? 'GoodVibes Agent connects to a GoodVibes host owned outside this product and does not start or restart it.'
|
|
117
116
|
: 'GoodVibes Agent does not own listener lifecycle.',
|
|
118
117
|
};
|
|
119
118
|
}
|
|
@@ -245,7 +244,6 @@ export type RemoteRuntimeEvents = Transport.RemoteRuntimeEvents;
|
|
|
245
244
|
export type SerializedRuntimeEnvelope = Transport.SerializedRuntimeEnvelope;
|
|
246
245
|
|
|
247
246
|
// Operations compatibility aliases.
|
|
248
|
-
export const AcpTaskAdapter = operations.AcpTaskAdapter;
|
|
249
247
|
export const OpsControlPlane = operations.OpsControlPlane;
|
|
250
248
|
export const OpsIllegalActionError = operations.OpsIllegalActionError;
|
|
251
249
|
export const OpsTargetNotFoundError = operations.OpsTargetNotFoundError;
|
|
@@ -436,10 +434,10 @@ export const uninstallEcosystemCatalogEntry = shell.uninstallEcosystemCatalogEnt
|
|
|
436
434
|
export const updateInstalledEcosystemEntry = shell.updateInstalledEcosystemEntry;
|
|
437
435
|
export const upsertEcosystemCatalogEntry = shell.upsertEcosystemCatalogEntry;
|
|
438
436
|
export const summarizeWorktreeOwnership = shell.summarizeWorktreeOwnership;
|
|
439
|
-
export const WorktreeRegistry = shell.WorktreeRegistry;
|
|
440
437
|
export const listPersistedWorktreeMeta = shell.listPersistedWorktreeMeta;
|
|
441
438
|
export const getPersistedWorktreeMeta = shell.getPersistedWorktreeMeta;
|
|
442
439
|
export const reviewWorktreeAttachments = shell.reviewWorktreeAttachments;
|
|
440
|
+
export const WorktreeRegistry = shell.WorktreeRegistry;
|
|
443
441
|
|
|
444
442
|
export type MutableRuntimeState = Shell.MutableRuntimeState;
|
|
445
443
|
export type ProviderAccountRecord = Shell.ProviderAccountRecord;
|
|
@@ -458,7 +456,6 @@ export type WorktreeRegistry = Shell.WorktreeRegistry;
|
|
|
458
456
|
export type WorktreeStatusRecord = Shell.WorktreeStatusRecord;
|
|
459
457
|
export type ManagedWorktreeMeta = Shell.ManagedWorktreeMeta;
|
|
460
458
|
export type ShellAgentManagerService = Shell.ShellAgentManagerService;
|
|
461
|
-
export type ShellAcpManagerService = Shell.ShellAcpManagerService;
|
|
462
459
|
export type ShellAutomationManagerService = Shell.ShellAutomationManagerService;
|
|
463
460
|
export type ShellAutomationManagerRuntimeService = Shell.ShellAutomationManagerRuntimeService;
|
|
464
461
|
export type ShellModeManagerService = Shell.ShellModeManagerService;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export type RollbackAction = () => Promise<void> | void;
|
|
5
|
+
|
|
6
|
+
export function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
7
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function readJsonObject(path: string): Record<string, unknown> {
|
|
11
|
+
if (!existsSync(path)) return {};
|
|
12
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8')) as unknown;
|
|
13
|
+
if (!isPlainObject(parsed)) throw new Error(`Expected an object JSON payload at ${path}.`);
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function writeJsonObject(path: string, payload: Record<string, unknown>): void {
|
|
18
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
19
|
+
writeFileSync(path, `${JSON.stringify(payload, null, 2)}\n`, 'utf-8');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function setNestedValue(root: Record<string, unknown>, key: string, value: unknown): Record<string, unknown> {
|
|
23
|
+
const parts = key.split('.');
|
|
24
|
+
const next = structuredClone(root);
|
|
25
|
+
let cursor: Record<string, unknown> = next;
|
|
26
|
+
|
|
27
|
+
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
28
|
+
const part = parts[index]!;
|
|
29
|
+
let existing = cursor[part];
|
|
30
|
+
if (!isPlainObject(existing)) {
|
|
31
|
+
existing = {};
|
|
32
|
+
cursor[part] = existing;
|
|
33
|
+
}
|
|
34
|
+
cursor = existing as Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
cursor[parts[parts.length - 1]!] = structuredClone(value);
|
|
38
|
+
return next;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function restoreFile(path: string, previous: string | null, reload?: () => void): void {
|
|
42
|
+
if (previous === null) {
|
|
43
|
+
if (existsSync(path)) unlinkSync(path);
|
|
44
|
+
} else {
|
|
45
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
46
|
+
writeFileSync(path, previous, 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
reload?.();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function snapshotFileRollback(path: string, reload?: () => void): RollbackAction {
|
|
52
|
+
const previous = existsSync(path) ? readFileSync(path, 'utf-8') : null;
|
|
53
|
+
return () => restoreFile(path, previous, reload);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function runRollbacks(rollbacks: readonly RollbackAction[]): Promise<readonly string[]> {
|
|
57
|
+
const errors: string[] = [];
|
|
58
|
+
for (const rollback of [...rollbacks].reverse()) {
|
|
59
|
+
try {
|
|
60
|
+
await rollback();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return errors;
|
|
66
|
+
}
|
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
import { existsSync,
|
|
2
|
-
import { dirname } from 'node:path';
|
|
1
|
+
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
3
2
|
import { isSecretRefInput } from '@pellux/goodvibes-sdk/platform/config';
|
|
4
3
|
import { CONFIG_SCHEMA, DEFAULT_CONFIG } from '../../config/index.ts';
|
|
5
4
|
import {
|
|
6
5
|
createAgentRuntimeProfile,
|
|
7
6
|
getAgentRuntimeProfileTemplate,
|
|
7
|
+
getAgentRuntimeProfileSelectionPath,
|
|
8
|
+
readAgentRuntimeProfileSelection,
|
|
8
9
|
resolveAgentRuntimeProfileHome,
|
|
10
|
+
setAgentRuntimeProfileSelection,
|
|
9
11
|
} from '../../agent/runtime-profile.ts';
|
|
10
|
-
import
|
|
12
|
+
import { AgentNoteRegistry } from '../../agent/note-registry.ts';
|
|
13
|
+
import { AgentPersonaRegistry, assertNoSecretLikeText } from '../../agent/persona-registry.ts';
|
|
14
|
+
import { AgentRoutineRegistry } from '../../agent/routine-registry.ts';
|
|
15
|
+
import { AgentSkillRegistry } from '../../agent/skill-registry.ts';
|
|
16
|
+
import {
|
|
17
|
+
isFeatureFlagConfigKey,
|
|
18
|
+
mergeFeatureFlagConfigValue,
|
|
19
|
+
readFeatureFlagConfigValue,
|
|
20
|
+
type FeatureFlagConfigKey,
|
|
21
|
+
} from '../surface-feature-flags.ts';
|
|
11
22
|
import {
|
|
12
23
|
getOnboardingRuntimeStatePath,
|
|
13
24
|
readOnboardingRuntimeState,
|
|
@@ -23,72 +34,12 @@ import type {
|
|
|
23
34
|
OnboardingApplyRequest,
|
|
24
35
|
OnboardingApplyResult,
|
|
25
36
|
} from './types.ts';
|
|
37
|
+
import { isPlainObject, readJsonObject, restoreFile, runRollbacks, setNestedValue, snapshotFileRollback, writeJsonObject, type RollbackAction } from './apply-file-helpers.ts';
|
|
26
38
|
|
|
27
39
|
function getNow(deps: Pick<OnboardingApplyDependencies, 'clock'>): number {
|
|
28
40
|
return deps.clock?.() ?? Date.now();
|
|
29
41
|
}
|
|
30
42
|
|
|
31
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
32
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function readJsonObject(path: string): Record<string, unknown> {
|
|
36
|
-
if (!existsSync(path)) return {};
|
|
37
|
-
const parsed = JSON.parse(readFileSync(path, 'utf-8')) as unknown;
|
|
38
|
-
if (!isPlainObject(parsed)) throw new Error(`Expected an object JSON payload at ${path}.`);
|
|
39
|
-
return parsed;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function writeJsonObject(path: string, payload: Record<string, unknown>): void {
|
|
43
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
44
|
-
writeFileSync(path, `${JSON.stringify(payload, null, 2)}\n`, 'utf-8');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function setNestedValue(root: Record<string, unknown>, key: string, value: unknown): Record<string, unknown> {
|
|
48
|
-
const parts = key.split('.');
|
|
49
|
-
const next = structuredClone(root);
|
|
50
|
-
let cursor: Record<string, unknown> = next;
|
|
51
|
-
|
|
52
|
-
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
53
|
-
const part = parts[index]!;
|
|
54
|
-
const existing = cursor[part];
|
|
55
|
-
if (!isPlainObject(existing)) cursor[part] = {};
|
|
56
|
-
cursor = cursor[part] as Record<string, unknown>;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
cursor[parts[parts.length - 1]!] = structuredClone(value);
|
|
60
|
-
return next;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
type RollbackAction = () => Promise<void> | void;
|
|
64
|
-
|
|
65
|
-
function restoreFile(path: string, previous: string | null, reload?: () => void): void {
|
|
66
|
-
if (previous === null) {
|
|
67
|
-
if (existsSync(path)) unlinkSync(path);
|
|
68
|
-
} else {
|
|
69
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
70
|
-
writeFileSync(path, previous, 'utf-8');
|
|
71
|
-
}
|
|
72
|
-
reload?.();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function snapshotFileRollback(path: string, reload?: () => void): RollbackAction {
|
|
76
|
-
const previous = existsSync(path) ? readFileSync(path, 'utf-8') : null;
|
|
77
|
-
return () => restoreFile(path, previous, reload);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async function runRollbacks(rollbacks: readonly RollbackAction[]): Promise<readonly string[]> {
|
|
81
|
-
const errors: string[] = [];
|
|
82
|
-
for (const rollback of [...rollbacks].reverse()) {
|
|
83
|
-
try {
|
|
84
|
-
await rollback();
|
|
85
|
-
} catch (error) {
|
|
86
|
-
errors.push(error instanceof Error ? error.message : String(error));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return errors;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
43
|
function isGoodVibesSecretReferenceValue(value: string): boolean {
|
|
93
44
|
const normalized = value.trim();
|
|
94
45
|
return normalized.startsWith('goodvibes://secrets/') && isSecretRefInput(normalized);
|
|
@@ -99,10 +50,6 @@ function isMalformedGoodVibesSecretReferenceValue(value: string): boolean {
|
|
|
99
50
|
return normalized.startsWith('goodvibes://') && !isGoodVibesSecretReferenceValue(normalized);
|
|
100
51
|
}
|
|
101
52
|
|
|
102
|
-
function isFeatureFlagConfigKey(key: string): key is FeatureFlagConfigKey {
|
|
103
|
-
return key === 'featureFlags' || key.startsWith('featureFlags.');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
53
|
function validateFeatureFlagConfigValue(operation: Extract<OnboardingApplyOperation, { kind: 'set-config' }>): boolean {
|
|
107
54
|
if (!isFeatureFlagConfigKey(operation.key)) return false;
|
|
108
55
|
|
|
@@ -137,7 +84,7 @@ function validateConfigValue(operation: Extract<OnboardingApplyOperation, { kind
|
|
|
137
84
|
const defaultValue = operation.key.split('.').reduce<unknown>((cursor, part) => (
|
|
138
85
|
isPlainObject(cursor) ? cursor[part] : undefined
|
|
139
86
|
), DEFAULT_CONFIG);
|
|
140
|
-
if (defaultValue === undefined) throw new Error(`Unknown config key
|
|
87
|
+
if (defaultValue === undefined) throw new Error(`Unknown config key ${operation.key}`);
|
|
141
88
|
if (typeof defaultValue === 'boolean' && typeof operation.value !== 'boolean') {
|
|
142
89
|
throw new Error(`Config key ${operation.key} expects a boolean value.`);
|
|
143
90
|
}
|
|
@@ -164,11 +111,11 @@ function validateConfigValue(operation: Extract<OnboardingApplyOperation, { kind
|
|
|
164
111
|
}
|
|
165
112
|
|
|
166
113
|
if (schema.type === 'enum' && schema.enumValues && stringValue !== null && !schema.enumValues.includes(stringValue)) {
|
|
167
|
-
throw new Error(`Invalid value for ${operation.key}
|
|
114
|
+
throw new Error(`Invalid value for ${operation.key} ${String(operation.value)}.`);
|
|
168
115
|
}
|
|
169
116
|
|
|
170
117
|
if (schema.validate && !schema.validate(operation.value)) {
|
|
171
|
-
throw new Error(`Invalid value for ${operation.key}
|
|
118
|
+
throw new Error(`Invalid value for ${operation.key} ${String(operation.value)}.`);
|
|
172
119
|
}
|
|
173
120
|
}
|
|
174
121
|
|
|
@@ -222,6 +169,80 @@ function validateCreateAgentProfileOperation(
|
|
|
222
169
|
if (templateId) getAgentRuntimeProfileTemplate(templateId, deps.shellPaths.homeDirectory);
|
|
223
170
|
}
|
|
224
171
|
|
|
172
|
+
function validateSelectAgentProfileOperation(
|
|
173
|
+
deps: OnboardingApplyDependencies,
|
|
174
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'select-agent-profile' }>,
|
|
175
|
+
): void {
|
|
176
|
+
const name = operation.name.trim();
|
|
177
|
+
if (name.length === 0) throw new Error('Agent profile name is required.');
|
|
178
|
+
const resolution = resolveAgentRuntimeProfileHome(deps.shellPaths.homeDirectory, name);
|
|
179
|
+
if (!existsSync(resolution.homeDirectory)) {
|
|
180
|
+
throw new Error(`Agent profile does not exist: ${resolution.id}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function validateCreateLocalPersonaOperation(
|
|
185
|
+
deps: OnboardingApplyDependencies,
|
|
186
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-local-persona' }>,
|
|
187
|
+
): void {
|
|
188
|
+
const name = operation.name.trim();
|
|
189
|
+
const description = operation.description.trim();
|
|
190
|
+
const body = operation.body.trim();
|
|
191
|
+
if (!name) throw new Error('Persona name is required.');
|
|
192
|
+
if (!description) throw new Error('Persona description is required.');
|
|
193
|
+
if (!body) throw new Error('Persona body is required.');
|
|
194
|
+
assertNoSecretLikeText([name, description, body]);
|
|
195
|
+
const duplicate = AgentPersonaRegistry.fromShellPaths(deps.shellPaths).get(name);
|
|
196
|
+
if (duplicate) throw new Error(`Persona already exists: ${duplicate.id}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function validateCreateLocalSkillOperation(
|
|
200
|
+
deps: OnboardingApplyDependencies,
|
|
201
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-local-skill' }>,
|
|
202
|
+
): void {
|
|
203
|
+
const registry = AgentSkillRegistry.fromShellPaths(deps.shellPaths);
|
|
204
|
+
const name = operation.name.trim();
|
|
205
|
+
const description = operation.description.trim();
|
|
206
|
+
const procedure = operation.procedure.trim();
|
|
207
|
+
if (!name) throw new Error('Skill name is required.');
|
|
208
|
+
if (!description) throw new Error('Skill description is required.');
|
|
209
|
+
if (!procedure) throw new Error('Skill procedure is required.');
|
|
210
|
+
assertNoSecretLikeText([name, description, procedure]);
|
|
211
|
+
const duplicate = registry.get(name);
|
|
212
|
+
if (duplicate) throw new Error(`Skill already exists: ${duplicate.id}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function validateCreateLocalRoutineOperation(
|
|
216
|
+
deps: OnboardingApplyDependencies,
|
|
217
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-local-routine' }>,
|
|
218
|
+
): void {
|
|
219
|
+
const registry = AgentRoutineRegistry.fromShellPaths(deps.shellPaths);
|
|
220
|
+
const name = operation.name.trim();
|
|
221
|
+
const description = operation.description.trim();
|
|
222
|
+
const steps = operation.steps.trim();
|
|
223
|
+
if (!name) throw new Error('Routine name is required.');
|
|
224
|
+
if (!description) throw new Error('Routine description is required.');
|
|
225
|
+
if (!steps) throw new Error('Routine steps are required.');
|
|
226
|
+
assertNoSecretLikeText([name, description, steps]);
|
|
227
|
+
const duplicate = registry.get(name);
|
|
228
|
+
if (duplicate) throw new Error(`Routine already exists: ${duplicate.id}`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function validateCreateLocalNoteOperation(
|
|
232
|
+
deps: OnboardingApplyDependencies,
|
|
233
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-local-note' }>,
|
|
234
|
+
): void {
|
|
235
|
+
const registry = AgentNoteRegistry.fromShellPaths(deps.shellPaths);
|
|
236
|
+
const title = operation.title.trim();
|
|
237
|
+
const body = operation.body.trim();
|
|
238
|
+
const tags = operation.tags ?? [];
|
|
239
|
+
if (!title) throw new Error('Note title is required.');
|
|
240
|
+
if (!body) throw new Error('Note body is required.');
|
|
241
|
+
assertNoSecretLikeText([title, body, ...tags]);
|
|
242
|
+
const duplicate = registry.get(title);
|
|
243
|
+
if (duplicate) throw new Error(`Note already exists: ${duplicate.id}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
225
246
|
function applyConfigOperation(
|
|
226
247
|
deps: OnboardingApplyDependencies,
|
|
227
248
|
operation: Extract<OnboardingApplyOperation, { kind: 'set-config' }>,
|
|
@@ -241,7 +262,11 @@ function applyConfigOperation(
|
|
|
241
262
|
};
|
|
242
263
|
}
|
|
243
264
|
|
|
244
|
-
|
|
265
|
+
if (isFeatureFlagConfigKey(operation.key)) {
|
|
266
|
+
mergeFeatureFlagConfigValue(deps.config, operation.key, operation.value);
|
|
267
|
+
} else {
|
|
268
|
+
deps.config.setDynamic(operation.key, operation.value);
|
|
269
|
+
}
|
|
245
270
|
return {
|
|
246
271
|
kind: operation.kind,
|
|
247
272
|
summary: `Updated ${operation.key} in global onboarding settings.`,
|
|
@@ -294,9 +319,15 @@ async function buildRollbackAction(
|
|
|
294
319
|
);
|
|
295
320
|
}
|
|
296
321
|
|
|
297
|
-
const previous =
|
|
322
|
+
const previous = isFeatureFlagConfigKey(operation.key)
|
|
323
|
+
? readFeatureFlagConfigValue(deps.config, operation.key)
|
|
324
|
+
: deps.config.get(operation.key);
|
|
298
325
|
return () => {
|
|
299
|
-
|
|
326
|
+
if (isFeatureFlagConfigKey(operation.key)) {
|
|
327
|
+
mergeFeatureFlagConfigValue(deps.config, operation.key, previous ?? 'disabled');
|
|
328
|
+
} else {
|
|
329
|
+
deps.config.setDynamic(operation.key, previous);
|
|
330
|
+
}
|
|
300
331
|
};
|
|
301
332
|
}
|
|
302
333
|
|
|
@@ -322,6 +353,31 @@ async function buildRollbackAction(
|
|
|
322
353
|
};
|
|
323
354
|
}
|
|
324
355
|
|
|
356
|
+
if (operation.kind === 'select-agent-profile') {
|
|
357
|
+
const path = getAgentRuntimeProfileSelectionPath(deps.shellPaths.homeDirectory);
|
|
358
|
+
return snapshotFileRollback(path);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (operation.kind === 'create-local-note') {
|
|
362
|
+
const registry = AgentNoteRegistry.fromShellPaths(deps.shellPaths);
|
|
363
|
+
return snapshotFileRollback(registry.snapshot().path);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (operation.kind === 'create-local-persona') {
|
|
367
|
+
const registry = AgentPersonaRegistry.fromShellPaths(deps.shellPaths);
|
|
368
|
+
return snapshotFileRollback(registry.snapshot().path);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (operation.kind === 'create-local-skill') {
|
|
372
|
+
const registry = AgentSkillRegistry.fromShellPaths(deps.shellPaths);
|
|
373
|
+
return snapshotFileRollback(registry.snapshot().path);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (operation.kind === 'create-local-routine') {
|
|
377
|
+
const registry = AgentRoutineRegistry.fromShellPaths(deps.shellPaths);
|
|
378
|
+
return snapshotFileRollback(registry.snapshot().path);
|
|
379
|
+
}
|
|
380
|
+
|
|
325
381
|
const neverOperation: never = operation;
|
|
326
382
|
throw new Error(`Unsupported onboarding operation: ${JSON.stringify(neverOperation)}`);
|
|
327
383
|
}
|
|
@@ -364,6 +420,90 @@ function applyCreateAgentProfileOperation(
|
|
|
364
420
|
};
|
|
365
421
|
}
|
|
366
422
|
|
|
423
|
+
function applySelectAgentProfileOperation(
|
|
424
|
+
deps: OnboardingApplyDependencies,
|
|
425
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'select-agent-profile' }>,
|
|
426
|
+
): OnboardingAppliedOperation {
|
|
427
|
+
validateSelectAgentProfileOperation(deps, operation);
|
|
428
|
+
const selection = setAgentRuntimeProfileSelection(deps.shellPaths.homeDirectory, operation.name);
|
|
429
|
+
return {
|
|
430
|
+
kind: operation.kind,
|
|
431
|
+
summary: `Selected Agent profile ${selection.id} for later plain goodvibes-agent runs.`,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function applyCreateLocalPersonaOperation(
|
|
436
|
+
deps: OnboardingApplyDependencies,
|
|
437
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-local-persona' }>,
|
|
438
|
+
): OnboardingAppliedOperation {
|
|
439
|
+
const registry = AgentPersonaRegistry.fromShellPaths(deps.shellPaths);
|
|
440
|
+
const persona = registry.create({
|
|
441
|
+
name: operation.name,
|
|
442
|
+
description: operation.description,
|
|
443
|
+
body: operation.body,
|
|
444
|
+
source: 'user',
|
|
445
|
+
provenance: 'onboarding',
|
|
446
|
+
});
|
|
447
|
+
if (operation.activate !== false) registry.setActive(persona.id);
|
|
448
|
+
return {
|
|
449
|
+
kind: operation.kind,
|
|
450
|
+
summary: `Created Agent-local persona ${persona.id}.`,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function applyCreateLocalSkillOperation(
|
|
455
|
+
deps: OnboardingApplyDependencies,
|
|
456
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-local-skill' }>,
|
|
457
|
+
): OnboardingAppliedOperation {
|
|
458
|
+
const skill = AgentSkillRegistry.fromShellPaths(deps.shellPaths).create({
|
|
459
|
+
name: operation.name,
|
|
460
|
+
description: operation.description,
|
|
461
|
+
procedure: operation.procedure,
|
|
462
|
+
enabled: operation.enabled !== false,
|
|
463
|
+
source: 'user',
|
|
464
|
+
provenance: 'onboarding',
|
|
465
|
+
});
|
|
466
|
+
return {
|
|
467
|
+
kind: operation.kind,
|
|
468
|
+
summary: `Created Agent-local skill ${skill.id}.`,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function applyCreateLocalRoutineOperation(
|
|
473
|
+
deps: OnboardingApplyDependencies,
|
|
474
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-local-routine' }>,
|
|
475
|
+
): OnboardingAppliedOperation {
|
|
476
|
+
const routine = AgentRoutineRegistry.fromShellPaths(deps.shellPaths).create({
|
|
477
|
+
name: operation.name,
|
|
478
|
+
description: operation.description,
|
|
479
|
+
steps: operation.steps,
|
|
480
|
+
enabled: operation.enabled !== false,
|
|
481
|
+
source: 'user',
|
|
482
|
+
provenance: 'onboarding',
|
|
483
|
+
});
|
|
484
|
+
return {
|
|
485
|
+
kind: operation.kind,
|
|
486
|
+
summary: `Created Agent-local routine ${routine.id}.`,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function applyCreateLocalNoteOperation(
|
|
491
|
+
deps: OnboardingApplyDependencies,
|
|
492
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-local-note' }>,
|
|
493
|
+
): OnboardingAppliedOperation {
|
|
494
|
+
const note = AgentNoteRegistry.fromShellPaths(deps.shellPaths).create({
|
|
495
|
+
title: operation.title,
|
|
496
|
+
body: operation.body,
|
|
497
|
+
tags: operation.tags ?? [],
|
|
498
|
+
source: 'user',
|
|
499
|
+
provenance: 'onboarding',
|
|
500
|
+
});
|
|
501
|
+
return {
|
|
502
|
+
kind: operation.kind,
|
|
503
|
+
summary: `Created Agent-local note ${note.id}.`,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
367
507
|
function orderApplyOperations(
|
|
368
508
|
operations: readonly OnboardingApplyOperation[],
|
|
369
509
|
): readonly OnboardingApplyOperation[] {
|
|
@@ -376,6 +516,13 @@ function orderApplyOperations(
|
|
|
376
516
|
operation.kind === 'set-config' && operation.key !== 'storage.secretPolicy'
|
|
377
517
|
));
|
|
378
518
|
const agentProfileOperations = operations.filter((operation) => operation.kind === 'create-agent-profile');
|
|
519
|
+
const agentProfileSelectionOperations = operations.filter((operation) => operation.kind === 'select-agent-profile');
|
|
520
|
+
const localBehaviorOperations = operations.filter((operation) => (
|
|
521
|
+
operation.kind === 'create-local-note'
|
|
522
|
+
|| operation.kind === 'create-local-persona'
|
|
523
|
+
|| operation.kind === 'create-local-skill'
|
|
524
|
+
|| operation.kind === 'create-local-routine'
|
|
525
|
+
));
|
|
379
526
|
const finalOperations = operations.filter((operation) => (
|
|
380
527
|
operation.kind === 'acknowledge'
|
|
381
528
|
));
|
|
@@ -386,6 +533,8 @@ function orderApplyOperations(
|
|
|
386
533
|
...secretOperations,
|
|
387
534
|
...configOperations,
|
|
388
535
|
...agentProfileOperations,
|
|
536
|
+
...agentProfileSelectionOperations,
|
|
537
|
+
...localBehaviorOperations,
|
|
389
538
|
...finalOperations,
|
|
390
539
|
];
|
|
391
540
|
}
|
|
@@ -427,6 +576,36 @@ function prevalidateApplyRequest(
|
|
|
427
576
|
continue;
|
|
428
577
|
}
|
|
429
578
|
|
|
579
|
+
if (operation.kind === 'select-agent-profile') {
|
|
580
|
+
const createdEarlier = orderedOperations.some((candidate) => (
|
|
581
|
+
candidate.kind === 'create-agent-profile'
|
|
582
|
+
&& resolveAgentRuntimeProfileHome(deps.shellPaths.homeDirectory, candidate.name).id
|
|
583
|
+
=== resolveAgentRuntimeProfileHome(deps.shellPaths.homeDirectory, operation.name).id
|
|
584
|
+
));
|
|
585
|
+
if (!createdEarlier) validateSelectAgentProfileOperation(deps, operation);
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (operation.kind === 'create-local-persona') {
|
|
590
|
+
validateCreateLocalPersonaOperation(deps, operation);
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (operation.kind === 'create-local-note') {
|
|
595
|
+
validateCreateLocalNoteOperation(deps, operation);
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (operation.kind === 'create-local-skill') {
|
|
600
|
+
validateCreateLocalSkillOperation(deps, operation);
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (operation.kind === 'create-local-routine') {
|
|
605
|
+
validateCreateLocalRoutineOperation(deps, operation);
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
|
|
430
609
|
const neverOperation: never = operation;
|
|
431
610
|
throw new Error(`Unsupported onboarding operation: ${JSON.stringify(neverOperation)}`);
|
|
432
611
|
} catch (error) {
|
|
@@ -446,6 +625,11 @@ function getVerificationFailureKind(itemId: string): OnboardingApplyOperation['k
|
|
|
446
625
|
if (itemId.startsWith('auth:')) return 'ensure-auth-user';
|
|
447
626
|
if (itemId.startsWith('acknowledge:')) return 'acknowledge';
|
|
448
627
|
if (itemId.startsWith('agent-profile:')) return 'create-agent-profile';
|
|
628
|
+
if (itemId.startsWith('selected-agent-profile:')) return 'select-agent-profile';
|
|
629
|
+
if (itemId.startsWith('local-note:')) return 'create-local-note';
|
|
630
|
+
if (itemId.startsWith('local-persona:')) return 'create-local-persona';
|
|
631
|
+
if (itemId.startsWith('local-skill:')) return 'create-local-skill';
|
|
632
|
+
if (itemId.startsWith('local-routine:')) return 'create-local-routine';
|
|
449
633
|
return 'set-config';
|
|
450
634
|
}
|
|
451
635
|
|
|
@@ -502,6 +686,36 @@ export async function applyOnboardingRequest(
|
|
|
502
686
|
continue;
|
|
503
687
|
}
|
|
504
688
|
|
|
689
|
+
if (operation.kind === 'select-agent-profile') {
|
|
690
|
+
applied.push(applySelectAgentProfileOperation(deps, operation));
|
|
691
|
+
rollbacks.push(rollback);
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (operation.kind === 'create-local-persona') {
|
|
696
|
+
applied.push(applyCreateLocalPersonaOperation(deps, operation));
|
|
697
|
+
rollbacks.push(rollback);
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (operation.kind === 'create-local-note') {
|
|
702
|
+
applied.push(applyCreateLocalNoteOperation(deps, operation));
|
|
703
|
+
rollbacks.push(rollback);
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (operation.kind === 'create-local-skill') {
|
|
708
|
+
applied.push(applyCreateLocalSkillOperation(deps, operation));
|
|
709
|
+
rollbacks.push(rollback);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (operation.kind === 'create-local-routine') {
|
|
714
|
+
applied.push(applyCreateLocalRoutineOperation(deps, operation));
|
|
715
|
+
rollbacks.push(rollback);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
|
|
505
719
|
const neverOperation: never = operation;
|
|
506
720
|
throw new Error(`Unsupported onboarding operation: ${JSON.stringify(neverOperation)}`);
|
|
507
721
|
} catch (error) {
|
|
@@ -83,7 +83,7 @@ function isDeepEqual(left: unknown, right: unknown): boolean {
|
|
|
83
83
|
const rightEntries = Object.entries(right);
|
|
84
84
|
if (leftEntries.length !== rightEntries.length) return false;
|
|
85
85
|
|
|
86
|
-
return leftEntries.every(([key, value]) => isDeepEqual(value, (right
|
|
86
|
+
return leftEntries.every(([key, value]) => isDeepEqual(value, Reflect.get(right, key)));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
return false;
|
|
@@ -224,7 +224,7 @@ function hasAutomationReviewSignals(snapshot: OnboardingSnapshotState): boolean
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
function describeOperatorTerminal(): string {
|
|
227
|
-
return 'Use GoodVibes Agent as the terminal operator while connecting to existing GoodVibes
|
|
227
|
+
return 'Use GoodVibes Agent as the terminal operator while connecting to an existing GoodVibes host. Agent setup does not create new entrypoints.';
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
function describeProviderAccess(snapshot: OnboardingSnapshotState): string {
|
|
@@ -250,7 +250,7 @@ function describeLocalBehavior(snapshot: OnboardingSnapshotState): string {
|
|
|
250
250
|
...discovery.routines.names,
|
|
251
251
|
].slice(0, 4);
|
|
252
252
|
const sampleText = samples.length > 0 ? ` Found: ${samples.join(', ')}.` : '';
|
|
253
|
-
return `Import ${discoveredCount} discovered Agent persona/skill/routine file(s) from local
|
|
253
|
+
return `Import ${discoveredCount} discovered Agent persona/skill/routine file(s) from Agent-local folders before creating blank behavior.${sampleText}`;
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
if (!hasLocalBehaviorCustomization(snapshot)) {
|
|
@@ -288,7 +288,7 @@ function describeAutomationReview(snapshot: OnboardingSnapshotState): string {
|
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
function describeTuiDelegation(): string {
|
|
291
|
-
return 'Delegate explicit build, fix, implementation, and review work to GoodVibes TUI;
|
|
291
|
+
return 'Delegate explicit build, fix, implementation, and review work to GoodVibes TUI; delegated review is requested only when the user explicitly asks for it.';
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
function getAcknowledgementAccepted(
|
|
@@ -391,10 +391,7 @@ export function deriveStep1CapabilityFlags(
|
|
|
391
391
|
};
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
-
export function deriveStep1_5NetworkMode(
|
|
395
|
-
bindSettings: Pick<OnboardingSnapshotState, 'bindSettings'>['bindSettings'],
|
|
396
|
-
): OnboardingNetworkMode {
|
|
397
|
-
void bindSettings;
|
|
394
|
+
export function deriveStep1_5NetworkMode(): OnboardingNetworkMode {
|
|
398
395
|
return 'local-network-default';
|
|
399
396
|
}
|
|
400
397
|
|
|
@@ -458,23 +455,23 @@ export function deriveReopenEditAcknowledgementState(
|
|
|
458
455
|
snapshot,
|
|
459
456
|
'auth',
|
|
460
457
|
'bootstrap-credential',
|
|
461
|
-
'A connected-
|
|
458
|
+
'A connected-host bootstrap credential signal is still visible to Agent.',
|
|
462
459
|
)
|
|
463
460
|
: authSessionCount > 0
|
|
464
461
|
? buildRequiredAcknowledgement(
|
|
465
462
|
snapshot,
|
|
466
463
|
'auth',
|
|
467
464
|
'active-sessions',
|
|
468
|
-
`${authSessionCount} connected-
|
|
465
|
+
`${authSessionCount} connected-host auth session signal(s) are currently visible.`,
|
|
469
466
|
)
|
|
470
467
|
: authUserCount > 0
|
|
471
468
|
? buildRequiredAcknowledgement(
|
|
472
469
|
snapshot,
|
|
473
470
|
'auth',
|
|
474
471
|
'auth-state',
|
|
475
|
-
`${authUserCount} connected-
|
|
472
|
+
`${authUserCount} connected-host auth user signal(s) are already visible.`,
|
|
476
473
|
)
|
|
477
|
-
: buildNotNeededAcknowledgement(snapshot, 'auth', 'No connected-
|
|
474
|
+
: buildNotNeededAcknowledgement(snapshot, 'auth', 'No connected-host auth signal needs confirmation.');
|
|
478
475
|
|
|
479
476
|
return {
|
|
480
477
|
providers,
|
|
@@ -488,7 +485,7 @@ export function deriveOnboardingStepState(
|
|
|
488
485
|
): OnboardingStepDerivationState {
|
|
489
486
|
return {
|
|
490
487
|
step1Capabilities: deriveStep1Capabilities(snapshot),
|
|
491
|
-
step1_5NetworkMode: deriveStep1_5NetworkMode(
|
|
488
|
+
step1_5NetworkMode: deriveStep1_5NetworkMode(),
|
|
492
489
|
reopenEditAcknowledgements: deriveReopenEditAcknowledgementState(snapshot),
|
|
493
490
|
};
|
|
494
491
|
}
|