@pellux/goodvibes-agent 0.1.74 → 0.1.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/agent/routine-schedule-args.ts +7 -4
- package/src/agent/routine-schedule-format.ts +7 -2
- package/src/cli/help.ts +2 -2
- package/src/cli/management-commands.ts +1 -1
- package/src/cli/routines-command.ts +2 -2
- package/src/cli/service-posture.ts +8 -8
- package/src/cli/status.ts +45 -45
- package/src/input/agent-workspace-categories.ts +1 -1
- package/src/input/commands/experience-runtime.ts +6 -6
- package/src/input/commands/operator-runtime.ts +1 -1
- package/src/input/commands/routines-runtime.ts +1 -1
- package/src/input/commands/schedule-runtime.ts +4 -4
- package/src/input/commands/tasks-runtime.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +10 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +38 -5
- package/src/panels/knowledge-panel.ts +1 -1
- package/src/panels/memory-panel.ts +1 -1
- package/src/panels/subscription-panel.ts +3 -3
- package/src/panels/system-messages-panel.ts +1 -1
- package/src/panels/tasks-panel.ts +1 -1
- package/src/renderer/agent-workspace.ts +2 -2
- package/src/renderer/help-overlay.ts +1 -1
- package/src/runtime/onboarding/apply.ts +61 -1
- package/src/runtime/onboarding/types.ts +6 -1
- package/src/runtime/onboarding/verify.ts +21 -1
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GoodVibes Agent will be recorded here.
|
|
4
4
|
|
|
5
|
+
## 0.1.76 - 2026-05-31
|
|
6
|
+
|
|
7
|
+
- Added optional starter profile creation to first-run onboarding so setup can create an isolated Agent home seeded with persona, skill, and routine templates.
|
|
8
|
+
- Added onboarding apply/verify support for Agent profile creation with prevalidation, rollback, and overwrite protection.
|
|
9
|
+
- Updated onboarding and renderer regressions so the first-run flow stays Agent-specific while exposing the starter profile path directly in the TUI.
|
|
10
|
+
|
|
11
|
+
## 0.1.75 - 2026-05-31
|
|
12
|
+
|
|
13
|
+
- Reworded Agent status, doctor, and external runtime diagnostics away from copied service/surface ownership language.
|
|
14
|
+
- Added `--delivery-channel` as the documented schedule promotion delivery flag while keeping prior delivery aliases as compatibility-only parser inputs.
|
|
15
|
+
- Cleaned Agent TUI help, workspace, and panel guidance so visible copy describes Agent workflows, channels, runtime endpoints, and external runtime connection boundaries.
|
|
16
|
+
|
|
5
17
|
## 0.1.74 - 2026-05-31
|
|
6
18
|
|
|
7
19
|
- Filtered checked-in foundation operator artifacts to Agent-relevant routes so host-specific knowledge segments are not documented in Agent artifacts.
|
package/README.md
CHANGED
|
@@ -74,14 +74,14 @@ Local Agent behavior is editable from the TUI:
|
|
|
74
74
|
/personas use research
|
|
75
75
|
/routines create --name "Evening Review" --description "Review open work before shutdown" --steps "Check work plan, approvals, and Agent Knowledge status before summarizing." --enabled true
|
|
76
76
|
/routines start evening-review
|
|
77
|
-
/schedule promote-routine evening-review --cron "0 17 * * 1-5" --timezone America/Chicago --delivery-
|
|
77
|
+
/schedule promote-routine evening-review --cron "0 17 * * 1-5" --timezone America/Chicago --delivery-channel slack --yes
|
|
78
78
|
/schedule receipts
|
|
79
79
|
/schedule reconcile
|
|
80
80
|
/agent-skills create --name "Morning Brief" --description "Daily briefing flow" --procedure "Check tasks, approvals, calendar, and unread state before summarizing." --enabled true
|
|
81
81
|
/skills local list
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Starting a routine records local usage and prints its steps; it does not spawn background agents or automation jobs. Promotion to an external schedule is separate and explicit: it calls the public `schedules.create` route only after `--yes`, can include explicit delivery targets such as `--delivery-
|
|
84
|
+
Starting a routine records local usage and prints its steps; it does not spawn background agents or automation jobs. Promotion to an external schedule is separate and explicit: it calls the public `schedules.create` route only after `--yes`, can include explicit delivery targets such as `--delivery-channel slack`, records a redacted local receipt, and the generated scheduled prompt keeps Agent Knowledge isolated from default Knowledge/Wiki and non-Agent knowledge segments. Use `/schedule reconcile` to compare those local receipts against live external schedules through public `schedules.list`.
|
|
85
85
|
|
|
86
86
|
## Runtime Prerequisite
|
|
87
87
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.76",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "GoodVibes personal operator assistant TUI with a proactive Agent product brain, isolated Agent Knowledge, local profiles, routines, skills, personas, and explicit build delegation.",
|
|
6
6
|
"type": "module",
|
|
@@ -37,10 +37,10 @@ export function isRoutineScheduleDeliverySurfaceKind(value: string): value is Ro
|
|
|
37
37
|
return DELIVERY_SURFACE_KINDS.includes(value as RoutineScheduleDeliverySurfaceKind);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function
|
|
40
|
+
function parseChannelDeliveryTarget(raw: string): RoutineScheduleDeliveryTargetSpec | string {
|
|
41
41
|
const [surfaceKind = '', routeId, label] = raw.split(':');
|
|
42
42
|
if (!isRoutineScheduleDeliverySurfaceKind(surfaceKind)) {
|
|
43
|
-
return `Unsupported delivery
|
|
43
|
+
return `Unsupported delivery channel "${surfaceKind}".`;
|
|
44
44
|
}
|
|
45
45
|
return {
|
|
46
46
|
kind: 'surface',
|
|
@@ -148,7 +148,10 @@ export function parseRoutineSchedulePromotionArgs(args: readonly string[]): Pars
|
|
|
148
148
|
};
|
|
149
149
|
continue;
|
|
150
150
|
}
|
|
151
|
-
if (
|
|
151
|
+
if (
|
|
152
|
+
optionName === '--delivery-channel'
|
|
153
|
+
|| optionName === '--deliver-channel'
|
|
154
|
+
) {
|
|
152
155
|
const consumed = optionValue(args, index, inlineValue);
|
|
153
156
|
index = consumed.nextIndex;
|
|
154
157
|
const value = consumed.value?.trim();
|
|
@@ -156,7 +159,7 @@ export function parseRoutineSchedulePromotionArgs(args: readonly string[]): Pars
|
|
|
156
159
|
errors.push(`${optionName} requires a value.`);
|
|
157
160
|
continue;
|
|
158
161
|
}
|
|
159
|
-
const target =
|
|
162
|
+
const target = parseChannelDeliveryTarget(value);
|
|
160
163
|
if (typeof target === 'string') errors.push(target);
|
|
161
164
|
else deliveryTargets.push(target);
|
|
162
165
|
continue;
|
|
@@ -18,6 +18,11 @@ function readString(record: Record<string, unknown>, key: string): string | null
|
|
|
18
18
|
return typeof value === 'string' ? value : null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
function formatDeliveryTargetKind(target: { readonly kind?: string; readonly surfaceKind?: string }): string {
|
|
22
|
+
if (target.kind === 'surface') return `channel${target.surfaceKind ? `/${target.surfaceKind}` : ''}`;
|
|
23
|
+
return `${target.kind ?? 'unknown'}${target.surfaceKind ? `/${target.surfaceKind}` : ''}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
export function formatRoutineSchedulePreview(preview: RoutineSchedulePromotionPreview): string {
|
|
22
27
|
const schedule = preview.payload.kind === 'cron'
|
|
23
28
|
? `${preview.payload.cron}${preview.payload.timezone ? ` [${preview.payload.timezone}]` : ''}`
|
|
@@ -90,9 +95,9 @@ export function formatRoutineScheduleReceipt(receipt: RoutineScheduleReceipt): s
|
|
|
90
95
|
` enabled: ${receipt.enabled ? 'yes' : 'no'}`,
|
|
91
96
|
receipt.provider ? ` provider: ${receipt.provider}` : '',
|
|
92
97
|
receipt.model ? ` model: ${receipt.model}` : '',
|
|
93
|
-
` target: ${receipt.target
|
|
98
|
+
` target: ${formatDeliveryTargetKind(receipt.target)}`,
|
|
94
99
|
receipt.deliveryMode ? ` delivery: ${receipt.deliveryMode}` : '',
|
|
95
|
-
...(receipt.deliveryTargets ?? []).map((target) => ` delivery target: ${target
|
|
100
|
+
...(receipt.deliveryTargets ?? []).map((target) => ` delivery target: ${formatDeliveryTargetKind(target)}${target.routeId ? ` route=${target.routeId}` : ''}${target.address ? ` address=${target.address}` : ''}${target.label ? ` label=${target.label}` : ''}`),
|
|
96
101
|
receipt.failureKind ? ` failure: ${receipt.failureKind}` : '',
|
|
97
102
|
receipt.failureError ? ` error: ${receipt.failureError}` : '',
|
|
98
103
|
].filter((line): line is string => Boolean(line)).join('\n');
|
package/src/cli/help.ts
CHANGED
|
@@ -152,7 +152,7 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
152
152
|
'routines receipts',
|
|
153
153
|
'routines reconcile',
|
|
154
154
|
'routines receipt <receipt-id>',
|
|
155
|
-
'routines promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--timezone <tz>] [--name <schedule-name>] [--provider <id>] [--model <model>] [--delivery-
|
|
155
|
+
'routines promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--timezone <tz>] [--name <schedule-name>] [--provider <id>] [--model <model>] [--delivery-channel <channel[:route[:label]]>|--delivery-route <route[:label]>|--delivery-webhook <url>|--delivery-link <url>] [--disabled] --yes',
|
|
156
156
|
],
|
|
157
157
|
summary: 'Inspect Agent-local routines, review local promotion receipts, reconcile receipts against live external schedules, and explicitly promote a reviewed routine into a GoodVibes schedule. Without --yes, promote only prints the schedules.create preview.',
|
|
158
158
|
examples: [
|
|
@@ -160,7 +160,7 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
160
160
|
'routines show daily-operations-sweep',
|
|
161
161
|
'routines receipts',
|
|
162
162
|
'routines reconcile',
|
|
163
|
-
'routines promote daily-operations-sweep --cron "0 9 * * *" --timezone America/Chicago --delivery-
|
|
163
|
+
'routines promote daily-operations-sweep --cron "0 9 * * *" --timezone America/Chicago --delivery-channel slack --yes',
|
|
164
164
|
'routines promote weekly-review --every 7d --disabled',
|
|
165
165
|
],
|
|
166
166
|
},
|
|
@@ -277,7 +277,7 @@ export async function handleTasks(runtime: CliCommandRuntime): Promise<string> {
|
|
|
277
277
|
const [sub = 'list', ...rest] = runtime.cli.commandArgs;
|
|
278
278
|
if (sub === 'submit') {
|
|
279
279
|
return [
|
|
280
|
-
'GoodVibes Agent blocks CLI task submission from the copied task
|
|
280
|
+
'GoodVibes Agent blocks CLI task submission from the copied task workflow.',
|
|
281
281
|
' policy: do normal assistant work in the main Agent conversation or use `goodvibes-agent run <prompt>` for an explicit one-shot run.',
|
|
282
282
|
' build/fix/review: use `goodvibes-agent delegate <task>` for explicit GoodVibes TUI handoff.',
|
|
283
283
|
' result: no local task was started.',
|
|
@@ -104,7 +104,7 @@ async function handleRoutinePromotion(runtime: CliCommandRuntime, args: readonly
|
|
|
104
104
|
};
|
|
105
105
|
return {
|
|
106
106
|
output: json ? JSON.stringify(failure, null, 2) : [
|
|
107
|
-
'Usage: goodvibes-agent routines promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--timezone <tz>] [--name <schedule-name>] [--provider <id>] [--model <model>] [--delivery-
|
|
107
|
+
'Usage: goodvibes-agent routines promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--timezone <tz>] [--name <schedule-name>] [--provider <id>] [--model <model>] [--delivery-channel <channel[:route[:label]]>|--delivery-route <route[:label]>|--delivery-webhook <url>|--delivery-link <url>] [--disabled] --yes',
|
|
108
108
|
...parsed.errors.map((error) => ` ${error}`),
|
|
109
109
|
].join('\n'),
|
|
110
110
|
exitCode: 2,
|
|
@@ -241,7 +241,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
|
|
|
241
241
|
return handleRoutinePromotion(runtime, rest);
|
|
242
242
|
}
|
|
243
243
|
return {
|
|
244
|
-
output: 'Usage: goodvibes-agent routines [list|enabled|show <id>|receipts|reconcile|receipt <id>|promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--delivery-
|
|
244
|
+
output: 'Usage: goodvibes-agent routines [list|enabled|show <id>|receipts|reconcile|receipt <id>|promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--delivery-channel <channel>|--delivery-route <route>|--delivery-webhook <url>] --yes]',
|
|
245
245
|
exitCode: 2,
|
|
246
246
|
};
|
|
247
247
|
}
|
|
@@ -62,8 +62,8 @@ export interface CliServicePosture {
|
|
|
62
62
|
|
|
63
63
|
const ENDPOINTS: readonly { readonly id: RuntimeEndpointId; readonly label: string; readonly enabledKey: string }[] = [
|
|
64
64
|
{ id: 'controlPlane', label: 'runtime API', enabledKey: 'controlPlane.enabled' },
|
|
65
|
-
{ id: 'httpListener', label: '
|
|
66
|
-
{ id: 'web', label: '
|
|
65
|
+
{ id: 'httpListener', label: 'incoming webhook listener', enabledKey: 'danger.httpListener' },
|
|
66
|
+
{ id: 'web', label: 'browser companion', enabledKey: 'web.enabled' },
|
|
67
67
|
];
|
|
68
68
|
|
|
69
69
|
interface CliServicePostureOptions {
|
|
@@ -184,13 +184,13 @@ export async function buildCliServicePosture(
|
|
|
184
184
|
const issues: string[] = [];
|
|
185
185
|
|
|
186
186
|
if (serverBackedEnabled && !config.enabled) {
|
|
187
|
-
issues.push('
|
|
187
|
+
issues.push('External runtime endpoints are configured, but Agent runtime ownership is disabled by design.');
|
|
188
188
|
}
|
|
189
189
|
if (config.enabled && !config.autostart) {
|
|
190
|
-
issues.push('External runtime
|
|
190
|
+
issues.push('External runtime host config has autostart off.');
|
|
191
191
|
}
|
|
192
192
|
if (config.enabled && !config.restartOnFailure) {
|
|
193
|
-
issues.push('External runtime
|
|
193
|
+
issues.push('External runtime host config has restart-on-failure off.');
|
|
194
194
|
}
|
|
195
195
|
for (const endpoint of endpoints) {
|
|
196
196
|
if (endpoint.enabled && options.probe && endpoint.reachable === false) {
|
|
@@ -230,15 +230,15 @@ export function formatCliServicePosture(posture: CliServicePosture, json = false
|
|
|
230
230
|
if (json) return JSON.stringify(posture, null, 2);
|
|
231
231
|
return [
|
|
232
232
|
'GoodVibes external runtime diagnostics',
|
|
233
|
-
'
|
|
234
|
-
`
|
|
233
|
+
' ownership: managed outside goodvibes-agent',
|
|
234
|
+
` host config enabled: ${yesNo(posture.config.enabled)}`,
|
|
235
235
|
` autostart config: ${yesNo(posture.config.autostart)}`,
|
|
236
236
|
` restartOnFailure config: ${yesNo(posture.config.restartOnFailure)}`,
|
|
237
237
|
` runtime host flag: ${yesNo(posture.config.daemonEnabled)}`,
|
|
238
238
|
` log: ${posture.log.path ?? 'n/a'} (${posture.log.exists ? 'present' : 'missing'})`,
|
|
239
239
|
...(posture.log.readError ? [` log read error: ${posture.log.readError}`] : []),
|
|
240
240
|
'',
|
|
241
|
-
'
|
|
241
|
+
'Runtime endpoints:',
|
|
242
242
|
...posture.endpoints.map((endpoint) =>
|
|
243
243
|
` ${endpoint.label}: enabled=${yesNo(endpoint.enabled)} ${endpoint.binding.hostMode} ${endpoint.binding.host}:${endpoint.binding.port} posture=${endpoint.bindPosture.label}${endpoint.reachable === undefined ? '' : ` reachable=${yesNo(endpoint.reachable)}`}`,
|
|
244
244
|
),
|
package/src/cli/status.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface CliAuthStatus {
|
|
|
28
28
|
|
|
29
29
|
export interface CliDoctorFinding {
|
|
30
30
|
readonly id: string;
|
|
31
|
-
readonly area: 'auth' | 'network' | 'onboarding' | '
|
|
31
|
+
readonly area: 'auth' | 'network' | 'onboarding' | 'runtime' | 'security' | 'secrets';
|
|
32
32
|
readonly severity: 'warning' | 'risk';
|
|
33
33
|
readonly summary: string;
|
|
34
34
|
readonly cause: string;
|
|
@@ -52,13 +52,13 @@ export interface CliStatusSnapshot {
|
|
|
52
52
|
readonly secretPolicyLabel: string;
|
|
53
53
|
readonly localUsers: CliAuthStatus | null;
|
|
54
54
|
};
|
|
55
|
-
readonly
|
|
55
|
+
readonly runtimeConnection: {
|
|
56
56
|
readonly enabled: unknown;
|
|
57
57
|
readonly autostart: unknown;
|
|
58
58
|
readonly restartOnFailure: unknown;
|
|
59
59
|
readonly lifecycle?: CliServicePosture;
|
|
60
60
|
};
|
|
61
|
-
readonly
|
|
61
|
+
readonly runtimeEndpoints: {
|
|
62
62
|
readonly controlPlane: ReturnType<typeof resolveRuntimeEndpointBinding> & { readonly enabled: unknown };
|
|
63
63
|
readonly httpListener: ReturnType<typeof resolveRuntimeEndpointBinding> & { readonly enabled: unknown };
|
|
64
64
|
readonly web: ReturnType<typeof resolveRuntimeEndpointBinding> & { readonly enabled: unknown };
|
|
@@ -111,30 +111,30 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
111
111
|
const serverBackedEnabled = daemonEnabled || controlPlaneEnabled || listenerEnabled || webEnabled;
|
|
112
112
|
const networkFacingSurfaces = [
|
|
113
113
|
['runtime API', controlPlaneEnabled, controlPlaneBinding],
|
|
114
|
-
['
|
|
115
|
-
['
|
|
114
|
+
['incoming webhook listener', listenerEnabled, httpListenerBinding],
|
|
115
|
+
['browser companion', webEnabled, webBinding],
|
|
116
116
|
].filter(([, enabled, binding]) => isNetworkFacing(enabled, binding as typeof controlPlaneBinding));
|
|
117
117
|
|
|
118
118
|
const findings: CliDoctorFinding[] = [];
|
|
119
119
|
|
|
120
120
|
if (serverBackedEnabled && !serviceEnabled) {
|
|
121
121
|
findings.push({
|
|
122
|
-
id: '
|
|
123
|
-
area: '
|
|
122
|
+
id: 'runtime-ownership-external',
|
|
123
|
+
area: 'runtime',
|
|
124
124
|
severity: 'warning',
|
|
125
|
-
summary: '
|
|
125
|
+
summary: 'External runtime endpoints are configured while Agent runtime ownership is disabled by design.',
|
|
126
126
|
cause: 'One or more runtime API, listener, or web settings are enabled while service.enabled is false.',
|
|
127
|
-
impact: 'The external GoodVibes runtime must own availability for those
|
|
128
|
-
action: 'Manage
|
|
127
|
+
impact: 'The external GoodVibes runtime must own availability for those endpoints; Agent will not start or enable them.',
|
|
128
|
+
action: 'Manage runtime availability from GoodVibes TUI or the owning host, then use Agent for read-only diagnostics.',
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
if (serviceEnabled && !serviceAutostart) {
|
|
133
133
|
findings.push({
|
|
134
|
-
id: '
|
|
135
|
-
area: '
|
|
134
|
+
id: 'runtime-autostart-disabled',
|
|
135
|
+
area: 'runtime',
|
|
136
136
|
severity: 'warning',
|
|
137
|
-
summary: 'External runtime
|
|
137
|
+
summary: 'External runtime host config has autostart off.',
|
|
138
138
|
cause: 'service.enabled is true and service.autostart is false.',
|
|
139
139
|
impact: 'The external GoodVibes runtime may not be available after login or reboot even though host-managed startup is selected.',
|
|
140
140
|
action: 'Configure autostart from GoodVibes TUI or the owning host; Agent will not mutate this setting.',
|
|
@@ -143,10 +143,10 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
143
143
|
|
|
144
144
|
if (serviceEnabled && !restartOnFailure) {
|
|
145
145
|
findings.push({
|
|
146
|
-
id: '
|
|
147
|
-
area: '
|
|
146
|
+
id: 'runtime-restart-disabled',
|
|
147
|
+
area: 'runtime',
|
|
148
148
|
severity: 'warning',
|
|
149
|
-
summary: 'External runtime
|
|
149
|
+
summary: 'External runtime host config has restart-on-failure off.',
|
|
150
150
|
cause: 'service.enabled is true and service.restartOnFailure is false.',
|
|
151
151
|
impact: 'A crashed runtime or listener may stay down until manually restarted.',
|
|
152
152
|
action: 'Configure restart-on-failure from GoodVibes TUI or the owning host; Agent will not mutate this setting.',
|
|
@@ -157,11 +157,11 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
157
157
|
for (const issue of options.service.issues) {
|
|
158
158
|
if (findings.some((finding) => finding.summary === issue)) continue;
|
|
159
159
|
findings.push({
|
|
160
|
-
id: `
|
|
161
|
-
area: '
|
|
160
|
+
id: `runtime-connection-${findings.length}`,
|
|
161
|
+
area: 'runtime',
|
|
162
162
|
severity: 'warning',
|
|
163
163
|
summary: issue,
|
|
164
|
-
cause: 'The
|
|
164
|
+
cause: 'The runtime connection inspection found a mismatch between configured endpoint state and observed host state.',
|
|
165
165
|
impact: 'Runtime API, listener, or web availability may not match the configuration.',
|
|
166
166
|
action: 'Use Agent status and doctor diagnostics here, then manage the runtime from GoodVibes TUI or your host tooling.',
|
|
167
167
|
});
|
|
@@ -175,17 +175,17 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
175
175
|
severity: 'warning',
|
|
176
176
|
summary: 'Onboarding has not been shown for this user.',
|
|
177
177
|
cause: 'No global user onboarding check marker was found.',
|
|
178
|
-
impact: 'Important
|
|
178
|
+
impact: 'Important runtime, network, provider, auth, or permission choices may still be implicit defaults.',
|
|
179
179
|
action: 'Run /onboarding in GoodVibes Agent or goodvibes-agent onboarding status to review setup state.',
|
|
180
180
|
});
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
if (networkFacingSurfaces.length > 0 && options.auth?.userStorePresent !== true) {
|
|
184
184
|
findings.push({
|
|
185
|
-
id: 'network-
|
|
185
|
+
id: 'network-endpoint-without-local-users',
|
|
186
186
|
area: 'auth',
|
|
187
187
|
severity: 'risk',
|
|
188
|
-
summary: 'Network-facing
|
|
188
|
+
summary: 'Network-facing runtime endpoints are enabled before local users are configured.',
|
|
189
189
|
cause: `${networkFacingSurfaces.map(([name]) => name).join(', ')} are LAN/custom-bound, but no local auth user store was found.`,
|
|
190
190
|
impact: 'Remote access paths may be unusable or unsafe until local admin auth is configured.',
|
|
191
191
|
action: 'Create/verify a local admin user before exposing GoodVibes on the network.',
|
|
@@ -194,7 +194,7 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
194
194
|
|
|
195
195
|
if (networkFacingSurfaces.length > 0 && options.auth?.bootstrapCredentialPresent === true) {
|
|
196
196
|
findings.push({
|
|
197
|
-
id: 'network-
|
|
197
|
+
id: 'network-endpoint-with-bootstrap-credential',
|
|
198
198
|
area: 'auth',
|
|
199
199
|
severity: 'risk',
|
|
200
200
|
summary: 'A bootstrap credential is still present while network-facing surfaces are enabled.',
|
|
@@ -223,7 +223,7 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
223
223
|
severity: 'risk',
|
|
224
224
|
summary: 'Plaintext secret storage is allowed.',
|
|
225
225
|
cause: 'storage.secretPolicy is plaintext_allowed.',
|
|
226
|
-
impact: 'Provider keys and
|
|
226
|
+
impact: 'Provider keys and channel tokens may be stored without secure backend protection.',
|
|
227
227
|
action: 'Use Require secure storage or Use secure storage when available for normal operation.',
|
|
228
228
|
});
|
|
229
229
|
}
|
|
@@ -233,10 +233,10 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
233
233
|
id: 'network-http-listener-enabled',
|
|
234
234
|
area: 'network',
|
|
235
235
|
severity: 'warning',
|
|
236
|
-
summary: 'The
|
|
237
|
-
cause: `
|
|
236
|
+
summary: 'The incoming webhook listener is reachable beyond loopback.',
|
|
237
|
+
cause: `Incoming webhook listener is enabled on ${httpListenerBinding.host}:${httpListenerBinding.port} with ${httpListenerBinding.hostMode} binding.`,
|
|
238
238
|
impact: 'External tools and devices may be able to reach incoming event endpoints.',
|
|
239
|
-
action: 'Keep listener secrets/signature checks configured for every enabled webhook
|
|
239
|
+
action: 'Keep listener secrets/signature checks configured for every enabled webhook endpoint.',
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
242
|
|
|
@@ -266,13 +266,13 @@ export function buildCliStatusSnapshot(options: CliStatusOptions): CliStatusSnap
|
|
|
266
266
|
secretPolicyLabel: secretPolicyLabel(config.get('storage.secretPolicy')),
|
|
267
267
|
localUsers: options.auth ?? null,
|
|
268
268
|
},
|
|
269
|
-
|
|
269
|
+
runtimeConnection: {
|
|
270
270
|
enabled: config.get('service.enabled'),
|
|
271
271
|
autostart: config.get('service.autostart'),
|
|
272
272
|
restartOnFailure: config.get('service.restartOnFailure'),
|
|
273
273
|
...(options.service ? { lifecycle: options.service } : {}),
|
|
274
274
|
},
|
|
275
|
-
|
|
275
|
+
runtimeEndpoints: {
|
|
276
276
|
controlPlane: { enabled: config.get('controlPlane.enabled'), ...controlPlaneBinding },
|
|
277
277
|
httpListener: { enabled: config.get('danger.httpListener'), ...httpListenerBinding },
|
|
278
278
|
web: { enabled: config.get('web.enabled'), ...webBinding },
|
|
@@ -289,15 +289,15 @@ export function buildCliStatusSnapshot(options: CliStatusOptions): CliStatusSnap
|
|
|
289
289
|
export function renderCliStatus(options: CliStatusOptions): string {
|
|
290
290
|
const config = options.configManager;
|
|
291
291
|
const snapshot = buildCliStatusSnapshot(options);
|
|
292
|
-
const serviceEnabled = snapshot.
|
|
293
|
-
const serviceAutostart = snapshot.
|
|
294
|
-
const restartOnFailure = snapshot.
|
|
295
|
-
const controlPlaneEnabled = snapshot.
|
|
296
|
-
const listenerEnabled = snapshot.
|
|
297
|
-
const webEnabled = snapshot.
|
|
298
|
-
const controlPlaneBinding = snapshot.
|
|
299
|
-
const httpListenerBinding = snapshot.
|
|
300
|
-
const webBinding = snapshot.
|
|
292
|
+
const serviceEnabled = snapshot.runtimeConnection.enabled;
|
|
293
|
+
const serviceAutostart = snapshot.runtimeConnection.autostart;
|
|
294
|
+
const restartOnFailure = snapshot.runtimeConnection.restartOnFailure;
|
|
295
|
+
const controlPlaneEnabled = snapshot.runtimeEndpoints.controlPlane.enabled;
|
|
296
|
+
const listenerEnabled = snapshot.runtimeEndpoints.httpListener.enabled;
|
|
297
|
+
const webEnabled = snapshot.runtimeEndpoints.web.enabled;
|
|
298
|
+
const controlPlaneBinding = snapshot.runtimeEndpoints.controlPlane;
|
|
299
|
+
const httpListenerBinding = snapshot.runtimeEndpoints.httpListener;
|
|
300
|
+
const webBinding = snapshot.runtimeEndpoints.web;
|
|
301
301
|
const marker = options.onboardingMarkers?.effective;
|
|
302
302
|
const findings = snapshot.findings;
|
|
303
303
|
|
|
@@ -326,10 +326,10 @@ export function renderCliStatus(options: CliStatusOptions): string {
|
|
|
326
326
|
? ` operatorTokens: ${options.auth.operatorTokenPresent ? 'present' : 'missing'} (${options.auth.operatorTokenPath})`
|
|
327
327
|
: ' operatorTokens: unknown',
|
|
328
328
|
'',
|
|
329
|
-
'External Runtime
|
|
330
|
-
`
|
|
331
|
-
`
|
|
332
|
-
`
|
|
329
|
+
'External Runtime Connection:',
|
|
330
|
+
` hostConfigEnabled: ${yesNo(serviceEnabled)}`,
|
|
331
|
+
` hostAutostart: ${yesNo(serviceAutostart)}`,
|
|
332
|
+
` hostRestartOnFailure: ${yesNo(restartOnFailure)}`,
|
|
333
333
|
...(options.service ? [
|
|
334
334
|
` platform: ${options.service.managed.platform}`,
|
|
335
335
|
` installed: ${yesNo(options.service.managed.installed)}`,
|
|
@@ -339,9 +339,9 @@ export function renderCliStatus(options: CliStatusOptions): string {
|
|
|
339
339
|
] : []),
|
|
340
340
|
'',
|
|
341
341
|
'Runtime Endpoints:',
|
|
342
|
-
bindLine('
|
|
343
|
-
bindLine('
|
|
344
|
-
bindLine('
|
|
342
|
+
bindLine('runtimeApi', controlPlaneEnabled, controlPlaneBinding),
|
|
343
|
+
bindLine('incomingWebhook', listenerEnabled, httpListenerBinding),
|
|
344
|
+
bindLine('browserCompanion', webEnabled, webBinding),
|
|
345
345
|
'',
|
|
346
346
|
'Onboarding:',
|
|
347
347
|
` checked: ${marker?.exists ? 'yes' : 'no'}`,
|
|
@@ -196,7 +196,7 @@ export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
|
|
|
196
196
|
detail: 'Agent does not create local automation jobs or hidden scheduler spawns. Reviewed local routines can be promoted into external schedules only through an explicit schedules.create command with --yes, optional delivery targets, and a redacted local receipt.',
|
|
197
197
|
actions: [
|
|
198
198
|
{ id: 'schedule-list', label: 'List schedules', detail: 'Inspect configured jobs and history without running or mutating them.', command: '/schedule list', kind: 'command', safety: 'read-only' },
|
|
199
|
-
{ id: 'schedule-promote-routine', label: 'Promote routine', detail: 'Create an external schedule from a local Agent routine. Requires a real routine id, schedule expression, optional delivery target, and explicit --yes.', command: '/schedule promote-routine <routine-id> --cron <expr> [--delivery-
|
|
199
|
+
{ id: 'schedule-promote-routine', label: 'Promote routine', detail: 'Create an external schedule from a local Agent routine. Requires a real routine id, schedule expression, optional delivery target, and explicit --yes.', command: '/schedule promote-routine <routine-id> --cron <expr> [--delivery-channel slack] --yes', kind: 'command', safety: 'safe' },
|
|
200
200
|
{ id: 'schedule-receipts', label: 'Promotion receipts', detail: 'Review local redacted receipt history for routine-to-schedule promotion attempts.', command: '/schedule receipts', kind: 'command', safety: 'read-only' },
|
|
201
201
|
{ id: 'schedule-reconcile', label: 'Reconcile schedules', detail: 'Compare local promotion receipts with live external schedules using schedules.list.', command: '/schedule reconcile', kind: 'command', safety: 'read-only' },
|
|
202
202
|
{ id: 'schedule-policy', label: 'Local scheduler blocked', detail: 'Local schedule add/run/remove/enable/disable remain blocked; only explicit external schedule promotion is allowed here.', kind: 'guidance', safety: 'blocked' },
|
|
@@ -64,7 +64,7 @@ export function registerExperienceRuntimeCommands(registry: CommandRegistry): vo
|
|
|
64
64
|
ctx.print([
|
|
65
65
|
`Approval Review: ${entry[0]}`,
|
|
66
66
|
` ${entry[1]}`,
|
|
67
|
-
' Related
|
|
67
|
+
' Related workspaces: /security, /policy preflight, /trust, /mcp',
|
|
68
68
|
].join('\n'));
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
@@ -74,7 +74,7 @@ export function registerExperienceRuntimeCommands(registry: CommandRegistry): vo
|
|
|
74
74
|
|
|
75
75
|
registry.register({
|
|
76
76
|
name: 'voice',
|
|
77
|
-
description: 'Review voice posture and package portable voice
|
|
77
|
+
description: 'Review voice posture and package portable voice interaction metadata',
|
|
78
78
|
usage: '[review|enable --yes|disable --yes|bundle export <path> --yes|bundle inspect <path>]',
|
|
79
79
|
handler(args, ctx) {
|
|
80
80
|
const parsed = stripYesFlag(args);
|
|
@@ -86,19 +86,19 @@ export function registerExperienceRuntimeCommands(registry: CommandRegistry): vo
|
|
|
86
86
|
ctx.print([
|
|
87
87
|
'Voice Review',
|
|
88
88
|
` enabled: ${enabled ? 'yes' : 'no'}`,
|
|
89
|
-
' posture: optional local companion
|
|
89
|
+
' posture: optional local companion interaction; disabled by default',
|
|
90
90
|
' note: voice remains an optional operator convenience, not a required SaaS dependency',
|
|
91
91
|
].join('\n'));
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
if (sub === 'enable' || sub === 'disable') {
|
|
95
95
|
if (!parsed.yes) {
|
|
96
|
-
requireYesFlag(ctx, `${sub} voice
|
|
96
|
+
requireYesFlag(ctx, `${sub} voice interaction`, `/voice ${sub} --yes`);
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
const next = sub === 'enable';
|
|
100
100
|
ctx.platform.configManager.setDynamic('ui.voiceEnabled', next);
|
|
101
|
-
ctx.print(`Voice
|
|
101
|
+
ctx.print(`Voice interaction ${next ? 'enabled' : 'disabled'} for this runtime.`);
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
if (sub === 'bundle') {
|
|
@@ -120,7 +120,7 @@ export function registerExperienceRuntimeCommands(registry: CommandRegistry): vo
|
|
|
120
120
|
enabled: Boolean(ctx.platform.configManager.get('ui.voiceEnabled')),
|
|
121
121
|
notes: [
|
|
122
122
|
'Voice is optional and local-first.',
|
|
123
|
-
'Operator review remains the primary
|
|
123
|
+
'Operator review remains the primary review path for risky actions.',
|
|
124
124
|
],
|
|
125
125
|
};
|
|
126
126
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
@@ -229,7 +229,7 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
|
|
|
229
229
|
+ ' /mode — show current mode and settings\n'
|
|
230
230
|
+ ' /mode show — show current mode and settings\n'
|
|
231
231
|
+ ' /mode quiet --yes — suppress all non-critical notifications\n'
|
|
232
|
-
+ ' /mode balanced --yes —
|
|
232
|
+
+ ' /mode balanced --yes — show warnings, batch info noise (default)\n'
|
|
233
233
|
+ ' /mode operator --yes — full verbosity, no suppression\n'
|
|
234
234
|
+ ' /mode set-domain <d> <v> --yes — per-domain verbosity override (minimal|normal|verbose)'
|
|
235
235
|
);
|
|
@@ -299,7 +299,7 @@ export function registerRoutinesRuntimeCommands(registry: CommandRegistry): void
|
|
|
299
299
|
name: 'routines',
|
|
300
300
|
aliases: ['routine'],
|
|
301
301
|
description: 'Manage local GoodVibes Agent routines',
|
|
302
|
-
usage: '[list|enabled|search <query>|show <id>|receipts|reconcile|receipt <id>|create --name <name> --description <summary> --steps <steps>|update <id> [--name ...] [--description ...] [--steps ...]|enable <id>|disable <id>|start <id>|review <id>|stale <id> <reason...>|promote <id> --cron <expr> [--delivery-
|
|
302
|
+
usage: '[list|enabled|search <query>|show <id>|receipts|reconcile|receipt <id>|create --name <name> --description <summary> --steps <steps>|update <id> [--name ...] [--description ...] [--steps ...]|enable <id>|disable <id>|start <id>|review <id>|stale <id> <reason...>|promote <id> --cron <expr> [--delivery-channel slack] --yes|delete <id> --yes]',
|
|
303
303
|
handler: runRoutinesRuntimeCommand,
|
|
304
304
|
});
|
|
305
305
|
}
|
|
@@ -64,7 +64,7 @@ async function promoteRoutineSchedule(args: readonly string[], ctx: CommandConte
|
|
|
64
64
|
const parsed = parseRoutineSchedulePromotionArgs(args);
|
|
65
65
|
if (parsed.errors.length > 0) {
|
|
66
66
|
ctx.print([
|
|
67
|
-
'Usage: /schedule promote-routine <routine-id> (--cron <expr>|--every <interval>|--at <iso-time>) [--timezone <tz>] [--name <schedule-name>] [--provider <id>] [--model <model>] [--delivery-
|
|
67
|
+
'Usage: /schedule promote-routine <routine-id> (--cron <expr>|--every <interval>|--at <iso-time>) [--timezone <tz>] [--name <schedule-name>] [--provider <id>] [--model <model>] [--delivery-channel <channel[:route[:label]]>|--delivery-route <route[:label]>|--delivery-webhook <url>|--delivery-link <url>] [--disabled] --yes',
|
|
68
68
|
...parsed.errors.map((error) => ` ${error}`),
|
|
69
69
|
].join('\n'));
|
|
70
70
|
return;
|
|
@@ -91,8 +91,8 @@ export function registerScheduleRuntimeCommands(registry: CommandRegistry): void
|
|
|
91
91
|
name: 'schedule',
|
|
92
92
|
aliases: ['sched'],
|
|
93
93
|
description: 'Inspect schedules and explicitly promote local Agent routines to external schedules',
|
|
94
|
-
usage: 'list | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-
|
|
95
|
-
argsHint: 'list | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-
|
|
94
|
+
usage: 'list | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-channel slack] --yes',
|
|
95
|
+
argsHint: 'list | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-channel slack] --yes',
|
|
96
96
|
async handler(args, ctx) {
|
|
97
97
|
const sub = args[0];
|
|
98
98
|
|
|
@@ -164,7 +164,7 @@ export function registerScheduleRuntimeCommands(registry: CommandRegistry): void
|
|
|
164
164
|
+ ' /schedule receipts\n'
|
|
165
165
|
+ ' /schedule reconcile\n'
|
|
166
166
|
+ ' /schedule receipt <receipt-id>\n'
|
|
167
|
-
+ ' /schedule promote-routine <routine-id> (--cron <expr>|--every <interval>|--at <iso-time>) [--delivery-
|
|
167
|
+
+ ' /schedule promote-routine <routine-id> (--cron <expr>|--every <interval>|--at <iso-time>) [--delivery-channel <channel>|--delivery-route <route>|--delivery-webhook <url>] --yes\n'
|
|
168
168
|
+ ' Local schedule mutations and runs remain blocked.'
|
|
169
169
|
);
|
|
170
170
|
},
|
|
@@ -16,7 +16,7 @@ const BLOCKED_TASK_MUTATIONS: ReadonlySet<string> = new Set([
|
|
|
16
16
|
function printTaskMutationBlocked(print: (text: string) => void, subcommand: string): void {
|
|
17
17
|
print([
|
|
18
18
|
`Task mutation "${subcommand}" is blocked in GoodVibes Agent.`,
|
|
19
|
-
' policy: runtime tasks are read-only from the Agent
|
|
19
|
+
' policy: runtime tasks are read-only from the Agent TUI; normal work stays in the main conversation.',
|
|
20
20
|
' durable tasks: use /workplan for visible planning and task tracking.',
|
|
21
21
|
' build/fix/review: use /delegate <task> to hand explicit implementation work to GoodVibes TUI.',
|
|
22
22
|
' result: no local runtime task state was changed.',
|
|
@@ -38,6 +38,16 @@ export function buildOnboardingApplyRequest(controller: OnboardingWizardControll
|
|
|
38
38
|
|
|
39
39
|
setSecret('OPENAI_API_KEY', controller.getStringFieldValue('providers.openai-api-key', ''));
|
|
40
40
|
|
|
41
|
+
const profileName = controller.getStringFieldValue('agent-setup.profile-name', '').trim();
|
|
42
|
+
if (profileName.length > 0) {
|
|
43
|
+
const selectedTemplate = controller.getStringFieldValue('agent-setup.profile-template', 'none').trim();
|
|
44
|
+
operations.push({
|
|
45
|
+
kind: 'create-agent-profile',
|
|
46
|
+
name: profileName,
|
|
47
|
+
...(selectedTemplate.length > 0 && selectedTemplate !== 'none' ? { templateId: selectedTemplate } : {}),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
return {
|
|
42
52
|
mode: controller.mode,
|
|
43
53
|
source: 'wizard',
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { REASONING_OPTIONS, HITL_MODE_OPTIONS, GUIDANCE_MODE_OPTIONS, PERMISSION_MODE_OPTIONS, SECRET_POLICY_OPTIONS } from './onboarding-wizard-constants.ts';
|
|
2
2
|
import { modelSelectionLabel, normalizeText } from './onboarding-wizard-helpers.ts';
|
|
3
|
+
import { listAgentRuntimeProfileTemplates } from '../../agent/runtime-profile.ts';
|
|
3
4
|
import type { OnboardingWizardController } from './onboarding-wizard.ts';
|
|
4
|
-
import type { OnboardingWizardActionFieldDefinition, OnboardingWizardFieldDefinition, OnboardingWizardModelPickerFieldDefinition, OnboardingWizardRadioFieldDefinition, OnboardingWizardStepDefinition } from './onboarding-wizard-types.ts';
|
|
5
|
+
import type { OnboardingWizardActionFieldDefinition, OnboardingWizardFieldDefinition, OnboardingWizardModelPickerFieldDefinition, OnboardingWizardRadioFieldDefinition, OnboardingWizardRadioOption, OnboardingWizardStepDefinition } from './onboarding-wizard-types.ts';
|
|
6
|
+
|
|
7
|
+
function buildStarterTemplateOptions(): readonly OnboardingWizardRadioOption[] {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
id: 'none',
|
|
11
|
+
label: 'No profile',
|
|
12
|
+
hint: 'Keep using the current Agent home. You can create isolated profiles later from the Agent workspace.',
|
|
13
|
+
},
|
|
14
|
+
...listAgentRuntimeProfileTemplates().map((template): OnboardingWizardRadioOption => ({
|
|
15
|
+
id: template.id,
|
|
16
|
+
label: template.name,
|
|
17
|
+
hint: `${template.description} Includes persona ${template.personaName}, skills ${template.skillNames.join(', ')}, and routines ${template.routineNames.join(', ')}.`,
|
|
18
|
+
})),
|
|
19
|
+
];
|
|
20
|
+
}
|
|
5
21
|
|
|
6
22
|
export function buildOnboardingWizardSteps(controller: OnboardingWizardController): readonly OnboardingWizardStepDefinition[] {
|
|
7
23
|
if (controller.hydrationPending || controller.hydrationError !== null) return [buildLoadingStep(controller)];
|
|
@@ -125,7 +141,7 @@ export function buildToolsStep(): OnboardingWizardStepDefinition {
|
|
|
125
141
|
kind: 'status',
|
|
126
142
|
id: 'agent-tools.no-hidden-work',
|
|
127
143
|
label: 'Hidden work policy',
|
|
128
|
-
hint: 'Tool use stays visible in the main Agent conversation or explicit command
|
|
144
|
+
hint: 'Tool use stays visible in the main Agent conversation or explicit command workspace; no hidden background work is started from onboarding.',
|
|
129
145
|
defaultValue: 'Visible',
|
|
130
146
|
},
|
|
131
147
|
],
|
|
@@ -169,6 +185,7 @@ export function buildAgentSetupStep(controller: OnboardingWizardController): Onb
|
|
|
169
185
|
summaryTitle: 'Agent setup posture',
|
|
170
186
|
summaryLines: [
|
|
171
187
|
'Agent owns the operator TUI and local behavior registry.',
|
|
188
|
+
'Optional starter profile: create an isolated Agent home from setup.',
|
|
172
189
|
'GoodVibes runtime lifecycle is external to this product.',
|
|
173
190
|
`Secret policy: ${controller.getStringFieldValue('agent-setup.secret-policy', secretPolicy)}`,
|
|
174
191
|
collectionIssues > 0 ? `${collectionIssues} setup snapshot issue(s)` : 'Setup snapshot collected cleanly',
|
|
@@ -198,12 +215,28 @@ export function buildAgentSetupStep(controller: OnboardingWizardController): Onb
|
|
|
198
215
|
options: SECRET_POLICY_OPTIONS,
|
|
199
216
|
defaultValue: secretPolicy,
|
|
200
217
|
},
|
|
218
|
+
{
|
|
219
|
+
kind: 'text',
|
|
220
|
+
id: 'agent-setup.profile-name',
|
|
221
|
+
label: 'Create starter profile',
|
|
222
|
+
hint: 'Optional: enter a new Agent profile name to create an isolated home during setup. Leave blank to keep the current home.',
|
|
223
|
+
placeholder: 'research-desk',
|
|
224
|
+
defaultValue: '',
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
kind: 'radio',
|
|
228
|
+
id: 'agent-setup.profile-template',
|
|
229
|
+
label: 'Starter profile template',
|
|
230
|
+
hint: 'Choose the persona, skills, and routine bundle to seed into the optional new Agent profile.',
|
|
231
|
+
options: buildStarterTemplateOptions(),
|
|
232
|
+
defaultValue: 'none',
|
|
233
|
+
},
|
|
201
234
|
{
|
|
202
235
|
kind: 'status',
|
|
203
236
|
id: 'agent-setup.profile-guide',
|
|
204
|
-
label: '
|
|
205
|
-
hint: 'Use /agent-profile guide after setup to
|
|
206
|
-
defaultValue: '
|
|
237
|
+
label: 'Profile guidance',
|
|
238
|
+
hint: 'Use /agent-profile guide after setup to export, customize, import, and launch Agent profiles.',
|
|
239
|
+
defaultValue: 'Available',
|
|
207
240
|
},
|
|
208
241
|
],
|
|
209
242
|
};
|
|
@@ -335,7 +335,7 @@ export class KnowledgePanel extends ScrollableListPanel<MemoryRecord> {
|
|
|
335
335
|
[' project ', C.label], [String(byScope.get('project') ?? 0), C.value],
|
|
336
336
|
[' team ', C.label], [String(byScope.get('team') ?? 0), C.good],
|
|
337
337
|
]),
|
|
338
|
-
buildGuidanceLine(width, '/recall review', 'work the stale and contradicted queue from the command
|
|
338
|
+
buildGuidanceLine(width, '/recall review', 'work the stale and contradicted queue from the command workspace', C),
|
|
339
339
|
];
|
|
340
340
|
|
|
341
341
|
const recentSummaryLines: Line[] = [];
|
|
@@ -189,7 +189,7 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
189
189
|
{ label: 'runbooks', value: String(byClass.get('runbook') ?? 0), valueColor: C.runbook },
|
|
190
190
|
], C),
|
|
191
191
|
filterLine,
|
|
192
|
-
buildGuidanceLine(width, '/recall review', 'review durable knowledge and queue posture from the command
|
|
192
|
+
buildGuidanceLine(width, '/recall review', 'review durable knowledge and queue posture from the command workspace', C),
|
|
193
193
|
];
|
|
194
194
|
|
|
195
195
|
const selected = records[this.selectedIndex];
|
|
@@ -79,7 +79,7 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
79
79
|
protected override getEmptyStateActions() {
|
|
80
80
|
return [
|
|
81
81
|
{ command: '/subscription login openai start --yes', summary: 'start the first-class OpenAI subscription flow' },
|
|
82
|
-
{ command: '/login provider <name> start --yes', summary: 'use the front-door auth
|
|
82
|
+
{ command: '/login provider <name> start --yes', summary: 'use the front-door auth flow for supported providers' },
|
|
83
83
|
{ command: '/services auth-review', summary: 'inspect configured service auth posture and stored secrets' },
|
|
84
84
|
];
|
|
85
85
|
}
|
|
@@ -205,7 +205,7 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
205
205
|
intro,
|
|
206
206
|
sections: [{ lines: [...summaryLines, ...emptyLines] }],
|
|
207
207
|
footerLines: [
|
|
208
|
-
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start browser-based provider login from the packaged subscription
|
|
208
|
+
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start browser-based provider login from the packaged subscription flow', C),
|
|
209
209
|
buildPanelLine(width, [[' Up/Down move Enter/X sign out selected provider r refresh', C.dim]]),
|
|
210
210
|
],
|
|
211
211
|
palette: C,
|
|
@@ -255,7 +255,7 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
255
255
|
header: headerLines,
|
|
256
256
|
footer: [
|
|
257
257
|
...detailRows,
|
|
258
|
-
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start browser-based provider login from the packaged subscription
|
|
258
|
+
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start browser-based provider login from the packaged subscription flow', C),
|
|
259
259
|
buildPanelLine(width, [[' Up/Down move Enter/X sign out selected provider r refresh', C.dim]]),
|
|
260
260
|
],
|
|
261
261
|
});
|
|
@@ -144,7 +144,7 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
|
|
|
144
144
|
this.getEmptyStateMessage(),
|
|
145
145
|
'Model switches, scan notices, provider/system state, and other operational updates will appear here once the runtime starts emitting them.',
|
|
146
146
|
[
|
|
147
|
-
{ command: '/help', summary: 'review
|
|
147
|
+
{ command: '/help', summary: 'review commands and operator workflows' },
|
|
148
148
|
{ command: '/cockpit', summary: 'open the unified runtime control room' },
|
|
149
149
|
],
|
|
150
150
|
C,
|
|
@@ -185,7 +185,7 @@ export class TasksPanel extends ScrollableListPanel<RuntimeTask> {
|
|
|
185
185
|
width,
|
|
186
186
|
' Runtime store not wired into this panel yet.',
|
|
187
187
|
'Use the Tasks panel with a live runtime store to review active execution, cancellations, retries, and completion results.',
|
|
188
|
-
[{ command: '/tasks', summary: '
|
|
188
|
+
[{ command: '/tasks', summary: 'inspect runtime tasks without starting background work' }],
|
|
189
189
|
C,
|
|
190
190
|
),
|
|
191
191
|
}],
|
|
@@ -192,10 +192,10 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
192
192
|
} else if (category.id === 'voice-media') {
|
|
193
193
|
base.push(
|
|
194
194
|
{ text: `Voice providers: ${snapshot.voiceProviderCount}; streaming TTS: ${snapshot.voiceStreamingProviderCount}; STT: ${snapshot.voiceSttProviderCount}; realtime: ${snapshot.voiceRealtimeProviderCount}.`, fg: PALETTE.info },
|
|
195
|
-
{ text: `Voice
|
|
195
|
+
{ text: `Voice interaction: ${snapshot.voiceSurfaceEnabled ? 'enabled' : 'disabled'}; use /voice review for portable voice posture.`, fg: snapshot.voiceSurfaceEnabled ? PALETTE.warn : PALETTE.muted },
|
|
196
196
|
{ text: `TTS config: provider ${snapshot.ttsProvider}; voice ${snapshot.ttsVoice}; response model ${snapshot.ttsResponseModel}.`, fg: PALETTE.info },
|
|
197
197
|
{ text: `Media providers: ${snapshot.mediaProviderCount}; understanding: ${snapshot.mediaUnderstandingProviderCount}; generation: ${snapshot.mediaGenerationProviderCount}.`, fg: PALETTE.info },
|
|
198
|
-
{ text: `Browser
|
|
198
|
+
{ text: `Browser companion: ${snapshot.browserSurfaceEnabled ? 'available' : 'not advertised'}; public base URL ${snapshot.browserSurfacePublicBaseUrl}.`, fg: snapshot.browserSurfaceEnabled ? PALETTE.warn : PALETTE.muted },
|
|
199
199
|
{ text: 'Build dispatch stays out of setup; explicit delegation is handled from the Build Delegation workspace.', fg: PALETTE.good },
|
|
200
200
|
{ text: 'Image input uses prompt attachments; media generation/provider setup stays behind explicit commands and configured providers.', fg: PALETTE.muted },
|
|
201
201
|
);
|
|
@@ -83,7 +83,7 @@ export function renderHelpOverlay(
|
|
|
83
83
|
['hooks', '', 'Hook workbench and runtime activity'],
|
|
84
84
|
['orchestration','', 'Graph and recursive-agent control room'],
|
|
85
85
|
['communication','', 'Structured agent communication workspace'],
|
|
86
|
-
['tasks', '', '
|
|
86
|
+
['tasks', '', 'Read-only task view for list/show/pause/resume/output'],
|
|
87
87
|
];
|
|
88
88
|
|
|
89
89
|
// Build command rows from featured list, filtering out unregistered commands.
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { isSecretRefInput } from '@pellux/goodvibes-sdk/platform/config';
|
|
4
4
|
import { CONFIG_SCHEMA, DEFAULT_CONFIG } from '../../config/index.ts';
|
|
5
|
+
import {
|
|
6
|
+
createAgentRuntimeProfile,
|
|
7
|
+
getAgentRuntimeProfileTemplate,
|
|
8
|
+
resolveAgentRuntimeProfileHome,
|
|
9
|
+
} from '../../agent/runtime-profile.ts';
|
|
5
10
|
import type { FeatureFlagConfigKey } from '../surface-feature-flags.ts';
|
|
6
11
|
import {
|
|
7
12
|
getOnboardingRuntimeStatePath,
|
|
@@ -250,6 +255,22 @@ function validateAcknowledgementOperation(
|
|
|
250
255
|
}
|
|
251
256
|
}
|
|
252
257
|
|
|
258
|
+
function validateCreateAgentProfileOperation(
|
|
259
|
+
deps: OnboardingApplyDependencies,
|
|
260
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-agent-profile' }>,
|
|
261
|
+
): void {
|
|
262
|
+
const name = operation.name.trim();
|
|
263
|
+
if (name.length === 0) throw new Error('Agent profile name is required.');
|
|
264
|
+
|
|
265
|
+
const resolution = resolveAgentRuntimeProfileHome(deps.shellPaths.homeDirectory, name);
|
|
266
|
+
if (existsSync(resolution.homeDirectory)) {
|
|
267
|
+
throw new Error(`Agent profile already exists: ${resolution.id}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const templateId = operation.templateId?.trim();
|
|
271
|
+
if (templateId) getAgentRuntimeProfileTemplate(templateId, deps.shellPaths.homeDirectory);
|
|
272
|
+
}
|
|
273
|
+
|
|
253
274
|
function applyConfigOperation(
|
|
254
275
|
deps: OnboardingApplyDependencies,
|
|
255
276
|
operation: Extract<OnboardingApplyOperation, { kind: 'set-config' }>,
|
|
@@ -441,6 +462,13 @@ async function buildRollbackAction(
|
|
|
441
462
|
);
|
|
442
463
|
}
|
|
443
464
|
|
|
465
|
+
if (operation.kind === 'create-agent-profile') {
|
|
466
|
+
const resolution = resolveAgentRuntimeProfileHome(deps.shellPaths.homeDirectory, operation.name);
|
|
467
|
+
return () => {
|
|
468
|
+
if (existsSync(resolution.homeDirectory)) rmSync(resolution.homeDirectory, { recursive: true, force: true });
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
444
472
|
const neverOperation: never = operation;
|
|
445
473
|
throw new Error(`Unsupported onboarding operation: ${JSON.stringify(neverOperation)}`);
|
|
446
474
|
}
|
|
@@ -465,6 +493,24 @@ function applyAcknowledgementOperation(
|
|
|
465
493
|
};
|
|
466
494
|
}
|
|
467
495
|
|
|
496
|
+
function applyCreateAgentProfileOperation(
|
|
497
|
+
deps: OnboardingApplyDependencies,
|
|
498
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-agent-profile' }>,
|
|
499
|
+
): OnboardingAppliedOperation {
|
|
500
|
+
validateCreateAgentProfileOperation(deps, operation);
|
|
501
|
+
const templateId = operation.templateId?.trim();
|
|
502
|
+
const profile = createAgentRuntimeProfile(deps.shellPaths.homeDirectory, operation.name, {
|
|
503
|
+
...(templateId ? { templateId } : {}),
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
return {
|
|
507
|
+
kind: operation.kind,
|
|
508
|
+
summary: profile.starterTemplateId
|
|
509
|
+
? `Created Agent profile ${profile.id} from ${profile.starterTemplateId}.`
|
|
510
|
+
: `Created Agent profile ${profile.id}.`,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
468
514
|
function orderApplyOperations(
|
|
469
515
|
operations: readonly OnboardingApplyOperation[],
|
|
470
516
|
): readonly OnboardingApplyOperation[] {
|
|
@@ -476,6 +522,7 @@ function orderApplyOperations(
|
|
|
476
522
|
const configOperations = operations.filter((operation) => (
|
|
477
523
|
operation.kind === 'set-config' && operation.key !== 'storage.secretPolicy'
|
|
478
524
|
));
|
|
525
|
+
const agentProfileOperations = operations.filter((operation) => operation.kind === 'create-agent-profile');
|
|
479
526
|
const finalOperations = operations.filter((operation) => (
|
|
480
527
|
operation.kind === 'acknowledge'
|
|
481
528
|
));
|
|
@@ -485,6 +532,7 @@ function orderApplyOperations(
|
|
|
485
532
|
...authOperations,
|
|
486
533
|
...secretOperations,
|
|
487
534
|
...configOperations,
|
|
535
|
+
...agentProfileOperations,
|
|
488
536
|
...finalOperations,
|
|
489
537
|
];
|
|
490
538
|
}
|
|
@@ -521,6 +569,11 @@ function prevalidateApplyRequest(
|
|
|
521
569
|
continue;
|
|
522
570
|
}
|
|
523
571
|
|
|
572
|
+
if (operation.kind === 'create-agent-profile') {
|
|
573
|
+
validateCreateAgentProfileOperation(deps, operation);
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
|
|
524
577
|
const neverOperation: never = operation;
|
|
525
578
|
throw new Error(`Unsupported onboarding operation: ${JSON.stringify(neverOperation)}`);
|
|
526
579
|
} catch (error) {
|
|
@@ -539,6 +592,7 @@ function getVerificationFailureKind(itemId: string): OnboardingApplyOperation['k
|
|
|
539
592
|
if (itemId.startsWith('secret:')) return 'set-secret';
|
|
540
593
|
if (itemId.startsWith('auth:')) return 'ensure-auth-user';
|
|
541
594
|
if (itemId.startsWith('acknowledge:')) return 'acknowledge';
|
|
595
|
+
if (itemId.startsWith('agent-profile:')) return 'create-agent-profile';
|
|
542
596
|
return 'set-config';
|
|
543
597
|
}
|
|
544
598
|
|
|
@@ -590,6 +644,12 @@ export async function applyOnboardingRequest(
|
|
|
590
644
|
continue;
|
|
591
645
|
}
|
|
592
646
|
|
|
647
|
+
if (operation.kind === 'create-agent-profile') {
|
|
648
|
+
applied.push(applyCreateAgentProfileOperation(deps, operation));
|
|
649
|
+
rollbacks.push(rollback);
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
|
|
593
653
|
const neverOperation: never = operation;
|
|
594
654
|
throw new Error(`Unsupported onboarding operation: ${JSON.stringify(neverOperation)}`);
|
|
595
655
|
} catch (error) {
|
|
@@ -264,6 +264,11 @@ export type OnboardingApplyOperation =
|
|
|
264
264
|
readonly kind: 'acknowledge';
|
|
265
265
|
readonly target: OnboardingAcknowledgementTarget;
|
|
266
266
|
readonly acknowledged: boolean;
|
|
267
|
+
}
|
|
268
|
+
| {
|
|
269
|
+
readonly kind: 'create-agent-profile';
|
|
270
|
+
readonly name: string;
|
|
271
|
+
readonly templateId?: string;
|
|
267
272
|
};
|
|
268
273
|
|
|
269
274
|
export interface OnboardingApplyRequest {
|
|
@@ -358,7 +363,7 @@ export interface OnboardingProviderAccountReadHelper {
|
|
|
358
363
|
|
|
359
364
|
export type OnboardingShellPaths = Pick<
|
|
360
365
|
ShellPathService,
|
|
361
|
-
'workingDirectory' | 'resolveProjectPath' | 'resolveUserPath'
|
|
366
|
+
'homeDirectory' | 'workingDirectory' | 'resolveProjectPath' | 'resolveUserPath'
|
|
362
367
|
>;
|
|
363
368
|
|
|
364
369
|
export interface OnboardingSnapshotDependencies {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { isSecretRefInput } from '@pellux/goodvibes-sdk/platform/config';
|
|
4
|
+
import { resolveAgentRuntimeProfileHome } from '../../agent/runtime-profile.ts';
|
|
2
5
|
import { readOnboardingRuntimeState } from './state.ts';
|
|
3
6
|
import type {
|
|
4
7
|
OnboardingApplyOperation,
|
|
@@ -147,6 +150,22 @@ function verifyAuthOperation(
|
|
|
147
150
|
};
|
|
148
151
|
}
|
|
149
152
|
|
|
153
|
+
function verifyCreateAgentProfileOperation(
|
|
154
|
+
deps: OnboardingVerificationDependencies,
|
|
155
|
+
operation: Extract<OnboardingApplyOperation, { kind: 'create-agent-profile' }>,
|
|
156
|
+
): OnboardingVerificationItem {
|
|
157
|
+
const resolution = resolveAgentRuntimeProfileHome(deps.shellPaths.homeDirectory, operation.name);
|
|
158
|
+
const ok = existsSync(join(resolution.homeDirectory, 'profile.json'));
|
|
159
|
+
return {
|
|
160
|
+
id: `agent-profile:${resolution.id}`,
|
|
161
|
+
status: ok ? 'pass' : 'fail',
|
|
162
|
+
message: ok
|
|
163
|
+
? `${resolution.id} Agent profile exists.`
|
|
164
|
+
: `${resolution.id} Agent profile was not created.`,
|
|
165
|
+
target: resolution.id,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
150
169
|
async function verifyOperation(
|
|
151
170
|
deps: OnboardingVerificationDependencies,
|
|
152
171
|
operation: OnboardingApplyOperation,
|
|
@@ -154,7 +173,8 @@ async function verifyOperation(
|
|
|
154
173
|
if (operation.kind === 'set-config') return verifyConfigOperation(deps, operation);
|
|
155
174
|
if (operation.kind === 'set-secret') return verifySecretOperation(deps, operation);
|
|
156
175
|
if (operation.kind === 'ensure-auth-user') return verifyAuthOperation(deps, operation);
|
|
157
|
-
return verifyAcknowledgementOperation(deps, operation);
|
|
176
|
+
if (operation.kind === 'acknowledge') return verifyAcknowledgementOperation(deps, operation);
|
|
177
|
+
return verifyCreateAgentProfileOperation(deps, operation);
|
|
158
178
|
}
|
|
159
179
|
|
|
160
180
|
export async function verifyOnboardingRequest(
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.1.
|
|
9
|
+
let _version = '0.1.76';
|
|
10
10
|
let _sdkVersion = '0.33.35';
|
|
11
11
|
try {
|
|
12
12
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|