@pellux/goodvibes-agent 0.1.104 → 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.
- package/CHANGELOG.md +16 -0
- package/README.md +9 -2
- package/docs/getting-started.md +9 -2
- package/docs/release-and-publishing.md +5 -3
- package/package.json +1 -1
- package/src/cli/help.ts +11 -11
- package/src/cli/management-commands.ts +1 -1
- package/src/cli/package-verification.ts +5 -5
- package/src/cli/service-posture.ts +11 -10
- package/src/cli/status.ts +29 -25
- package/src/input/agent-workspace-categories.ts +5 -5
- package/src/input/commands/guidance-runtime.ts +2 -2
- package/src/input/commands/health-runtime.ts +1 -1
- package/src/input/commands/onboarding-runtime.ts +4 -3
- package/src/input/commands/platform-access-runtime.ts +31 -79
- package/src/input/commands/session-content.ts +24 -41
- package/src/input/commands.ts +15 -0
- package/src/input/handler-content-actions.ts +1 -1
- package/src/input/keybindings.ts +2 -2
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +295 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +63 -309
- package/src/main.ts +1 -1
- package/src/panels/builtin/usage.ts +21 -0
- package/src/panels/builtin-panels.ts +2 -2
- package/src/panels/panel-list-panel.ts +1 -2
- package/src/panels/panel-picker.ts +1 -2
- package/src/panels/types.ts +1 -1
- package/src/renderer/help-overlay.ts +4 -4
- package/src/renderer/settings-modal.ts +1 -1
- package/src/runtime/bootstrap.ts +2 -2
- package/src/tools/agent-context-policy.ts +4 -4
- package/src/tools/agent-tool-policy-guard.ts +9 -9
- package/src/version.ts +1 -1
- package/src/panels/builtin/development.ts +0 -31
|
@@ -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
|
|
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
|
-
`
|
|
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
|
|
48
|
-
usage: '
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
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
|
|
115
|
-
usage: '[review|show <provider>|repair <provider>|bundle export <path> --yes|bundle inspect <path
|
|
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
|
|
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
|
|
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
|
-
|
|
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}
|
|
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
|
|
158
|
-
usage: '
|
|
159
|
-
argsHint: '
|
|
157
|
+
description: 'Undo the last conversation turn',
|
|
158
|
+
usage: '',
|
|
159
|
+
argsHint: '',
|
|
160
160
|
handler(args, ctx) {
|
|
161
|
-
if (args
|
|
162
|
-
|
|
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.
|
|
167
|
+
ctx.print('Last turn undone. Use /redo to restore.');
|
|
177
168
|
ctx.renderRequest();
|
|
178
169
|
} else {
|
|
179
|
-
ctx.print('Nothing to undo.
|
|
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
|
|
187
|
-
usage: '
|
|
188
|
-
argsHint: '
|
|
177
|
+
description: 'Redo the last undone conversation turn',
|
|
178
|
+
usage: '',
|
|
179
|
+
argsHint: '',
|
|
189
180
|
handler(args, ctx) {
|
|
190
|
-
if (args
|
|
191
|
-
|
|
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.
|
|
187
|
+
ctx.print('Turn restored.');
|
|
206
188
|
ctx.renderRequest();
|
|
207
189
|
} else {
|
|
208
|
-
ctx.print('Nothing to redo.
|
|
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
|
-
|
|
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
|
|
273
|
-
: [`
|
|
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
|
|
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
|
|
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
|
});
|
package/src/input/commands.ts
CHANGED
|
@@ -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
|
|
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
|
|
package/src/input/keybindings.ts
CHANGED
|
@@ -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
|
|
71
|
+
'block-save': 'Block file save disabled; copy block or export conversation',
|
|
72
72
|
'delete-word': 'Delete word backward',
|
|
73
|
-
'apply-diff-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
|
+
}
|