@pellux/goodvibes-agent 0.1.78 → 0.1.80
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 +10 -0
- package/package.json +1 -1
- package/src/input/agent-workspace-snapshot.ts +17 -0
- package/src/input/agent-workspace-types.ts +2 -0
- package/src/input/agent-workspace-voice-media.ts +216 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +99 -2
- package/src/renderer/agent-workspace.ts +31 -3
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GoodVibes Agent will be recorded here.
|
|
4
4
|
|
|
5
|
+
## 0.1.80 - 2026-06-01
|
|
6
|
+
|
|
7
|
+
- Added an Agent day-one readiness checklist to the onboarding review step covering runtime connection, default model route, profile setup, isolated Agent Knowledge, local behavior, channels, routines/schedules, and explicit build delegation.
|
|
8
|
+
- Kept the first-run review focused on Agent setup outcomes instead of copied runtime lifecycle or non-Agent knowledge behavior.
|
|
9
|
+
|
|
10
|
+
## 0.1.79 - 2026-06-01
|
|
11
|
+
|
|
12
|
+
- Expanded the Voice & Media workspace into a provider readiness matrix covering selected TTS readiness, missing secret key names, media understanding/generation posture, and browser-tool state.
|
|
13
|
+
- Kept voice, browser, and generated media side effects explicit: the workspace renders setup state and next steps only, never secret values or hidden runtime actions.
|
|
14
|
+
|
|
5
15
|
## 0.1.78 - 2026-06-01
|
|
6
16
|
|
|
7
17
|
- Expanded the Agent channel workspace into a concrete readiness matrix with setup state, missing runtime config keys, default-target posture, and safe next steps for each externally owned channel.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.80",
|
|
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",
|
|
@@ -6,6 +6,7 @@ import { AgentSkillRegistry, type AgentSkillRecord } from '../agent/skill-regist
|
|
|
6
6
|
import { getAgentRuntimeProfilesRoot, listAgentRuntimeProfiles, listAgentRuntimeProfileTemplates } from '../agent/runtime-profile.ts';
|
|
7
7
|
import { buildAgentWorkspaceChannels } from './agent-workspace-channels.ts';
|
|
8
8
|
import { buildAgentWorkspaceSetupChecklist } from './agent-workspace-setup.ts';
|
|
9
|
+
import { buildAgentWorkspaceVoiceMediaReadiness, type AgentWorkspaceVoiceMediaProviderDescriptor } from './agent-workspace-voice-media.ts';
|
|
9
10
|
import type {
|
|
10
11
|
AgentWorkspaceLocalLibraryItem,
|
|
11
12
|
AgentWorkspaceRuntimeProfileItem,
|
|
@@ -217,6 +218,16 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
|
|
|
217
218
|
return [];
|
|
218
219
|
}
|
|
219
220
|
})();
|
|
221
|
+
const voiceProviderDescriptors: readonly AgentWorkspaceVoiceMediaProviderDescriptor[] = voiceProviders.map((provider) => ({
|
|
222
|
+
id: provider.id,
|
|
223
|
+
label: provider.label,
|
|
224
|
+
capabilities: provider.capabilities,
|
|
225
|
+
}));
|
|
226
|
+
const mediaProviderDescriptors: readonly AgentWorkspaceVoiceMediaProviderDescriptor[] = mediaProviders.map((provider) => ({
|
|
227
|
+
id: provider.id,
|
|
228
|
+
label: provider.label,
|
|
229
|
+
capabilities: provider.capabilities,
|
|
230
|
+
}));
|
|
220
231
|
const warnings: string[] = [];
|
|
221
232
|
if (provider === 'unknown' || model === 'unknown') warnings.push('Provider/model unavailable in this runtime context.');
|
|
222
233
|
if (!context.executeCommand) warnings.push('Command dispatch is unavailable; workspace actions will show guidance only.');
|
|
@@ -226,6 +237,11 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
|
|
|
226
237
|
const ttsLlmModel = readConfigString(context, 'tts.llmModel', '');
|
|
227
238
|
const daemonBaseUrl = `http://${host}:${port}`;
|
|
228
239
|
const channels = buildAgentWorkspaceChannels(context);
|
|
240
|
+
const voiceMediaReadiness = buildAgentWorkspaceVoiceMediaReadiness({
|
|
241
|
+
context,
|
|
242
|
+
voiceProviders: voiceProviderDescriptors,
|
|
243
|
+
mediaProviders: mediaProviderDescriptors,
|
|
244
|
+
});
|
|
229
245
|
const setupChecklist = buildAgentWorkspaceSetupChecklist({
|
|
230
246
|
provider,
|
|
231
247
|
model,
|
|
@@ -278,6 +294,7 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
|
|
|
278
294
|
mediaProviderCount: mediaProviders.length,
|
|
279
295
|
mediaUnderstandingProviderCount: mediaProviders.filter((entry) => entry.capabilities.includes('understand')).length,
|
|
280
296
|
mediaGenerationProviderCount: mediaProviders.filter((entry) => entry.capabilities.includes('generate')).length,
|
|
297
|
+
voiceMediaReadiness,
|
|
281
298
|
browserSurfaceEnabled: readConfigBoolean(context, 'web.enabled', false),
|
|
282
299
|
browserSurfacePublicBaseUrl: readConfigString(context, 'web.publicBaseUrl', '(not configured)'),
|
|
283
300
|
activeRuntimeProfile: inferActiveRuntimeProfile(context.workspace?.shellPaths?.homeDirectory ?? ''),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentWorkspaceChannelStatus } from './agent-workspace-channels.ts';
|
|
2
2
|
import type { AgentWorkspaceSetupChecklistItem } from './agent-workspace-setup.ts';
|
|
3
|
+
import type { AgentWorkspaceVoiceMediaReadiness } from './agent-workspace-voice-media.ts';
|
|
3
4
|
|
|
4
5
|
export const AGENT_WORKSPACE_MODAL_NAME = 'agentWorkspace';
|
|
5
6
|
|
|
@@ -147,6 +148,7 @@ export interface AgentWorkspaceRuntimeSnapshot {
|
|
|
147
148
|
readonly mediaProviderCount: number;
|
|
148
149
|
readonly mediaUnderstandingProviderCount: number;
|
|
149
150
|
readonly mediaGenerationProviderCount: number;
|
|
151
|
+
readonly voiceMediaReadiness: AgentWorkspaceVoiceMediaReadiness;
|
|
150
152
|
readonly browserSurfaceEnabled: boolean;
|
|
151
153
|
readonly browserSurfacePublicBaseUrl: string;
|
|
152
154
|
readonly activeRuntimeProfile: string;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { CommandContext } from './command-registry.ts';
|
|
2
|
+
|
|
3
|
+
export type AgentWorkspaceVoiceMediaDomain = 'voice' | 'media';
|
|
4
|
+
export type AgentWorkspaceVoiceMediaSetupState = 'ready' | 'registered' | 'needs-secret' | 'not-registered';
|
|
5
|
+
|
|
6
|
+
export interface AgentWorkspaceVoiceMediaProviderDescriptor {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly label?: string;
|
|
9
|
+
readonly capabilities: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AgentWorkspaceVoiceMediaProviderStatus {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly label: string;
|
|
15
|
+
readonly domain: AgentWorkspaceVoiceMediaDomain;
|
|
16
|
+
readonly features: readonly string[];
|
|
17
|
+
readonly setupState: AgentWorkspaceVoiceMediaSetupState;
|
|
18
|
+
readonly selected: boolean;
|
|
19
|
+
readonly secretKeyOptions: readonly string[];
|
|
20
|
+
readonly configuredSecretKeys: readonly string[];
|
|
21
|
+
readonly missingSecretKeyOptions: readonly string[];
|
|
22
|
+
readonly nextStep: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AgentWorkspaceVoiceMediaReadiness {
|
|
26
|
+
readonly voiceProviders: readonly AgentWorkspaceVoiceMediaProviderStatus[];
|
|
27
|
+
readonly mediaProviders: readonly AgentWorkspaceVoiceMediaProviderStatus[];
|
|
28
|
+
readonly readyVoiceProviderCount: number;
|
|
29
|
+
readonly readyMediaProviderCount: number;
|
|
30
|
+
readonly selectedTtsProviderStatus: AgentWorkspaceVoiceMediaSetupState;
|
|
31
|
+
readonly selectedTtsProviderLabel: string;
|
|
32
|
+
readonly ttsVoiceConfigured: boolean;
|
|
33
|
+
readonly ttsResponseRouteConfigured: boolean;
|
|
34
|
+
readonly browserToolState: 'disabled' | 'local-only' | 'public-url';
|
|
35
|
+
readonly browserToolNextStep: string;
|
|
36
|
+
readonly nextSteps: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type AgentWorkspaceConfigReader = {
|
|
40
|
+
get(key: string): unknown;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type ProviderSecretSpec = {
|
|
44
|
+
readonly id: string;
|
|
45
|
+
readonly secretKeyOptions: readonly string[];
|
|
46
|
+
readonly noSecretRequired?: boolean;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const VOICE_SECRET_SPECS: readonly ProviderSecretSpec[] = [
|
|
50
|
+
{ id: 'openai', secretKeyOptions: ['OPENAI_API_KEY', 'OPENAI_KEY'] },
|
|
51
|
+
{ id: 'deepgram', secretKeyOptions: ['DEEPGRAM_API_KEY'] },
|
|
52
|
+
{ id: 'google', secretKeyOptions: ['GEMINI_API_KEY', 'GOOGLE_API_KEY', 'GOOGLE_GEMINI_API_KEY'] },
|
|
53
|
+
{ id: 'elevenlabs', secretKeyOptions: ['ELEVENLABS_API_KEY', 'XI_API_KEY'] },
|
|
54
|
+
{ id: 'microsoft', secretKeyOptions: [], noSecretRequired: true },
|
|
55
|
+
{ id: 'vydra', secretKeyOptions: ['VYDRA_API_KEY'] },
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const MEDIA_SECRET_SPECS: readonly ProviderSecretSpec[] = [
|
|
59
|
+
{ id: 'builtin:image-understanding', secretKeyOptions: [], noSecretRequired: true },
|
|
60
|
+
{ id: 'byteplus', secretKeyOptions: ['BYTEPLUS_API_KEY'] },
|
|
61
|
+
{ id: 'runway', secretKeyOptions: ['RUNWAY_API_KEY', 'RUNWAYML_API_SECRET'] },
|
|
62
|
+
{ id: 'alibaba', secretKeyOptions: ['MODELSTUDIO_API_KEY', 'DASHSCOPE_API_KEY', 'QWEN_API_KEY'] },
|
|
63
|
+
{ id: 'fal', secretKeyOptions: ['FAL_KEY', 'FAL_API_KEY'] },
|
|
64
|
+
{ id: 'comfy', secretKeyOptions: ['COMFY_BASE_URL', 'COMFY_API_KEY'] },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
function readConfigString(context: CommandContext, key: string, fallback: string): string {
|
|
68
|
+
try {
|
|
69
|
+
const configManager = context.platform?.configManager as unknown as AgentWorkspaceConfigReader | undefined;
|
|
70
|
+
const value = configManager?.get(key);
|
|
71
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : fallback;
|
|
72
|
+
} catch {
|
|
73
|
+
return fallback;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function readConfigBoolean(context: CommandContext, key: string, fallback: boolean): boolean {
|
|
78
|
+
try {
|
|
79
|
+
const configManager = context.platform?.configManager as unknown as AgentWorkspaceConfigReader | undefined;
|
|
80
|
+
const value = configManager?.get(key);
|
|
81
|
+
if (typeof value === 'boolean') return value;
|
|
82
|
+
if (typeof value === 'string') {
|
|
83
|
+
const normalized = value.trim().toLowerCase();
|
|
84
|
+
if (normalized === 'true') return true;
|
|
85
|
+
if (normalized === 'false') return false;
|
|
86
|
+
}
|
|
87
|
+
return fallback;
|
|
88
|
+
} catch {
|
|
89
|
+
return fallback;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function configuredSecretKeys(options: readonly string[]): readonly string[] {
|
|
94
|
+
return options.filter((key) => {
|
|
95
|
+
const value = process.env[key];
|
|
96
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function specFor(domain: AgentWorkspaceVoiceMediaDomain, id: string): ProviderSecretSpec {
|
|
101
|
+
const specs = domain === 'voice' ? VOICE_SECRET_SPECS : MEDIA_SECRET_SPECS;
|
|
102
|
+
return specs.find((entry) => entry.id === id) ?? { id, secretKeyOptions: [] };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function providerLabel(provider: AgentWorkspaceVoiceMediaProviderDescriptor): string {
|
|
106
|
+
return typeof provider.label === 'string' && provider.label.trim().length > 0 ? provider.label.trim() : provider.id;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function buildProviderStatus(
|
|
110
|
+
domain: AgentWorkspaceVoiceMediaDomain,
|
|
111
|
+
provider: AgentWorkspaceVoiceMediaProviderDescriptor,
|
|
112
|
+
selectedProviderId: string,
|
|
113
|
+
): AgentWorkspaceVoiceMediaProviderStatus {
|
|
114
|
+
const spec = specFor(domain, provider.id);
|
|
115
|
+
const configured = configuredSecretKeys(spec.secretKeyOptions);
|
|
116
|
+
const selected = selectedProviderId === provider.id;
|
|
117
|
+
const setupState: AgentWorkspaceVoiceMediaSetupState = spec.noSecretRequired || configured.length > 0
|
|
118
|
+
? 'ready'
|
|
119
|
+
: spec.secretKeyOptions.length > 0
|
|
120
|
+
? 'needs-secret'
|
|
121
|
+
: 'registered';
|
|
122
|
+
const label = providerLabel(provider);
|
|
123
|
+
const nextStep = setupState === 'ready'
|
|
124
|
+
? `${label} is ready for explicit Agent voice/media use.`
|
|
125
|
+
: setupState === 'needs-secret'
|
|
126
|
+
? `Configure one of ${spec.secretKeyOptions.join('|')} in the owning runtime environment.`
|
|
127
|
+
: `${label} is registered; confirm any provider-specific setup in the owning runtime.`;
|
|
128
|
+
return {
|
|
129
|
+
id: provider.id,
|
|
130
|
+
label,
|
|
131
|
+
domain,
|
|
132
|
+
features: provider.capabilities,
|
|
133
|
+
setupState,
|
|
134
|
+
selected,
|
|
135
|
+
secretKeyOptions: spec.secretKeyOptions,
|
|
136
|
+
configuredSecretKeys: configured,
|
|
137
|
+
missingSecretKeyOptions: configured.length > 0 || spec.noSecretRequired ? [] : spec.secretKeyOptions,
|
|
138
|
+
nextStep,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function missingSelectedProviderStatus(selectedProviderId: string): AgentWorkspaceVoiceMediaProviderStatus {
|
|
143
|
+
return {
|
|
144
|
+
id: selectedProviderId,
|
|
145
|
+
label: selectedProviderId,
|
|
146
|
+
domain: 'voice',
|
|
147
|
+
features: [],
|
|
148
|
+
setupState: 'not-registered',
|
|
149
|
+
selected: true,
|
|
150
|
+
secretKeyOptions: [],
|
|
151
|
+
configuredSecretKeys: [],
|
|
152
|
+
missingSecretKeyOptions: [],
|
|
153
|
+
nextStep: `Selected TTS provider ${selectedProviderId} is not registered in this runtime.`,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function selectedProviderStatus(
|
|
158
|
+
providers: readonly AgentWorkspaceVoiceMediaProviderStatus[],
|
|
159
|
+
selectedProviderId: string,
|
|
160
|
+
): AgentWorkspaceVoiceMediaProviderStatus {
|
|
161
|
+
return providers.find((provider) => provider.id === selectedProviderId) ?? missingSelectedProviderStatus(selectedProviderId);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function browserToolState(context: CommandContext): AgentWorkspaceVoiceMediaReadiness['browserToolState'] {
|
|
165
|
+
if (!readConfigBoolean(context, 'web.enabled', false)) return 'disabled';
|
|
166
|
+
const publicBaseUrl = readConfigString(context, 'web.publicBaseUrl', '');
|
|
167
|
+
return publicBaseUrl ? 'public-url' : 'local-only';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function browserToolNextStep(state: AgentWorkspaceVoiceMediaReadiness['browserToolState']): string {
|
|
171
|
+
if (state === 'disabled') return 'Inspect MCP browser/automation tools; enable browser access in the owning runtime only when needed.';
|
|
172
|
+
if (state === 'local-only') return 'Browser tooling is local-only; keep external exposure off unless explicitly configured.';
|
|
173
|
+
return 'Public browser URL is configured; use explicit user action and runtime policy before browser-side effects.';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function buildAgentWorkspaceVoiceMediaReadiness(options: {
|
|
177
|
+
readonly context: CommandContext;
|
|
178
|
+
readonly voiceProviders: readonly AgentWorkspaceVoiceMediaProviderDescriptor[];
|
|
179
|
+
readonly mediaProviders: readonly AgentWorkspaceVoiceMediaProviderDescriptor[];
|
|
180
|
+
}): AgentWorkspaceVoiceMediaReadiness {
|
|
181
|
+
const selectedTtsProviderId = readConfigString(options.context, 'tts.provider', '');
|
|
182
|
+
const ttsVoice = readConfigString(options.context, 'tts.voice', '');
|
|
183
|
+
const ttsLlmProvider = readConfigString(options.context, 'tts.llmProvider', '');
|
|
184
|
+
const ttsLlmModel = readConfigString(options.context, 'tts.llmModel', '');
|
|
185
|
+
const voiceProviders = options.voiceProviders.map((provider) => buildProviderStatus('voice', provider, selectedTtsProviderId));
|
|
186
|
+
const mediaProviders = options.mediaProviders.map((provider) => buildProviderStatus('media', provider, ''));
|
|
187
|
+
const selectedStatus = selectedTtsProviderId
|
|
188
|
+
? selectedProviderStatus(voiceProviders, selectedTtsProviderId)
|
|
189
|
+
: {
|
|
190
|
+
...missingSelectedProviderStatus('(provider default)'),
|
|
191
|
+
setupState: 'registered' as const,
|
|
192
|
+
nextStep: 'Choose a TTS provider when live speech should be predictable.',
|
|
193
|
+
};
|
|
194
|
+
const browserState = browserToolState(options.context);
|
|
195
|
+
const nextSteps = [
|
|
196
|
+
selectedStatus.setupState === 'needs-secret' ? selectedStatus.nextStep : '',
|
|
197
|
+
!ttsVoice ? 'Choose a stable TTS voice for day-one spoken replies.' : '',
|
|
198
|
+
mediaProviders.some((provider) => provider.features.includes('generate') && provider.setupState === 'needs-secret')
|
|
199
|
+
? 'Configure at least one media generation provider secret before image/video generation.'
|
|
200
|
+
: '',
|
|
201
|
+
browserToolNextStep(browserState),
|
|
202
|
+
].filter((step) => step.length > 0);
|
|
203
|
+
return {
|
|
204
|
+
voiceProviders,
|
|
205
|
+
mediaProviders,
|
|
206
|
+
readyVoiceProviderCount: voiceProviders.filter((provider) => provider.setupState === 'ready').length,
|
|
207
|
+
readyMediaProviderCount: mediaProviders.filter((provider) => provider.setupState === 'ready').length,
|
|
208
|
+
selectedTtsProviderStatus: selectedStatus.setupState,
|
|
209
|
+
selectedTtsProviderLabel: selectedStatus.label,
|
|
210
|
+
ttsVoiceConfigured: ttsVoice.length > 0,
|
|
211
|
+
ttsResponseRouteConfigured: ttsLlmProvider.length > 0 && ttsLlmModel.length > 0,
|
|
212
|
+
browserToolState: browserState,
|
|
213
|
+
browserToolNextStep: browserToolNextStep(browserState),
|
|
214
|
+
nextSteps,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
@@ -3,6 +3,7 @@ import { modelSelectionLabel, normalizeText } from './onboarding-wizard-helpers.
|
|
|
3
3
|
import { listAgentRuntimeProfileTemplates } from '../../agent/runtime-profile.ts';
|
|
4
4
|
import type { OnboardingWizardController } from './onboarding-wizard.ts';
|
|
5
5
|
import type { OnboardingWizardActionFieldDefinition, OnboardingWizardFieldDefinition, OnboardingWizardModelPickerFieldDefinition, OnboardingWizardRadioFieldDefinition, OnboardingWizardRadioOption, OnboardingWizardStepDefinition } from './onboarding-wizard-types.ts';
|
|
6
|
+
import type { OnboardingStep1CapabilityId, OnboardingStep1CapabilityItem } from '../../runtime/onboarding/index.ts';
|
|
6
7
|
|
|
7
8
|
function buildStarterTemplateOptions(): readonly OnboardingWizardRadioOption[] {
|
|
8
9
|
return [
|
|
@@ -58,6 +59,98 @@ function addApplyAndContinueAction(step: OnboardingWizardStepDefinition): Onboar
|
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
function findRuntimeCapability(
|
|
63
|
+
controller: OnboardingWizardController,
|
|
64
|
+
id: OnboardingStep1CapabilityId,
|
|
65
|
+
): OnboardingStep1CapabilityItem | null {
|
|
66
|
+
return controller.runtimeDerived.step1Capabilities.find((capability) => capability.id === id) ?? null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function currentMainModelLabel(controller: OnboardingWizardController): string {
|
|
70
|
+
const routing = controller.runtimeSnapshot?.providerRouting;
|
|
71
|
+
return modelSelectionLabel(controller.modelSelectionState.get('main') ?? {
|
|
72
|
+
providerId: normalizeText(routing?.primaryProviderId),
|
|
73
|
+
modelId: normalizeText(routing?.primaryModelId),
|
|
74
|
+
enabled: true,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function profileSetupLabel(controller: OnboardingWizardController): string {
|
|
79
|
+
const profileName = normalizeText(controller.getStringFieldValue('agent-setup.profile-name', ''));
|
|
80
|
+
const templateId = controller.getStringFieldValue('agent-setup.profile-template', 'none');
|
|
81
|
+
if (profileName.length === 0) return 'Current home';
|
|
82
|
+
return templateId === 'none' ? `Create ${profileName}` : `Create ${profileName} from ${templateId}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buildReviewReadinessFields(controller: OnboardingWizardController): readonly OnboardingWizardFieldDefinition[] {
|
|
86
|
+
const localBehavior = findRuntimeCapability(controller, 'local-behavior');
|
|
87
|
+
const channels = findRuntimeCapability(controller, 'communication-channels');
|
|
88
|
+
const automation = findRuntimeCapability(controller, 'automation-review');
|
|
89
|
+
const collectionIssues = controller.runtimeSnapshot?.collectionIssues.length ?? 0;
|
|
90
|
+
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
kind: 'status',
|
|
94
|
+
id: 'review.readiness.connection',
|
|
95
|
+
label: 'Runtime connection snapshot',
|
|
96
|
+
hint: collectionIssues > 0
|
|
97
|
+
? `${collectionIssues} setup snapshot issue(s) need attention before this Agent is day-one ready.`
|
|
98
|
+
: 'The setup snapshot loaded cleanly from the external GoodVibes runtime.',
|
|
99
|
+
defaultValue: collectionIssues > 0 ? 'Needs attention' : 'Ready',
|
|
100
|
+
spacerBeforeRows: 1,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
kind: 'status',
|
|
104
|
+
id: 'review.readiness.model',
|
|
105
|
+
label: 'Default model route',
|
|
106
|
+
hint: 'Normal assistant turns use this selected provider/model route unless changed later from the model picker.',
|
|
107
|
+
defaultValue: currentMainModelLabel(controller),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
kind: 'status',
|
|
111
|
+
id: 'review.readiness.profile',
|
|
112
|
+
label: 'Agent profile',
|
|
113
|
+
hint: 'Profiles isolate Agent-local config, sessions, memory, personas, skills, routines, and setup state.',
|
|
114
|
+
defaultValue: profileSetupLabel(controller),
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
kind: 'status',
|
|
118
|
+
id: 'review.readiness.knowledge',
|
|
119
|
+
label: 'Agent Knowledge segment',
|
|
120
|
+
hint: 'Ask, search, status, and ingest stay on /api/goodvibes-agent/knowledge/* with no default wiki or non-Agent fallback.',
|
|
121
|
+
defaultValue: 'Isolated',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
kind: 'status',
|
|
125
|
+
id: 'review.readiness.local-behavior',
|
|
126
|
+
label: 'Local behavior library',
|
|
127
|
+
hint: localBehavior?.detail ?? 'Agent-local memory, routines, skills, and personas remain local until a stable shared registry exists.',
|
|
128
|
+
defaultValue: localBehavior?.selected ? 'Customized' : 'Starter ready',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
kind: 'status',
|
|
132
|
+
id: 'review.readiness.channels',
|
|
133
|
+
label: 'Channels and notifications',
|
|
134
|
+
hint: channels?.detail ?? 'Connect only the channels the Agent should use, and keep outbound delivery explicit.',
|
|
135
|
+
defaultValue: channels?.selected ? 'Review configured' : 'Optional setup',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
kind: 'status',
|
|
139
|
+
id: 'review.readiness.automation',
|
|
140
|
+
label: 'Routines and schedules',
|
|
141
|
+
hint: automation?.detail ?? 'Local routines run in the main conversation; external schedules require explicit promotion and confirmation.',
|
|
142
|
+
defaultValue: automation?.selected ? 'Review configured' : 'Local first',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
kind: 'status',
|
|
146
|
+
id: 'review.readiness.delegation',
|
|
147
|
+
label: 'Build delegation',
|
|
148
|
+
hint: 'Build, fix, implementation, and review work is handed to GoodVibes TUI only when explicitly requested.',
|
|
149
|
+
defaultValue: 'Explicit only',
|
|
150
|
+
},
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
|
|
61
154
|
export function buildCommunicationStep(): OnboardingWizardStepDefinition {
|
|
62
155
|
return {
|
|
63
156
|
id: 'agent-communication',
|
|
@@ -636,14 +729,17 @@ export function buildReviewStep(controller: OnboardingWizardController): Onboard
|
|
|
636
729
|
const unsavedLabel = controller.dirtyStepCount === 1
|
|
637
730
|
? '1 screen has unapplied changes'
|
|
638
731
|
: `${controller.dirtyStepCount} screens have unapplied changes`;
|
|
732
|
+
const collectionIssues = controller.runtimeSnapshot?.collectionIssues.length ?? 0;
|
|
733
|
+
const dayOneReadiness = collectionIssues > 0 ? `${collectionIssues} connection issue(s) before day-one ready` : 'operator checklist ready';
|
|
639
734
|
|
|
640
735
|
return {
|
|
641
736
|
id: 'review',
|
|
642
737
|
title: 'Review and apply',
|
|
643
738
|
shortLabel: 'Review',
|
|
644
|
-
description: 'Review Agent-
|
|
645
|
-
summaryTitle: '
|
|
739
|
+
description: 'Review the Agent day-one checklist and apply setup directly from the wizard.',
|
|
740
|
+
summaryTitle: 'Agent day-one readiness',
|
|
646
741
|
summaryLines: [
|
|
742
|
+
`Day-one readiness: ${dayOneReadiness}`,
|
|
647
743
|
unsavedLabel,
|
|
648
744
|
`${controller.buildApplyRequest().operations.length} Agent setting change(s) ready to apply`,
|
|
649
745
|
feedback ? `Last apply: ${feedback.title}` : 'No apply errors reported',
|
|
@@ -651,6 +747,7 @@ export function buildReviewStep(controller: OnboardingWizardController): Onboard
|
|
|
651
747
|
],
|
|
652
748
|
fields: [
|
|
653
749
|
...feedbackFields,
|
|
750
|
+
...buildReviewReadinessFields(controller),
|
|
654
751
|
{
|
|
655
752
|
kind: 'status',
|
|
656
753
|
id: 'review.global-marker',
|
|
@@ -242,13 +242,41 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
242
242
|
{ text: 'Agent-owned content appears here only after explicit Agent knowledge ingestion.', fg: PALETTE.muted },
|
|
243
243
|
);
|
|
244
244
|
} else if (category.id === 'voice-media') {
|
|
245
|
+
const readiness = snapshot.voiceMediaReadiness;
|
|
246
|
+
const voiceRows = readiness.voiceProviders.slice(0, 6);
|
|
247
|
+
const mediaRows = readiness.mediaProviders.slice(0, 6);
|
|
245
248
|
base.push(
|
|
246
249
|
{ text: `Voice providers: ${snapshot.voiceProviderCount}; streaming TTS: ${snapshot.voiceStreamingProviderCount}; STT: ${snapshot.voiceSttProviderCount}; realtime: ${snapshot.voiceRealtimeProviderCount}.`, fg: PALETTE.info },
|
|
247
|
-
{ text: `Voice interaction: ${snapshot.voiceSurfaceEnabled ? 'enabled' : 'disabled'};
|
|
250
|
+
{ text: `Voice interaction: ${snapshot.voiceSurfaceEnabled ? 'enabled' : 'disabled'}; ready providers ${readiness.readyVoiceProviderCount}/${snapshot.voiceProviderCount}.`, fg: snapshot.voiceSurfaceEnabled ? PALETTE.warn : PALETTE.muted },
|
|
248
251
|
{ text: `TTS config: provider ${snapshot.ttsProvider}; voice ${snapshot.ttsVoice}; response model ${snapshot.ttsResponseModel}.`, fg: PALETTE.info },
|
|
252
|
+
{ text: `Selected TTS readiness: ${readiness.selectedTtsProviderLabel} -> ${readiness.selectedTtsProviderStatus}; voice ${readiness.ttsVoiceConfigured ? 'configured' : 'default'}; response route ${readiness.ttsResponseRouteConfigured ? 'configured' : 'chat route'}.`, fg: readiness.selectedTtsProviderStatus === 'ready' ? PALETTE.good : PALETTE.warn },
|
|
249
253
|
{ text: `Media providers: ${snapshot.mediaProviderCount}; understanding: ${snapshot.mediaUnderstandingProviderCount}; generation: ${snapshot.mediaGenerationProviderCount}.`, fg: PALETTE.info },
|
|
250
|
-
{ text: `
|
|
251
|
-
{ text:
|
|
254
|
+
{ text: `Ready media providers: ${readiness.readyMediaProviderCount}/${snapshot.mediaProviderCount}.`, fg: readiness.readyMediaProviderCount > 0 ? PALETTE.good : PALETTE.warn },
|
|
255
|
+
{ text: `Browser tooling: ${readiness.browserToolState}; public base URL ${snapshot.browserSurfacePublicBaseUrl}.`, fg: snapshot.browserSurfaceEnabled ? PALETTE.warn : PALETTE.muted },
|
|
256
|
+
{ text: readiness.browserToolNextStep, fg: PALETTE.muted },
|
|
257
|
+
{ text: 'Voice provider readiness', fg: PALETTE.title, bold: true },
|
|
258
|
+
);
|
|
259
|
+
for (const provider of voiceRows) {
|
|
260
|
+
const selected = provider.selected ? 'selected; ' : '';
|
|
261
|
+
const missing = provider.missingSecretKeyOptions.length > 0 ? `; needs ${provider.missingSecretKeyOptions.join('|')}` : '';
|
|
262
|
+
base.push({
|
|
263
|
+
text: `${provider.label}: ${selected}${provider.setupState}; ${provider.features.join(', ') || 'registered'}${missing}.`,
|
|
264
|
+
fg: provider.setupState === 'ready' ? PALETTE.good : provider.setupState === 'needs-secret' ? PALETTE.warn : PALETTE.muted,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
if (snapshot.voiceProviderCount > voiceRows.length) base.push({ text: `${snapshot.voiceProviderCount - voiceRows.length} more voice provider(s).`, fg: PALETTE.dim });
|
|
268
|
+
base.push({ text: 'Media provider readiness', fg: PALETTE.title, bold: true });
|
|
269
|
+
for (const provider of mediaRows) {
|
|
270
|
+
const missing = provider.missingSecretKeyOptions.length > 0 ? `; needs ${provider.missingSecretKeyOptions.join('|')}` : '';
|
|
271
|
+
base.push({
|
|
272
|
+
text: `${provider.label}: ${provider.setupState}; ${provider.features.join(', ') || 'registered'}${missing}.`,
|
|
273
|
+
fg: provider.setupState === 'ready' ? PALETTE.good : provider.setupState === 'needs-secret' ? PALETTE.warn : PALETTE.muted,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (snapshot.mediaProviderCount > mediaRows.length) base.push({ text: `${snapshot.mediaProviderCount - mediaRows.length} more media provider(s).`, fg: PALETTE.dim });
|
|
277
|
+
for (const step of readiness.nextSteps.slice(0, 4)) base.push({ text: `Next: ${step}`, fg: PALETTE.info });
|
|
278
|
+
base.push(
|
|
279
|
+
{ text: 'No secret values are rendered. Voice, browser, and generated media side effects require explicit user action.', fg: PALETTE.warn },
|
|
252
280
|
{ text: 'Image input uses prompt attachments; media generation/provider setup stays behind explicit commands and configured providers.', fg: PALETTE.muted },
|
|
253
281
|
);
|
|
254
282
|
} else if (category.id === 'profiles') {
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.1.
|
|
9
|
+
let _version = '0.1.80';
|
|
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 {
|