@pellux/goodvibes-agent 0.1.105 → 0.1.106

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.
@@ -9,32 +9,17 @@ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
9
9
  interface AuthReviewBundle {
10
10
  readonly version: 1;
11
11
  readonly exportedAt: number;
12
- readonly runtimeLoginUrl: string;
13
- readonly listenerLoginUrl: string;
12
+ readonly externalRuntimeAuth: 'managed-outside-agent';
14
13
  readonly secretKeys: readonly string[];
15
14
  readonly activeSubscriptions: readonly string[];
16
15
  readonly pendingSubscriptions: readonly string[];
17
16
  }
18
17
 
19
- type AuthServiceLoginTarget = 'runtime' | 'listener';
20
-
21
- function normalizeAuthServiceLoginTarget(value: string | undefined): AuthServiceLoginTarget | null {
22
- const normalized = value?.trim().toLowerCase();
23
- if (normalized === 'runtime' || normalized === 'daemon') return 'runtime';
24
- if (normalized === 'listener' || normalized === 'inbound-listener') return 'listener';
25
- return null;
26
- }
27
-
28
- function authServiceSecretPrefix(target: AuthServiceLoginTarget): string {
29
- return target === 'runtime' ? 'RUNTIME' : 'LISTENER';
30
- }
31
-
32
18
  function inspectAuthBundle(bundle: AuthReviewBundle): string {
33
19
  return [
34
20
  'Auth Review Bundle',
35
21
  ` exportedAt: ${new Date(bundle.exportedAt).toISOString()}`,
36
- ` runtimeLoginUrl: ${bundle.runtimeLoginUrl}`,
37
- ` listenerLoginUrl: ${bundle.listenerLoginUrl}`,
22
+ ` externalRuntimeAuth: ${bundle.externalRuntimeAuth}`,
38
23
  ` stored secrets: ${bundle.secretKeys.length}`,
39
24
  ` active subscriptions: ${bundle.activeSubscriptions.length}`,
40
25
  ` pending subscriptions: ${bundle.pendingSubscriptions.length}`,
@@ -44,12 +29,11 @@ function inspectAuthBundle(bundle: AuthReviewBundle): string {
44
29
  export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry): void {
45
30
  registry.register({
46
31
  name: 'login',
47
- description: 'Front-door login flow for provider subscriptions and local service sessions',
48
- usage: '[provider <name> start|finish <code> --yes|service <runtime|listener> <baseUrl> <username> <password> [secretKey] --yes]',
32
+ description: 'Front-door login flow for provider subscriptions',
33
+ usage: 'provider <name> start|finish <code> --yes',
49
34
  async handler(args, ctx) {
50
35
  const parsed = stripYesFlag(args);
51
36
  const commandArgs = [...parsed.rest];
52
- const shellPaths = requireShellPaths(ctx);
53
37
  const target = (commandArgs[0] ?? '').toLowerCase();
54
38
  if (target === 'provider') {
55
39
  const provider = commandArgs[1];
@@ -69,19 +53,16 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
69
53
  ctx.print(`Use /subscription login ${provider} ${mode}${commandArgs[3] ? ` ${commandArgs[3]}` : ''} --yes`);
70
54
  return;
71
55
  }
72
- if (target === 'service') {
73
- if (!parsed.yes) {
74
- requireYesFlag(ctx, 'store a local service session token', '/login service <runtime|listener> <baseUrl> <username> <password> [secretKey] --yes');
75
- return;
76
- }
77
- if (ctx.executeCommand) {
78
- await ctx.executeCommand('auth', ['login', ...commandArgs.slice(1), '--yes']);
79
- return;
80
- }
81
- ctx.print('Use /auth login <runtime|listener> <baseUrl> <username> <password> [secretKey] --yes');
56
+ if (target === 'service' || target === 'runtime' || target === 'listener' || target === 'daemon') {
57
+ ctx.print([
58
+ 'Runtime service login is external to GoodVibes Agent.',
59
+ 'Agent does not create, exchange, store, rotate, revoke, or clear runtime/listener service sessions.',
60
+ 'Use the runtime-owning GoodVibes TUI or host tooling for runtime auth administration.',
61
+ 'Agent login supports provider subscriptions only: /login provider <name> start|finish <code> --yes.',
62
+ ].join('\n'));
82
63
  return;
83
64
  }
84
- ctx.print('Usage: /login [provider <name> start|finish <code> --yes|service <runtime|listener> <baseUrl> <username> <password> [secretKey] --yes]');
65
+ ctx.print('Usage: /login provider <name> start|finish <code> --yes');
85
66
  },
86
67
  });
87
68
 
@@ -111,25 +92,34 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
111
92
 
112
93
  registry.register({
113
94
  name: 'auth',
114
- description: 'Review auth posture and exchange session login tokens with local services',
115
- usage: '[review|show <provider>|repair <provider>|bundle export <path> --yes|bundle inspect <path>|login <runtime|listener> <baseUrl> <username> <password> [secretKey] --yes]',
95
+ description: 'Review provider auth posture and export redacted auth review bundles',
96
+ usage: '[review|show <provider>|repair <provider>|bundle export <path> --yes|bundle inspect <path>]',
116
97
  async handler(args, ctx) {
117
98
  const parsed = stripYesFlag(args);
118
99
  const commandArgs = [...parsed.rest];
119
- const shellPaths = requireShellPaths(ctx);
120
100
  const sub = commandArgs[0] ?? 'review';
121
- const subscriptions = requireSubscriptionManager(ctx);
122
- const serviceRegistry = requireServiceRegistry(ctx);
123
- const secretsManager = requireSecretsManager(ctx);
124
101
  if (sub === 'local') {
125
102
  ctx.print([
126
103
  'Local runtime auth management is external to GoodVibes Agent.',
127
104
  'Agent connects to an already-running GoodVibes runtime and does not create, delete, rotate, revoke, or clear runtime auth users, sessions, or bootstrap credentials.',
128
105
  'Use the runtime-owning GoodVibes TUI or host tooling for runtime auth administration.',
129
- 'Agent auth commands available here: /auth review, /auth show <provider>, /auth repair <provider>, /auth login <runtime|listener> ... --yes.',
106
+ 'Agent auth commands available here: /auth review, /auth show <provider>, /auth repair <provider>, /auth bundle export <path> --yes, /auth bundle inspect <path>.',
130
107
  ].join('\n'));
131
108
  return;
132
109
  }
110
+ if (sub === 'login') {
111
+ ctx.print([
112
+ 'Runtime service login is external to GoodVibes Agent.',
113
+ 'Agent does not create, exchange, store, rotate, revoke, or clear runtime/listener service sessions.',
114
+ 'Use the runtime-owning GoodVibes TUI or host tooling for runtime auth administration.',
115
+ ].join('\n'));
116
+ return;
117
+ }
118
+
119
+ const shellPaths = requireShellPaths(ctx);
120
+ const subscriptions = requireSubscriptionManager(ctx);
121
+ const serviceRegistry = requireServiceRegistry(ctx);
122
+ const secretsManager = requireSecretsManager(ctx);
133
123
  if (sub === 'review') {
134
124
  const snapshot = await buildAuthInspectionSnapshot({
135
125
  serviceRegistry,
@@ -139,8 +129,7 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
139
129
  const builtinProviders = listBuiltinSubscriptionProviders().map((entry) => entry.provider);
140
130
  ctx.print([
141
131
  'Auth Review',
142
- ' runtime login route: /login',
143
- ' listener login route: /login',
132
+ ' runtime auth: managed outside goodvibes-agent',
144
133
  ` stored secrets: ${snapshot.secretKeyCount}`,
145
134
  ` built-in providers: ${builtinProviders.length}${builtinProviders.length > 0 ? ` (${builtinProviders.join(', ')})` : ''}`,
146
135
  ` active subscriptions: ${snapshot.activeSubscriptions}${snapshot.activeSubscriptions > 0 ? ` (${snapshot.providers.filter((provider) => provider.activeSubscription).map((provider) => provider.provider).join(', ')})` : ''}`,
@@ -221,8 +210,7 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
221
210
  const bundle: AuthReviewBundle = {
222
211
  version: 1,
223
212
  exportedAt: Date.now(),
224
- runtimeLoginUrl: 'http://127.0.0.1:3421/login',
225
- listenerLoginUrl: 'http://127.0.0.1:3422/login',
213
+ externalRuntimeAuth: 'managed-outside-agent',
226
214
  secretKeys,
227
215
  activeSubscriptions: subscriptions.list().map((entry) => entry.provider),
228
216
  pendingSubscriptions: subscriptions.listPending().map((entry) => entry.provider),
@@ -238,43 +226,7 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
238
226
  return;
239
227
  }
240
228
  }
241
-
242
- if (sub === 'login') {
243
- const target = normalizeAuthServiceLoginTarget(commandArgs[1]);
244
- const baseUrl = commandArgs[2];
245
- const username = commandArgs[3];
246
- const password = commandArgs[4];
247
- const secretKey = commandArgs[5] ?? `${target ? authServiceSecretPrefix(target) : 'SERVICE'}_SESSION_TOKEN`;
248
- if (!target || !baseUrl || !username || !password) {
249
- ctx.print('Usage: /auth login <runtime|listener> <baseUrl> <username> <password> [secretKey] --yes');
250
- return;
251
- }
252
- if (!parsed.yes) {
253
- requireYesFlag(ctx, `store ${target} session token`, '/auth login <runtime|listener> <baseUrl> <username> <password> [secretKey] --yes');
254
- return;
255
- }
256
- const url = new URL('/login', baseUrl).toString();
257
- const response = await fetch(url, {
258
- method: 'POST',
259
- headers: { 'Content-Type': 'application/json' },
260
- body: JSON.stringify({ username, password }),
261
- });
262
- if (!response.ok) {
263
- const body = await response.text();
264
- ctx.print(`Auth login failed (${response.status}): ${body}`);
265
- return;
266
- }
267
- const body = await response.json() as { token?: unknown };
268
- if (typeof body.token !== 'string') {
269
- ctx.print('Auth login response did not include a session token.');
270
- return;
271
- }
272
- await requireSecretsManager(ctx).set(secretKey, body.token);
273
- ctx.print(`Stored ${target} session token in secure storage as ${secretKey}.`);
274
- return;
275
- }
276
-
277
- ctx.print('Usage: /auth [review|show <provider>|bundle export <path> --yes|bundle inspect <path>|login <runtime|listener> <baseUrl> <username> <password> [secretKey] --yes]');
229
+ ctx.print('Usage: /auth [review|show <provider>|bundle export <path> --yes|bundle inspect <path>]');
278
230
  },
279
231
  });
280
232
  }
@@ -144,7 +144,7 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
144
144
  if (meta.title) ctx.session.conversationManager.title = meta.title;
145
145
  ctx.session.conversationManager.rebuildHistory();
146
146
  ctx.renderRequest();
147
- ctx.print(`Session loaded: ${args[0]} (${messages.length} messages)${agentRecords.length > 0 ? ` [ignored ${agentRecords.length} copied local agent record${agentRecords.length !== 1 ? 's' : ''}]` : ''}`);
147
+ ctx.print(`Session loaded: ${args[0]} (${messages.length} messages)${agentRecords.length > 0 ? ` [ignored ${agentRecords.length} runtime-owned local agent record${agentRecords.length !== 1 ? 's' : ''}]` : ''}`);
148
148
  } catch (e) {
149
149
  ctx.print(`Failed to load session: ${summarizeError(e)}`);
150
150
  }
@@ -154,58 +154,40 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
154
154
  registry.register({
155
155
  name: 'undo',
156
156
  aliases: [],
157
- description: 'Undo last action. /undo file — revert last file write/edit. /undo — remove last conversation turn.',
158
- usage: '[file]',
159
- argsHint: '[file]',
157
+ description: 'Undo the last conversation turn',
158
+ usage: '',
159
+ argsHint: '',
160
160
  handler(args, ctx) {
161
- if (args[0] === 'file') {
162
- if (!ctx.workspace.fileUndoManager) {
163
- ctx.print('File undo not available.');
164
- return;
165
- }
166
- try {
167
- const result = ctx.workspace.fileUndoManager.undo();
168
- ctx.print(result ? `File reverted: ${result.path} (${result.tool} tool). Use /redo file to re-apply.` : 'Nothing to undo. No file operations recorded.');
169
- } catch (err) {
170
- ctx.print(`File undo failed: ${summarizeError(err)}`);
171
- }
161
+ if (args.length > 0) {
162
+ ctx.print('Usage: /undo\n Removes the last conversation turn. File edit undo belongs to the delegated GoodVibes TUI session.');
172
163
  return;
173
164
  }
174
165
  const success = ctx.session.conversationManager.undo();
175
166
  if (success) {
176
- ctx.print('Last turn undone. Use /redo to restore. Tip: /undo file to revert a file write/edit.');
167
+ ctx.print('Last turn undone. Use /redo to restore.');
177
168
  ctx.renderRequest();
178
169
  } else {
179
- ctx.print('Nothing to undo. Tip: use /undo file to revert the last file write/edit.');
170
+ ctx.print('Nothing to undo.');
180
171
  }
181
172
  },
182
173
  });
183
174
 
184
175
  registry.register({
185
176
  name: 'redo',
186
- description: 'Redo last undone action. /redo file — re-apply last reverted file. /redo — restore conversation turn.',
187
- usage: '[file]',
188
- argsHint: '[file]',
177
+ description: 'Redo the last undone conversation turn',
178
+ usage: '',
179
+ argsHint: '',
189
180
  handler(args, ctx) {
190
- if (args[0] === 'file') {
191
- if (!ctx.workspace.fileUndoManager) {
192
- ctx.print('File redo not available.');
193
- return;
194
- }
195
- try {
196
- const result = ctx.workspace.fileUndoManager.redo();
197
- ctx.print(result ? `File re-applied: ${result.path} (${result.tool} tool).` : 'Nothing to redo.');
198
- } catch (err) {
199
- ctx.print(`File redo failed: ${summarizeError(err)}`);
200
- }
181
+ if (args.length > 0) {
182
+ ctx.print('Usage: /redo\n Restores the last undone conversation turn. File edit redo belongs to the delegated GoodVibes TUI session.');
201
183
  return;
202
184
  }
203
185
  const success = ctx.session.conversationManager.redo();
204
186
  if (success) {
205
- ctx.print('Turn restored. Tip: /redo file to re-apply a reverted file.');
187
+ ctx.print('Turn restored.');
206
188
  ctx.renderRequest();
207
189
  } else {
208
- ctx.print('Nothing to redo. Tip: use /redo file to re-apply the last reverted file.');
190
+ ctx.print('Nothing to redo.');
209
191
  }
210
192
  },
211
193
  });
@@ -260,8 +242,9 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
260
242
  });
261
243
 
262
244
  registry.register({
263
- name: 'memory',
264
- description: 'Manage session memories (pinned across context compaction)',
245
+ name: 'session-memory',
246
+ aliases: ['smemory'],
247
+ description: 'Manage conversation-pinned memories used during context compaction',
265
248
  usage: '[list|add <text>|remove <id> --yes]',
266
249
  argsHint: '[list|add|remove]',
267
250
  handler(args, ctx) {
@@ -269,12 +252,12 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
269
252
  if (sub === 'list' || args.length === 0) {
270
253
  const memories = requireSessionMemoryStore(ctx).list();
271
254
  ctx.print(memories.length === 0
272
- ? 'No session memories. Use !# prefix or /memory add <text> to create one.'
273
- : [`Session Memories (${memories.length}):`, ...memories.map(m => ` [${m.id}] ${m.text}`)].join('\n'));
255
+ ? 'No conversation-pinned memories. Use !# prefix or /session-memory add <text> to create one.'
256
+ : [`Conversation-Pinned Memories (${memories.length}):`, ...memories.map(m => ` [${m.id}] ${m.text}`)].join('\n'));
274
257
  } else if (sub === 'add') {
275
258
  const text = args.slice(1).join(' ').trim();
276
259
  if (!text) {
277
- ctx.print('Usage: /memory add <text>');
260
+ ctx.print('Usage: /session-memory add <text>');
278
261
  return;
279
262
  }
280
263
  const id = requireSessionMemoryStore(ctx).add(text);
@@ -283,17 +266,17 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
283
266
  const parsed = stripYesFlag(args);
284
267
  const id = parsed.rest[1];
285
268
  if (!id) {
286
- ctx.print('Usage: /memory remove <id> --yes');
269
+ ctx.print('Usage: /session-memory remove <id> --yes');
287
270
  return;
288
271
  }
289
272
  if (!parsed.yes) {
290
- requireYesFlag(ctx, `remove session memory ${id}`, '/memory remove <id> --yes');
273
+ requireYesFlag(ctx, `remove conversation-pinned memory ${id}`, '/session-memory remove <id> --yes');
291
274
  return;
292
275
  }
293
276
  const store = requireSessionMemoryStore(ctx);
294
277
  ctx.print(store.remove(id) ? `Memory removed: [${id}]` : `Memory not found: ${id}`);
295
278
  } else {
296
- ctx.print('Usage: /memory [list|add <text>|remove <id> --yes]\n /memory — list all session memories\n /memory list — list all session memories\n /memory add <text> — add a memory without sending a message\n /memory remove <id> --yes — remove a specific memory');
279
+ ctx.print('Usage: /session-memory [list|add <text>|remove <id> --yes]\n /session-memory — list conversation-pinned memories\n /session-memory list — list conversation-pinned memories\n /session-memory add <text> — add a memory without sending a message\n /session-memory remove <id> --yes — remove a specific memory');
297
280
  }
298
281
  },
299
282
  });
@@ -1,4 +1,5 @@
1
1
  import type { CommandRegistry } from './command-registry.ts';
2
+ import type { CommandContext } from './command-registry.ts';
2
3
  import { policyCommand } from './commands/policy.ts';
3
4
  import { sessionCommand } from './commands/session.ts';
4
5
  import { recallCommand } from './commands/memory.ts';
@@ -35,6 +36,19 @@ import { registerPersonasRuntimeCommands } from './commands/personas-runtime.ts'
35
36
  import { registerAgentSkillsRuntimeCommands } from './commands/agent-skills-runtime.ts';
36
37
  import { registerRoutinesRuntimeCommands } from './commands/routines-runtime.ts';
37
38
 
39
+ function registerAgentMemoryCommand(registry: CommandRegistry): void {
40
+ registry.register({
41
+ name: 'memory',
42
+ aliases: ['mem'],
43
+ description: 'Agent-local memory: add, search, review, stale, and delete durable memory records',
44
+ usage: '<list|add|search|queue|review|stale|remove> [args]',
45
+ argsHint: 'list|add|search|queue|review|stale|remove',
46
+ handler: async (args: string[], context: CommandContext): Promise<void> => {
47
+ await recallCommand.handler(args.length === 0 ? ['list'] : args, context);
48
+ },
49
+ });
50
+ }
51
+
38
52
  /**
39
53
  * registerBuiltinCommands - Register all built-in slash commands into the registry.
40
54
  * Call once during application startup.
@@ -71,6 +85,7 @@ export function registerBuiltinCommands(registry: CommandRegistry): void {
71
85
  registerPlanningRuntimeCommands(registry);
72
86
  registerScheduleRuntimeCommands(registry);
73
87
  registerSessionContentCommands(registry);
88
+ registerAgentMemoryCommand(registry);
74
89
 
75
90
  // ── /policy ───────────────────────────────────────────────────────────────
76
91
  registry.register(policyCommand);
@@ -304,7 +304,7 @@ export function handleBlockSave(
304
304
  requestRender();
305
305
  return;
306
306
  }
307
- conversationManager.log('[Block save blocked in GoodVibes Agent: use /share <html|json|md> <path> --yes or copy the block explicitly.]', { fg: '#f59e0b' });
307
+ conversationManager.log('[Block file save is disabled in GoodVibes Agent: copy the block explicitly or use /export markdown <path> --yes for the conversation.]', { fg: '#f59e0b' });
308
308
  requestRender();
309
309
  }
310
310
 
@@ -68,9 +68,9 @@ export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
68
68
  'search': 'Toggle conversation search',
69
69
  'block-copy': 'Copy nearest block to clipboard',
70
70
  'bookmark': 'Bookmark / unbookmark nearest block',
71
- 'block-save': 'Block file save blocked; use /share --yes',
71
+ 'block-save': 'Block file save disabled; copy block or export conversation',
72
72
  'delete-word': 'Delete word backward',
73
- 'apply-diff-line-start': 'Apply nearest diff / move to line start',
73
+ 'apply-diff-line-start': 'Delegate diff changes / move to line start',
74
74
  'next-error-line-end': 'Navigate to next error / move to line end',
75
75
  'kill-line': 'Kill to end of line',
76
76
  'clear-prompt': 'Clear the prompt',
@@ -0,0 +1,295 @@
1
+ import type { OnboardingWizardStepDefinition } from './onboarding-wizard-types.ts';
2
+
3
+ export function buildCommunicationStep(): OnboardingWizardStepDefinition {
4
+ return {
5
+ id: 'agent-communication',
6
+ title: 'Channels and notifications',
7
+ shortLabel: 'Channels',
8
+ description: 'Prepare the Agent for companion pairing, messaging-channel awareness, notification delivery, and safe outbound communication while leaving runtime hosting external.',
9
+ summaryTitle: 'Communication posture',
10
+ summaryLines: [
11
+ 'Companion chat: paired through the GoodVibes runtime',
12
+ 'Channel accounts: inspect readiness before using them',
13
+ 'Outbound messages: explicit user action only',
14
+ ],
15
+ fields: [
16
+ {
17
+ kind: 'status',
18
+ id: 'agent-communication.companion',
19
+ label: 'Companion pairing',
20
+ hint: 'Use /pair from the Agent workspace to pair companion clients through the already-running GoodVibes runtime.',
21
+ defaultValue: 'External runtime route',
22
+ },
23
+ {
24
+ kind: 'status',
25
+ id: 'agent-communication.channels',
26
+ label: 'Messaging channels',
27
+ hint: 'Use the Channels workspace to inspect account readiness, delivery posture, and recent communication without changing runtime hosting.',
28
+ defaultValue: 'Inspectable',
29
+ },
30
+ {
31
+ kind: 'status',
32
+ id: 'agent-communication.notifications',
33
+ label: 'Notification delivery',
34
+ hint: 'Routine, approval, and work-plan notifications require an explicit delivery target and command; Agent never silently sends external messages.',
35
+ defaultValue: 'Explicit only',
36
+ },
37
+ {
38
+ kind: 'status',
39
+ id: 'agent-communication.inbound-policy',
40
+ label: 'Inbound command policy',
41
+ hint: 'Incoming channel commands stay constrained by runtime policy, allowlists, and account posture.',
42
+ defaultValue: 'Policy gated',
43
+ },
44
+ ],
45
+ };
46
+ }
47
+
48
+ export function buildToolsStep(): OnboardingWizardStepDefinition {
49
+ return {
50
+ id: 'agent-tools',
51
+ title: 'Tools and MCP',
52
+ shortLabel: 'Tools',
53
+ description: 'Review tool access for the Agent operator: MCP servers, browser/media helpers, safe read-only inspection, and explicit approval before side effects.',
54
+ summaryTitle: 'Tool posture',
55
+ summaryLines: [
56
+ 'MCP and tools: inspect before use',
57
+ 'Read/search/summarize: safe by default',
58
+ 'Writes, installs, external sends, and account changes: require explicit user action',
59
+ ],
60
+ fields: [
61
+ {
62
+ kind: 'status',
63
+ id: 'agent-tools.mcp',
64
+ label: 'MCP servers and tools',
65
+ hint: 'Use /mcp servers and the Agent workspace Tools area to inspect connected servers, roles, and tool readiness.',
66
+ defaultValue: 'Inspectable',
67
+ },
68
+ {
69
+ kind: 'status',
70
+ id: 'agent-tools.browser-media',
71
+ label: 'Browser and media helpers',
72
+ hint: 'Browser, image, audio, and file helpers are task-scoped tools. Agent uses them only when the current task needs them and policy allows it.',
73
+ defaultValue: 'Task scoped',
74
+ },
75
+ {
76
+ kind: 'status',
77
+ id: 'agent-tools.approval-boundary',
78
+ label: 'Power action boundary',
79
+ hint: 'Workspace writes, package installs, external sends, and account changes require an explicit command or confirmation.',
80
+ defaultValue: 'Approval required',
81
+ },
82
+ {
83
+ kind: 'status',
84
+ id: 'agent-tools.no-hidden-work',
85
+ label: 'Hidden work policy',
86
+ hint: 'Tool use stays visible in the main Agent conversation or explicit command workspace; no hidden background work is started from onboarding.',
87
+ defaultValue: 'Visible',
88
+ },
89
+ ],
90
+ };
91
+ }
92
+
93
+ export function buildAgentKnowledgeStep(): OnboardingWizardStepDefinition {
94
+ return {
95
+ id: 'agent-knowledge',
96
+ title: 'Agent Knowledge',
97
+ shortLabel: 'Knowledge',
98
+ description: 'Agent Knowledge is isolated to the GoodVibes Agent product segment. It never falls back to default Knowledge/Wiki or any non-Agent product segment.',
99
+ summaryTitle: 'Knowledge isolation',
100
+ summaryLines: [
101
+ 'Route segment: /api/goodvibes-agent/knowledge/*',
102
+ 'Default wiki fallback: disabled',
103
+ 'Non-Agent route fallback: disabled',
104
+ ],
105
+ fields: [
106
+ {
107
+ kind: 'status',
108
+ id: 'agent-knowledge.route',
109
+ label: 'Isolated Agent Knowledge route',
110
+ hint: 'Ask, search, status, and ingest use /api/goodvibes-agent/knowledge/* only.',
111
+ defaultValue: 'Isolated',
112
+ },
113
+ {
114
+ kind: 'status',
115
+ id: 'agent-knowledge.no-default-wiki',
116
+ label: 'Default Knowledge/Wiki fallback',
117
+ hint: 'Agent setup and Agent ask/search must not query the default wiki when Agent Knowledge has no answer.',
118
+ defaultValue: 'Blocked',
119
+ },
120
+ {
121
+ kind: 'status',
122
+ id: 'agent-knowledge.no-non-agent-routes',
123
+ label: 'Non-Agent route fallback',
124
+ hint: 'Other product routes are not part of Agent Knowledge.',
125
+ defaultValue: 'Blocked',
126
+ },
127
+ ],
128
+ };
129
+ }
130
+
131
+ export function buildLocalStateStep(): OnboardingWizardStepDefinition {
132
+ return {
133
+ id: 'agent-local-state',
134
+ title: 'Local memory and behavior',
135
+ shortLabel: 'Memory',
136
+ description: 'Review the Agent-local behavior model. Memory, personas, skills, routines, and Agent profiles stay local until a stable shared registry exists.',
137
+ summaryTitle: 'Local Agent state',
138
+ summaryLines: [
139
+ 'Memory/personas/skills/routines: local Agent registries',
140
+ 'Secrets: rejected or stored by secret reference',
141
+ 'Profiles: isolated Agent homes',
142
+ ],
143
+ fields: [
144
+ {
145
+ kind: 'status',
146
+ id: 'agent-local-state.memory',
147
+ label: 'Local memory',
148
+ hint: 'Use /memory to create, review, stale, search, and delete Agent-local memory records.',
149
+ defaultValue: 'Local registry',
150
+ },
151
+ {
152
+ kind: 'status',
153
+ id: 'agent-local-state.personas',
154
+ label: 'Personas',
155
+ hint: 'Use /personas to create and activate serial operating modes for the main conversation.',
156
+ defaultValue: 'Local registry',
157
+ },
158
+ {
159
+ kind: 'status',
160
+ id: 'agent-local-state.skills',
161
+ label: 'Skills',
162
+ hint: 'Use /agent-skills and /skills local to manage reusable Agent procedures.',
163
+ defaultValue: 'Local registry',
164
+ },
165
+ {
166
+ kind: 'status',
167
+ id: 'agent-local-state.routines',
168
+ label: 'Routines',
169
+ hint: 'Use /routines for reusable local procedures. Starting a routine prints steps in the main conversation and does not spawn hidden work.',
170
+ defaultValue: 'Local registry',
171
+ },
172
+ ],
173
+ };
174
+ }
175
+
176
+ export function buildAutomationStep(): OnboardingWizardStepDefinition {
177
+ return {
178
+ id: 'agent-automation',
179
+ title: 'Routines and automation',
180
+ shortLabel: 'Routines',
181
+ description: 'Set the Agent automation posture: local routines run in the main conversation, while external schedules remain explicit.',
182
+ summaryTitle: 'Routine and schedule posture',
183
+ summaryLines: [
184
+ 'Local routines: reusable main-conversation workflows',
185
+ 'External schedules: explicit promotion only',
186
+ 'Runs/cancels/retries: command-confirmed side effects',
187
+ ],
188
+ fields: [
189
+ {
190
+ kind: 'status',
191
+ id: 'agent-automation.local-routines',
192
+ label: 'Local routine library',
193
+ hint: 'Use /routines or the Agent workspace to create, review, enable, and start local routines without spawning hidden jobs.',
194
+ defaultValue: 'Local registry',
195
+ },
196
+ {
197
+ kind: 'status',
198
+ id: 'agent-automation.schedule-observability',
199
+ label: 'Schedule observability',
200
+ hint: 'Use /schedule list, /schedule reconcile, and automation views to inspect externally owned jobs and runs.',
201
+ defaultValue: 'Read first',
202
+ },
203
+ {
204
+ kind: 'status',
205
+ id: 'agent-automation.schedule-promotion',
206
+ label: 'Routine-to-schedule promotion',
207
+ hint: 'Creating external schedules from routines requires a reviewed routine, a real timing expression, optional delivery target, and explicit confirmation.',
208
+ defaultValue: 'Explicit command',
209
+ },
210
+ {
211
+ kind: 'status',
212
+ id: 'agent-automation.mutations',
213
+ label: 'Automation mutations',
214
+ hint: 'Run, pause, resume, cancel, retry, approve, and deny actions are never inferred from chat; they require exact commands and confirmation.',
215
+ defaultValue: 'Confirmed only',
216
+ },
217
+ ],
218
+ };
219
+ }
220
+
221
+ export function buildVoiceMediaStep(): OnboardingWizardStepDefinition {
222
+ return {
223
+ id: 'agent-voice-media',
224
+ title: 'Voice and media',
225
+ shortLabel: 'Voice',
226
+ description: 'Prepare voice, speech, image input, and media understanding as Agent operator tools rather than runtime lifecycle features.',
227
+ summaryTitle: 'Voice and media posture',
228
+ summaryLines: [
229
+ 'Voice and speech: optional operator tools',
230
+ 'Image/audio inputs: explicit attachment workflows',
231
+ 'Media generation and playback: provider-backed and policy-gated',
232
+ ],
233
+ fields: [
234
+ {
235
+ kind: 'status',
236
+ id: 'agent-voice-media.voice',
237
+ label: 'Voice interaction',
238
+ hint: 'Use the voice/media workspace and TTS settings to configure spoken responses for the Agent conversation.',
239
+ defaultValue: 'Optional',
240
+ },
241
+ {
242
+ kind: 'status',
243
+ id: 'agent-voice-media.attachments',
244
+ label: 'Image and audio input',
245
+ hint: 'Attach files explicitly to a prompt or command. Agent does not ingest media into Knowledge without an Agent Knowledge ingest action.',
246
+ defaultValue: 'Explicit input',
247
+ },
248
+ {
249
+ kind: 'status',
250
+ id: 'agent-voice-media.output',
251
+ label: 'Generated media and playback',
252
+ hint: 'Media output uses configured providers and visible command/turn flow; external publication still requires explicit approval.',
253
+ defaultValue: 'Policy gated',
254
+ },
255
+ ],
256
+ };
257
+ }
258
+
259
+ export function buildDelegationPolicyStep(): OnboardingWizardStepDefinition {
260
+ return {
261
+ id: 'agent-delegation',
262
+ title: 'Build delegation',
263
+ shortLabel: 'Delegate',
264
+ description: 'GoodVibes Agent is not the coding TUI. Explicit build, fix, review, or implementation work is delegated to GoodVibes TUI; ordinary assistant work stays serial in this conversation.',
265
+ summaryTitle: 'Delegation policy',
266
+ summaryLines: [
267
+ 'Normal chat: main Agent conversation',
268
+ 'Build/fix/review: explicit GoodVibes TUI delegation',
269
+ 'WRFC: only when explicitly requested for build/fix/review',
270
+ ],
271
+ fields: [
272
+ {
273
+ kind: 'status',
274
+ id: 'agent-delegation.normal-chat',
275
+ label: 'Normal assistant work',
276
+ hint: 'Planning, research, summaries, local memory updates, and safe read-only checks stay in the main Agent conversation.',
277
+ defaultValue: 'Serial',
278
+ },
279
+ {
280
+ kind: 'status',
281
+ id: 'agent-delegation.build-work',
282
+ label: 'Build/fix/review work',
283
+ hint: 'Use /delegate with the full original task. GoodVibes TUI owns coding execution and WRFC chains.',
284
+ defaultValue: 'Explicit delegation',
285
+ },
286
+ {
287
+ kind: 'status',
288
+ id: 'agent-delegation.wrfc',
289
+ label: 'WRFC policy',
290
+ hint: 'Agent never uses WRFC by default; request it only for explicit build, fix, review, or implementation work.',
291
+ defaultValue: 'Explicit only',
292
+ },
293
+ ],
294
+ };
295
+ }