@pellux/goodvibes-agent 0.1.113 → 0.1.116
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 +19 -0
- package/README.md +4 -0
- package/dist/package/main.js +1659 -324
- package/docs/getting-started.md +4 -0
- package/package.json +1 -1
- package/src/agent/behavior-discovery-summary.ts +167 -0
- package/src/agent/persona-discovery.ts +117 -0
- package/src/agent/routine-discovery.ts +115 -0
- package/src/agent/runtime-profile-starters.ts +331 -0
- package/src/agent/runtime-profile.ts +150 -330
- package/src/cli/help.ts +11 -3
- package/src/cli/local-library-command.ts +81 -1
- package/src/cli/profiles-command.ts +128 -0
- package/src/cli/routines-command.ts +156 -1
- package/src/input/agent-workspace-basic-command-editor-submission.ts +534 -0
- package/src/input/agent-workspace-basic-command-editors.ts +77 -395
- package/src/input/agent-workspace-categories.ts +7 -0
- package/src/input/agent-workspace-command-editor.ts +4 -0
- package/src/input/agent-workspace-setup.ts +33 -13
- package/src/input/agent-workspace-snapshot.ts +6 -0
- package/src/input/agent-workspace-types.ts +6 -0
- package/src/input/commands/agent-runtime-profile-runtime.ts +73 -4
- package/src/input/commands/personas-runtime.ts +87 -2
- package/src/input/commands/routines-runtime.ts +87 -2
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +46 -9
- package/src/input/onboarding/onboarding-wizard-steps.ts +1 -1
- package/src/renderer/agent-workspace.ts +26 -0
- package/src/runtime/onboarding/derivation.ts +20 -1
- package/src/runtime/onboarding/snapshot.ts +2 -0
- package/src/runtime/onboarding/types.ts +2 -0
- package/src/version.ts +1 -1
|
@@ -4,6 +4,7 @@ import type { CommandContext } from './command-registry.ts';
|
|
|
4
4
|
import { AgentPersonaRegistry, type AgentPersonaRecord } from '../agent/persona-registry.ts';
|
|
5
5
|
import { AgentRoutineRegistry, type AgentRoutineRecord } from '../agent/routine-registry.ts';
|
|
6
6
|
import { AgentSkillRegistry, type AgentSkillBundleRecord, type AgentSkillRecord } from '../agent/skill-registry.ts';
|
|
7
|
+
import { summarizeAgentBehaviorDiscovery } from '../agent/behavior-discovery-summary.ts';
|
|
7
8
|
import { isPromptActiveMemory } from '../agent/memory-prompt.ts';
|
|
8
9
|
import { getAgentRuntimeProfilesRoot, listAgentRuntimeProfiles, listAgentRuntimeProfileTemplates } from '../agent/runtime-profile.ts';
|
|
9
10
|
import { buildAgentWorkspaceChannels } from './agent-workspace-channels.ts';
|
|
@@ -233,6 +234,7 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
|
|
|
233
234
|
return { count: 0, enabled: 0, items: [] };
|
|
234
235
|
}
|
|
235
236
|
})();
|
|
237
|
+
const discoveredBehavior = summarizeAgentBehaviorDiscovery(context.workspace?.shellPaths);
|
|
236
238
|
const runtimeProfiles = (() => {
|
|
237
239
|
try {
|
|
238
240
|
return listAgentRuntimeProfiles(context.workspace?.shellPaths?.homeDirectory ?? '');
|
|
@@ -317,6 +319,9 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
|
|
|
317
319
|
skillBundleCount: skillSnapshot.bundleCount,
|
|
318
320
|
enabledSkillBundleCount: skillSnapshot.enabledBundleCount,
|
|
319
321
|
activePersonaName: personaSnapshot.activeName,
|
|
322
|
+
discoveredPersonas: discoveredBehavior.personas,
|
|
323
|
+
discoveredSkills: discoveredBehavior.skills,
|
|
324
|
+
discoveredRoutines: discoveredBehavior.routines,
|
|
320
325
|
readyChannelCount: channels.filter((channel) => channel.ready).length,
|
|
321
326
|
voiceProviderCount: voiceProviders.length,
|
|
322
327
|
mediaProviderCount: mediaProviders.length,
|
|
@@ -351,6 +356,7 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
|
|
|
351
356
|
localPersonaCount: personaSnapshot.count,
|
|
352
357
|
activePersonaName: personaSnapshot.activeName,
|
|
353
358
|
localPersonas: personaSnapshot.items,
|
|
359
|
+
discoveredBehavior,
|
|
354
360
|
knowledgeRoute: '/api/goodvibes-agent/knowledge',
|
|
355
361
|
knowledgeIsolation: 'agent-only',
|
|
356
362
|
executionPolicy: 'serial-proactive',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AgentWorkspaceChannelStatus } from './agent-workspace-channels.ts';
|
|
2
2
|
import type { AgentWorkspaceSetupChecklistItem } from './agent-workspace-setup.ts';
|
|
3
3
|
import type { AgentWorkspaceVoiceMediaReadiness } from './agent-workspace-voice-media.ts';
|
|
4
|
+
import type { AgentBehaviorDiscoverySnapshot } from '../agent/behavior-discovery-summary.ts';
|
|
4
5
|
|
|
5
6
|
export const AGENT_WORKSPACE_MODAL_NAME = 'agentWorkspace';
|
|
6
7
|
|
|
@@ -26,9 +27,13 @@ export type AgentWorkspaceEditorKind =
|
|
|
26
27
|
| 'tts-prompt'
|
|
27
28
|
| 'image-input'
|
|
28
29
|
| 'skill-bundle'
|
|
30
|
+
| 'persona-discovery-import'
|
|
31
|
+
| 'routine-discovery-import'
|
|
29
32
|
| 'skill-discovery-import'
|
|
30
33
|
| 'profile-template-export'
|
|
31
34
|
| 'profile-template-import'
|
|
35
|
+
| 'profile-template-from-discovered'
|
|
36
|
+
| 'profile-from-discovered'
|
|
32
37
|
| 'routine-schedule'
|
|
33
38
|
| 'reminder-schedule';
|
|
34
39
|
|
|
@@ -169,6 +174,7 @@ export interface AgentWorkspaceRuntimeSnapshot {
|
|
|
169
174
|
readonly localPersonaCount: number;
|
|
170
175
|
readonly activePersonaName: string;
|
|
171
176
|
readonly localPersonas: readonly AgentWorkspaceLocalLibraryItem[];
|
|
177
|
+
readonly discoveredBehavior: AgentBehaviorDiscoverySnapshot;
|
|
172
178
|
readonly knowledgeRoute: '/api/goodvibes-agent/knowledge';
|
|
173
179
|
readonly knowledgeIsolation: 'agent-only';
|
|
174
180
|
readonly executionPolicy: 'serial-proactive';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mkdirSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import {
|
|
4
|
+
createAgentRuntimeProfileFromDiscovered,
|
|
5
|
+
createAgentRuntimeProfileTemplateFromDiscovered,
|
|
4
6
|
createAgentRuntimeProfile,
|
|
5
7
|
deleteAgentRuntimeProfile,
|
|
6
8
|
exportAgentRuntimeProfileTemplate,
|
|
@@ -22,6 +24,12 @@ function parseFlag(args: readonly string[], name: string): string | undefined {
|
|
|
22
24
|
return value && !value.startsWith('--') ? value : undefined;
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
function parseCsvFlag(args: readonly string[], name: string): readonly string[] | undefined {
|
|
28
|
+
const raw = parseFlag(args, name);
|
|
29
|
+
if (!raw) return undefined;
|
|
30
|
+
return raw.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
function profileLine(profile: AgentRuntimeProfileInfo): string {
|
|
26
34
|
const created = profile.createdAt ? ` created=${profile.createdAt}` : '';
|
|
27
35
|
const starter = profile.starterTemplateId ? ` starter=${profile.starterTemplateId}` : '';
|
|
@@ -62,6 +70,7 @@ function renderTemplates(homeDirectory: string): string {
|
|
|
62
70
|
' edit the JSON file',
|
|
63
71
|
' /agent-profile template import ./agent-starter.json --yes',
|
|
64
72
|
' /agent-profile create <name> --template <imported-id> --yes',
|
|
73
|
+
' /agent-profile create-from-discovered <name> --yes',
|
|
65
74
|
].join('\n');
|
|
66
75
|
}
|
|
67
76
|
|
|
@@ -108,8 +117,8 @@ export function registerAgentRuntimeProfileRuntimeCommands(registry: CommandRegi
|
|
|
108
117
|
name: 'agent-profile',
|
|
109
118
|
aliases: ['runtime-profile', 'agent-profiles'],
|
|
110
119
|
description: 'Manage isolated Agent profiles and starter templates',
|
|
111
|
-
usage: '[list|templates|guide|template show|template export|template import|create|delete]',
|
|
112
|
-
handler(args, ctx) {
|
|
120
|
+
usage: '[list|templates|guide|template show|template export|template import|template from-discovered|create|create-from-discovered|delete]',
|
|
121
|
+
async handler(args, ctx) {
|
|
113
122
|
const shellPaths = requireShellPaths(ctx);
|
|
114
123
|
const homeDirectory = shellPaths.homeDirectory;
|
|
115
124
|
const parsed = stripYesFlag(args);
|
|
@@ -175,7 +184,35 @@ export function registerAgentRuntimeProfileRuntimeCommands(registry: CommandRegi
|
|
|
175
184
|
ctx.print(`Agent starter template imported: ${template.id}\n source: ${template.source}\n use: /agent-profile create <name> --template ${template.id} --yes`);
|
|
176
185
|
return;
|
|
177
186
|
}
|
|
178
|
-
|
|
187
|
+
if (mode === 'from-discovered' || mode === 'import-discovered') {
|
|
188
|
+
const templateId = commandArgs[2];
|
|
189
|
+
if (!templateId) {
|
|
190
|
+
ctx.print('Usage: /agent-profile template from-discovered <id> [--name <name>] [--description <summary>] [--persona <name>] [--skills all|name,name] [--routines all|name,name] [--replace] --yes');
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (!parsed.yes) {
|
|
194
|
+
requireYesFlag(ctx, `create Agent starter template ${templateId} from discovered behavior`, '/agent-profile template from-discovered <id> --yes');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const template = await createAgentRuntimeProfileTemplateFromDiscovered(shellPaths, {
|
|
198
|
+
id: templateId,
|
|
199
|
+
name: parseFlag(commandArgs, '--name'),
|
|
200
|
+
description: parseFlag(commandArgs, '--description'),
|
|
201
|
+
persona: parseFlag(commandArgs, '--persona'),
|
|
202
|
+
skills: parseCsvFlag(commandArgs, '--skills'),
|
|
203
|
+
routines: parseCsvFlag(commandArgs, '--routines'),
|
|
204
|
+
replace: commandArgs.includes('--replace'),
|
|
205
|
+
});
|
|
206
|
+
ctx.print([
|
|
207
|
+
`Agent starter template created from discovered behavior: ${template.id}`,
|
|
208
|
+
` persona: ${template.personaName}`,
|
|
209
|
+
` skills: ${template.skillNames.join(', ')}`,
|
|
210
|
+
` routines: ${template.routineNames.join(', ')}`,
|
|
211
|
+
` use: /agent-profile create <name> --template ${template.id} --yes`,
|
|
212
|
+
].join('\n'));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
ctx.print('Usage: /agent-profile template [show <id>|export <id> <path> --yes|import <path> --yes|from-discovered <id> --yes]');
|
|
179
216
|
return;
|
|
180
217
|
}
|
|
181
218
|
|
|
@@ -200,6 +237,38 @@ export function registerAgentRuntimeProfileRuntimeCommands(registry: CommandRegi
|
|
|
200
237
|
return;
|
|
201
238
|
}
|
|
202
239
|
|
|
240
|
+
if (sub === 'create-from-discovered' || sub === 'create-discovered') {
|
|
241
|
+
const profileName = commandArgs[1];
|
|
242
|
+
if (!profileName) {
|
|
243
|
+
ctx.print('Usage: /agent-profile create-from-discovered <name> [--template-id <id>] [--profile-name <display>] [--description <summary>] [--persona <name>] [--skills all|name,name] [--routines all|name,name] [--replace] --yes');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (!parsed.yes) {
|
|
247
|
+
requireYesFlag(ctx, `create Agent profile ${profileName} from discovered behavior`, '/agent-profile create-from-discovered <name> --yes');
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const created = await createAgentRuntimeProfileFromDiscovered(shellPaths, {
|
|
251
|
+
profileName,
|
|
252
|
+
templateId: parseFlag(commandArgs, '--template-id') ?? parseFlag(commandArgs, '--starter-id'),
|
|
253
|
+
name: parseFlag(commandArgs, '--profile-name') ?? parseFlag(commandArgs, '--name'),
|
|
254
|
+
description: parseFlag(commandArgs, '--description'),
|
|
255
|
+
persona: parseFlag(commandArgs, '--persona'),
|
|
256
|
+
skills: parseCsvFlag(commandArgs, '--skills'),
|
|
257
|
+
routines: parseCsvFlag(commandArgs, '--routines'),
|
|
258
|
+
replace: commandArgs.includes('--replace'),
|
|
259
|
+
});
|
|
260
|
+
ctx.print([
|
|
261
|
+
`Agent profile created from discovered behavior: ${created.profile.id}`,
|
|
262
|
+
` home: ${created.profile.homeDirectory}`,
|
|
263
|
+
` starter: ${created.template.id}`,
|
|
264
|
+
` persona: ${created.template.personaName}`,
|
|
265
|
+
` skills: ${created.template.skillNames.join(', ')}`,
|
|
266
|
+
` routines: ${created.template.routineNames.join(', ')}`,
|
|
267
|
+
` launch: goodvibes-agent --agent-profile ${created.profile.id}`,
|
|
268
|
+
].join('\n'));
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
203
272
|
if (sub === 'delete') {
|
|
204
273
|
const name = commandArgs[1];
|
|
205
274
|
if (!name) {
|
|
@@ -214,7 +283,7 @@ export function registerAgentRuntimeProfileRuntimeCommands(registry: CommandRegi
|
|
|
214
283
|
return;
|
|
215
284
|
}
|
|
216
285
|
|
|
217
|
-
ctx.print('Usage: /agent-profile [list|templates|guide|template show <id>|template export <id> <path> --yes|template import <path> --yes|create <name> [--template <id>] --yes|delete <name> --yes]');
|
|
286
|
+
ctx.print('Usage: /agent-profile [list|templates|guide|template show <id>|template export <id> <path> --yes|template import <path> --yes|create <name> [--template <id>] --yes|create-from-discovered <name> --yes|delete <name> --yes]');
|
|
218
287
|
} catch (error) {
|
|
219
288
|
ctx.print(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
220
289
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { discoverPersonas, type DiscoveredPersonaRecord } from '../../agent/persona-discovery.ts';
|
|
1
2
|
import { AgentPersonaRegistry, type AgentPersonaRecord } from '../../agent/persona-registry.ts';
|
|
2
3
|
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
3
4
|
import { requireShellPaths } from './runtime-services.ts';
|
|
@@ -82,6 +83,82 @@ function renderPersona(persona: AgentPersonaRecord, activePersonaId: string | nu
|
|
|
82
83
|
].filter(Boolean).join('\n');
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
function summarizeDiscoveredPersona(persona: DiscoveredPersonaRecord): string {
|
|
87
|
+
const description = persona.description ? ` - ${persona.description}` : '';
|
|
88
|
+
return ` ${persona.name} ${persona.origin}${description}\n path: ${persona.path}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function renderDiscoveredPersonas(personas: readonly DiscoveredPersonaRecord[]): string {
|
|
92
|
+
if (personas.length === 0) {
|
|
93
|
+
return [
|
|
94
|
+
'Discovered Agent persona files',
|
|
95
|
+
' No persona markdown files found in project/global Agent persona folders.',
|
|
96
|
+
' Search roots: .goodvibes/personas, .goodvibes/agent/personas, .goodvibes/agents, ~/.goodvibes/personas, ~/.goodvibes/agent/personas, ~/.goodvibes/agents',
|
|
97
|
+
].join('\n');
|
|
98
|
+
}
|
|
99
|
+
return [
|
|
100
|
+
`Discovered Agent persona files (${personas.length})`,
|
|
101
|
+
...personas.map(summarizeDiscoveredPersona),
|
|
102
|
+
'',
|
|
103
|
+
'Import one with: /personas import-discovered <name> --yes',
|
|
104
|
+
].join('\n');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function discoveredPersonaLookupValues(persona: DiscoveredPersonaRecord): readonly string[] {
|
|
108
|
+
const slug = persona.name.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
109
|
+
const basename = persona.path.split(/[\\/]/).pop()?.replace(/\.md$/i, '') ?? '';
|
|
110
|
+
return [persona.name, slug, persona.path, basename].map((value) => value.trim().toLowerCase()).filter(Boolean);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function findDiscoveredPersona(personas: readonly DiscoveredPersonaRecord[], idOrName: string): DiscoveredPersonaRecord | null {
|
|
114
|
+
const lookup = idOrName.trim().toLowerCase();
|
|
115
|
+
if (!lookup) return null;
|
|
116
|
+
return personas.find((persona) => discoveredPersonaLookupValues(persona).includes(lookup)) ?? null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function frontmatterList(persona: DiscoveredPersonaRecord, key: string): readonly string[] {
|
|
120
|
+
const value = persona.frontmatter[key];
|
|
121
|
+
if (!value) return [];
|
|
122
|
+
return splitList(value);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function importDiscoveredPersona(args: readonly string[], ctx: CommandContext, personaRegistry: AgentPersonaRegistry): Promise<void> {
|
|
126
|
+
const parsed = parsePersonaArgs(args);
|
|
127
|
+
const name = parsed.rest.join(' ').trim();
|
|
128
|
+
if (!name) {
|
|
129
|
+
ctx.print('Usage: /personas import-discovered <name> [--use] --yes');
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const discovered = findDiscoveredPersona(await discoverPersonas(requireShellPaths(ctx)), name);
|
|
133
|
+
if (!discovered) {
|
|
134
|
+
ctx.print(`Unknown discovered Agent persona: ${name}\nRun /personas discover to inspect available persona files.`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!parsed.yes) {
|
|
138
|
+
ctx.print([
|
|
139
|
+
'Agent persona import preview',
|
|
140
|
+
` name: ${discovered.name}`,
|
|
141
|
+
` origin: ${discovered.origin}`,
|
|
142
|
+
` path: ${discovered.path}`,
|
|
143
|
+
` description: ${discovered.description || '(none)'}`,
|
|
144
|
+
` body characters: ${discovered.body.length}`,
|
|
145
|
+
' next: rerun with --yes to import into the Agent-local persona registry',
|
|
146
|
+
].join('\n'));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const persona = personaRegistry.create({
|
|
150
|
+
name: discovered.name,
|
|
151
|
+
description: discovered.description || `Imported persona from ${discovered.origin} markdown file.`,
|
|
152
|
+
body: discovered.body,
|
|
153
|
+
tags: frontmatterList(discovered, 'tags'),
|
|
154
|
+
triggers: frontmatterList(discovered, 'triggers'),
|
|
155
|
+
source: 'imported',
|
|
156
|
+
provenance: `discovered:${discovered.origin}:${discovered.path}`,
|
|
157
|
+
});
|
|
158
|
+
if (parsed.flags.get('use') === 'true') personaRegistry.setActive(persona.id);
|
|
159
|
+
ctx.print(`Imported Agent persona ${persona.id}: ${persona.name}${parsed.flags.get('use') === 'true' ? ' (active)' : ''}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
85
162
|
function requiredFlag(flags: ReadonlyMap<string, string>, key: string): string {
|
|
86
163
|
const value = flags.get(key)?.trim();
|
|
87
164
|
if (!value) throw new Error(`Missing --${key}.`);
|
|
@@ -97,7 +174,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
97
174
|
name: 'personas',
|
|
98
175
|
aliases: ['persona'],
|
|
99
176
|
description: 'Manage local GoodVibes Agent personas',
|
|
100
|
-
usage: '[list|search <query>|show <id>|create --name <name> --description <summary> --body <instructions>|update <id> [--name ...] [--description ...] [--body ...]|use <id>|active|clear|review <id>|stale <id> <reason...>|delete <id> --yes]',
|
|
177
|
+
usage: '[list|discover|import-discovered <name> --yes|search <query>|show <id>|create --name <name> --description <summary> --body <instructions>|update <id> [--name ...] [--description ...] [--body ...]|use <id>|active|clear|review <id>|stale <id> <reason...>|delete <id> --yes]',
|
|
101
178
|
async handler(args, ctx) {
|
|
102
179
|
const sub = (args[0] ?? 'list').toLowerCase();
|
|
103
180
|
const registryStore = registryFromContext(ctx);
|
|
@@ -106,6 +183,14 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
106
183
|
ctx.print(renderList('Agent Personas', registryStore, registryStore.list()));
|
|
107
184
|
return;
|
|
108
185
|
}
|
|
186
|
+
if (sub === 'discover' || sub === 'discovered') {
|
|
187
|
+
ctx.print(renderDiscoveredPersonas(await discoverPersonas(requireShellPaths(ctx))));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (sub === 'import-discovered' || sub === 'import-persona') {
|
|
191
|
+
await importDiscoveredPersona(args.slice(1), ctx, registryStore);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
109
194
|
if (sub === 'search') {
|
|
110
195
|
const query = args.slice(1).join(' ').trim();
|
|
111
196
|
ctx.print(renderList(query ? `Agent Personas matching "${query}"` : 'Agent Personas', registryStore, registryStore.search(query)));
|
|
@@ -210,7 +295,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
|
|
|
210
295
|
ctx.print(`Deleted Agent persona ${removed.id}: ${removed.name}`);
|
|
211
296
|
return;
|
|
212
297
|
}
|
|
213
|
-
ctx.print('Usage: /personas [list|search <query>|show <id>|create|update|use|active|clear|review|stale|delete]');
|
|
298
|
+
ctx.print('Usage: /personas [list|discover|import-discovered|search <query>|show <id>|create|update|use|active|clear|review|stale|delete]');
|
|
214
299
|
} catch (error) {
|
|
215
300
|
printError(ctx, error);
|
|
216
301
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { discoverRoutines, type DiscoveredRoutineRecord } from '../../agent/routine-discovery.ts';
|
|
1
2
|
import { AgentRoutineRegistry, type AgentRoutineRecord } from '../../agent/routine-registry.ts';
|
|
2
3
|
import {
|
|
3
4
|
buildRoutineSchedulePreview,
|
|
@@ -111,10 +112,86 @@ function renderRoutine(routine: AgentRoutineRecord): string {
|
|
|
111
112
|
].filter(Boolean).join('\n');
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
function summarizeDiscoveredRoutine(routine: DiscoveredRoutineRecord): string {
|
|
116
|
+
const description = routine.description ? ` - ${routine.description}` : '';
|
|
117
|
+
return ` ${routine.name} ${routine.origin}${description}\n path: ${routine.path}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function renderDiscoveredRoutines(routines: readonly DiscoveredRoutineRecord[]): string {
|
|
121
|
+
if (routines.length === 0) {
|
|
122
|
+
return [
|
|
123
|
+
'Discovered Agent routine files',
|
|
124
|
+
' No routine markdown files found in project/global Agent routine folders.',
|
|
125
|
+
' Search roots: .goodvibes/routines, .goodvibes/agent/routines, ~/.goodvibes/routines, ~/.goodvibes/agent/routines',
|
|
126
|
+
].join('\n');
|
|
127
|
+
}
|
|
128
|
+
return [
|
|
129
|
+
`Discovered Agent routine files (${routines.length})`,
|
|
130
|
+
...routines.map(summarizeDiscoveredRoutine),
|
|
131
|
+
'',
|
|
132
|
+
'Import one with: /routines import-discovered <name> --yes',
|
|
133
|
+
].join('\n');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function discoveredRoutineLookupValues(routine: DiscoveredRoutineRecord): readonly string[] {
|
|
137
|
+
const slug = routine.name.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
138
|
+
const basename = routine.path.split(/[\\/]/).pop()?.replace(/\.md$/i, '') ?? '';
|
|
139
|
+
return [routine.name, slug, routine.path, basename].map((value) => value.trim().toLowerCase()).filter(Boolean);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function findDiscoveredRoutine(routines: readonly DiscoveredRoutineRecord[], idOrName: string): DiscoveredRoutineRecord | null {
|
|
143
|
+
const lookup = idOrName.trim().toLowerCase();
|
|
144
|
+
if (!lookup) return null;
|
|
145
|
+
return routines.find((routine) => discoveredRoutineLookupValues(routine).includes(lookup)) ?? null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function frontmatterList(routine: DiscoveredRoutineRecord, key: string): readonly string[] {
|
|
149
|
+
const value = routine.frontmatter[key];
|
|
150
|
+
if (!value) return [];
|
|
151
|
+
return splitList(value);
|
|
152
|
+
}
|
|
153
|
+
|
|
114
154
|
function printError(ctx: CommandContext, error: unknown): void {
|
|
115
155
|
ctx.print(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
116
156
|
}
|
|
117
157
|
|
|
158
|
+
async function importDiscoveredRoutine(args: readonly string[], ctx: CommandContext, routineRegistry: AgentRoutineRegistry): Promise<void> {
|
|
159
|
+
const parsed = parseRoutineArgs(args);
|
|
160
|
+
const name = parsed.rest.join(' ').trim();
|
|
161
|
+
if (!name) {
|
|
162
|
+
ctx.print('Usage: /routines import-discovered <name> [--enabled] --yes');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const discovered = findDiscoveredRoutine(await discoverRoutines(requireShellPaths(ctx)), name);
|
|
166
|
+
if (!discovered) {
|
|
167
|
+
ctx.print(`Unknown discovered Agent routine: ${name}\nRun /routines discover to inspect available routine files.`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (!parsed.yes) {
|
|
171
|
+
ctx.print([
|
|
172
|
+
'Agent routine import preview',
|
|
173
|
+
` name: ${discovered.name}`,
|
|
174
|
+
` origin: ${discovered.origin}`,
|
|
175
|
+
` path: ${discovered.path}`,
|
|
176
|
+
` description: ${discovered.description || '(none)'}`,
|
|
177
|
+
` steps characters: ${discovered.steps.length}`,
|
|
178
|
+
' next: rerun with --yes to import into the Agent-local routine registry',
|
|
179
|
+
].join('\n'));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const routine = routineRegistry.create({
|
|
183
|
+
name: discovered.name,
|
|
184
|
+
description: discovered.description || `Imported routine from ${discovered.origin} markdown file.`,
|
|
185
|
+
steps: discovered.steps,
|
|
186
|
+
tags: frontmatterList(discovered, 'tags'),
|
|
187
|
+
triggers: frontmatterList(discovered, 'triggers'),
|
|
188
|
+
enabled: parsed.flags.get('enabled') === 'true',
|
|
189
|
+
source: 'imported',
|
|
190
|
+
provenance: `discovered:${discovered.origin}:${discovered.path}`,
|
|
191
|
+
});
|
|
192
|
+
ctx.print(`Imported Agent routine ${routine.id}: ${routine.name}${routine.enabled ? ' (enabled)' : ''}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
118
195
|
async function promoteRoutine(args: readonly string[], routineRegistry: AgentRoutineRegistry, ctx: CommandContext): Promise<void> {
|
|
119
196
|
const parsed = parseRoutineSchedulePromotionArgs(args);
|
|
120
197
|
if (parsed.errors.length > 0) {
|
|
@@ -154,6 +231,14 @@ export async function runRoutinesRuntimeCommand(args: readonly string[], ctx: Co
|
|
|
154
231
|
ctx.print(renderList('Enabled Agent Routines', routineRegistry, snapshot.enabledRoutines));
|
|
155
232
|
return;
|
|
156
233
|
}
|
|
234
|
+
if (sub === 'discover' || sub === 'discovered') {
|
|
235
|
+
ctx.print(renderDiscoveredRoutines(await discoverRoutines(requireShellPaths(ctx))));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (sub === 'import-discovered' || sub === 'import-routine') {
|
|
239
|
+
await importDiscoveredRoutine(args.slice(1), ctx, routineRegistry);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
157
242
|
if (sub === 'search') {
|
|
158
243
|
const query = args.slice(1).join(' ').trim();
|
|
159
244
|
ctx.print(renderList(query ? `Agent Routines matching "${query}"` : 'Agent Routines', routineRegistry, routineRegistry.search(query)));
|
|
@@ -288,7 +373,7 @@ export async function runRoutinesRuntimeCommand(args: readonly string[], ctx: Co
|
|
|
288
373
|
ctx.print(`Deleted Agent routine ${removed.id}: ${removed.name}`);
|
|
289
374
|
return;
|
|
290
375
|
}
|
|
291
|
-
ctx.print('Usage: /routines [list|enabled|search|show|receipts|reconcile|receipt|create|update|enable|disable|start|review|stale|promote|delete]');
|
|
376
|
+
ctx.print('Usage: /routines [list|enabled|discover|import-discovered|search|show|receipts|reconcile|receipt|create|update|enable|disable|start|review|stale|promote|delete]');
|
|
292
377
|
} catch (error) {
|
|
293
378
|
printError(ctx, error);
|
|
294
379
|
}
|
|
@@ -299,7 +384,7 @@ export function registerRoutinesRuntimeCommands(registry: CommandRegistry): void
|
|
|
299
384
|
name: 'routines',
|
|
300
385
|
aliases: ['routine'],
|
|
301
386
|
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-channel slack] --yes|delete <id> --yes]',
|
|
387
|
+
usage: '[list|enabled|discover|import-discovered <name> --yes|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
388
|
handler: runRoutinesRuntimeCommand,
|
|
304
389
|
});
|
|
305
390
|
}
|
|
@@ -1,4 +1,31 @@
|
|
|
1
1
|
import type { OnboardingWizardStepDefinition } from './onboarding-wizard-types.ts';
|
|
2
|
+
import type { AgentBehaviorDiscoverySnapshot } from '../../agent/behavior-discovery-summary.ts';
|
|
3
|
+
|
|
4
|
+
function discoveryCount(discovery: AgentBehaviorDiscoverySnapshot | undefined): number {
|
|
5
|
+
if (!discovery) return 0;
|
|
6
|
+
return discovery.personas.count + discovery.skills.count + discovery.routines.count;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function discoverySummary(discovery: AgentBehaviorDiscoverySnapshot | undefined): string {
|
|
10
|
+
if (!discovery || discoveryCount(discovery) === 0) return 'No importable local behavior files found yet';
|
|
11
|
+
return [
|
|
12
|
+
`${discovery.personas.count} persona file(s)`,
|
|
13
|
+
`${discovery.skills.count} skill file(s)`,
|
|
14
|
+
`${discovery.routines.count} routine file(s)`,
|
|
15
|
+
].join('; ');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function discoverySample(discovery: AgentBehaviorDiscoverySnapshot | undefined): string {
|
|
19
|
+
if (!discovery) return '';
|
|
20
|
+
const names = [
|
|
21
|
+
...discovery.personas.names,
|
|
22
|
+
...discovery.skills.names,
|
|
23
|
+
...discovery.routines.names,
|
|
24
|
+
].slice(0, 4);
|
|
25
|
+
if (names.length === 0) return 'Use /personas discover, /agent-skills discover, and /routines discover after setup to rescan.';
|
|
26
|
+
const remaining = discoveryCount(discovery) > names.length ? `, +${discoveryCount(discovery) - names.length} more` : '';
|
|
27
|
+
return `Import candidates: ${names.join(', ')}${remaining}.`;
|
|
28
|
+
}
|
|
2
29
|
|
|
3
30
|
export function buildCommunicationStep(): OnboardingWizardStepDefinition {
|
|
4
31
|
return {
|
|
@@ -128,15 +155,19 @@ export function buildAgentKnowledgeStep(): OnboardingWizardStepDefinition {
|
|
|
128
155
|
};
|
|
129
156
|
}
|
|
130
157
|
|
|
131
|
-
export function buildLocalStateStep(): OnboardingWizardStepDefinition {
|
|
158
|
+
export function buildLocalStateStep(discovery?: AgentBehaviorDiscoverySnapshot): OnboardingWizardStepDefinition {
|
|
159
|
+
const discoveredCount = discoveryCount(discovery);
|
|
132
160
|
return {
|
|
133
161
|
id: 'agent-local-state',
|
|
134
162
|
title: 'Local memory and behavior',
|
|
135
|
-
shortLabel: '
|
|
136
|
-
description:
|
|
163
|
+
shortLabel: 'Behavior',
|
|
164
|
+
description: discoveredCount > 0
|
|
165
|
+
? 'Review importable Agent-local behavior files, then create an isolated profile from them or import individual records.'
|
|
166
|
+
: 'Review the Agent-local behavior model. Memory, personas, skills, routines, and Agent profiles stay local until a stable shared registry exists.',
|
|
137
167
|
summaryTitle: 'Local Agent state',
|
|
138
168
|
summaryLines: [
|
|
139
169
|
'Memory/personas/skills/routines: local Agent registries',
|
|
170
|
+
`Discovered behavior files: ${discoverySummary(discovery)}`,
|
|
140
171
|
'Secrets: rejected or stored by secret reference',
|
|
141
172
|
'Profiles: isolated Agent homes',
|
|
142
173
|
],
|
|
@@ -152,22 +183,28 @@ export function buildLocalStateStep(): OnboardingWizardStepDefinition {
|
|
|
152
183
|
kind: 'status',
|
|
153
184
|
id: 'agent-local-state.personas',
|
|
154
185
|
label: 'Personas',
|
|
155
|
-
hint:
|
|
156
|
-
|
|
186
|
+
hint: discovery?.personas.count && discovery.personas.count > 0
|
|
187
|
+
? `${discovery.personas.count} persona file(s) are available. Use the Profiles workspace to create a profile from discovered behavior, or preview individual files with /personas discover.`
|
|
188
|
+
: 'Use /personas to create and activate serial operating modes for the main conversation.',
|
|
189
|
+
defaultValue: discovery?.personas.count && discovery.personas.count > 0 ? `${discovery.personas.count} discovered` : 'Local registry',
|
|
157
190
|
},
|
|
158
191
|
{
|
|
159
192
|
kind: 'status',
|
|
160
193
|
id: 'agent-local-state.skills',
|
|
161
194
|
label: 'Skills',
|
|
162
|
-
hint:
|
|
163
|
-
|
|
195
|
+
hint: discovery?.skills.count && discovery.skills.count > 0
|
|
196
|
+
? `${discovery.skills.count} skill file(s) are available. Use the Profiles workspace to create a profile from discovered behavior, or preview individual files with /agent-skills discover.`
|
|
197
|
+
: 'Use /agent-skills and /skills local to manage reusable Agent procedures.',
|
|
198
|
+
defaultValue: discovery?.skills.count && discovery.skills.count > 0 ? `${discovery.skills.count} discovered` : 'Local registry',
|
|
164
199
|
},
|
|
165
200
|
{
|
|
166
201
|
kind: 'status',
|
|
167
202
|
id: 'agent-local-state.routines',
|
|
168
203
|
label: 'Routines',
|
|
169
|
-
hint:
|
|
170
|
-
|
|
204
|
+
hint: discovery?.routines.count && discovery.routines.count > 0
|
|
205
|
+
? `${discovery.routines.count} routine file(s) are available. Use the Profiles workspace to create a profile from discovered behavior, or preview individual files with /routines discover. ${discoverySample(discovery)}`
|
|
206
|
+
: 'Use /routines for reusable local procedures. Starting a routine prints steps in the main conversation and does not spawn hidden work.',
|
|
207
|
+
defaultValue: discovery?.routines.count && discovery.routines.count > 0 ? `${discovery.routines.count} discovered` : 'Local registry',
|
|
171
208
|
},
|
|
172
209
|
],
|
|
173
210
|
};
|
|
@@ -31,7 +31,7 @@ export function buildOnboardingWizardSteps(controller: OnboardingWizardControlle
|
|
|
31
31
|
buildCommunicationStep(),
|
|
32
32
|
buildToolsStep(),
|
|
33
33
|
buildAgentKnowledgeStep(),
|
|
34
|
-
buildLocalStateStep(),
|
|
34
|
+
buildLocalStateStep(controller.runtimeSnapshot?.localBehaviorDiscovery),
|
|
35
35
|
buildAutomationStep(),
|
|
36
36
|
buildVoiceMediaStep(),
|
|
37
37
|
buildDelegationPolicyStep(),
|
|
@@ -104,6 +104,31 @@ function setupChecklistLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLi
|
|
|
104
104
|
return lines;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
function discoverySummaryLine(label: string, summary: AgentWorkspaceRuntimeSnapshot['discoveredBehavior']['personas'], command: string): ContextLine[] {
|
|
108
|
+
if (summary.count === 0) return [];
|
|
109
|
+
const names = summary.names.length > 0
|
|
110
|
+
? ` ${summary.names.join(', ')}${summary.count > summary.names.length ? `, +${summary.count - summary.names.length} more` : ''}.`
|
|
111
|
+
: '';
|
|
112
|
+
return [
|
|
113
|
+
{ text: `${label}: ${summary.count} discovered; project ${summary.projectLocalCount}; global ${summary.globalCount}.`, fg: PALETTE.info, bold: true },
|
|
114
|
+
{ text: ` ${command} to preview, then use the import form after review.${names}`, fg: PALETTE.muted },
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function behaviorDiscoveryLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
|
|
119
|
+
const lines: ContextLine[] = [
|
|
120
|
+
...discoverySummaryLine('Discovered personas', snapshot.discoveredBehavior.personas, '/personas discover'),
|
|
121
|
+
...discoverySummaryLine('Discovered skills', snapshot.discoveredBehavior.skills, '/agent-skills discover'),
|
|
122
|
+
...discoverySummaryLine('Discovered routines', snapshot.discoveredBehavior.routines, '/routines discover'),
|
|
123
|
+
];
|
|
124
|
+
if (lines.length === 0) return [];
|
|
125
|
+
return [
|
|
126
|
+
{ text: '' },
|
|
127
|
+
{ text: 'Discovered Behavior Files', fg: PALETTE.title, bold: true },
|
|
128
|
+
...lines,
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
|
|
107
132
|
function localLibraryLines(
|
|
108
133
|
title: string,
|
|
109
134
|
items: readonly AgentWorkspaceRuntimeSnapshot['localPersonas'][number][],
|
|
@@ -199,6 +224,7 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
199
224
|
{ text: `Connection: ${snapshot.runtimeBaseUrl}`, fg: PALETTE.info },
|
|
200
225
|
{ text: 'Agent role: interactive operator TUI; setup changes here are Agent-local.', fg: PALETTE.good },
|
|
201
226
|
...setupChecklistLines(snapshot),
|
|
227
|
+
...behaviorDiscoveryLines(snapshot),
|
|
202
228
|
{ text: '' },
|
|
203
229
|
{ text: `Workspace: ${snapshot.workingDirectory}`, fg: PALETTE.muted },
|
|
204
230
|
{ text: `Home: ${snapshot.homeDirectory}`, fg: PALETTE.muted },
|
|
@@ -211,7 +211,8 @@ function hasExternalIntegrations(snapshot: OnboardingSnapshotState): boolean {
|
|
|
211
211
|
function hasLocalBehaviorCustomization(snapshot: OnboardingSnapshotState): boolean {
|
|
212
212
|
return hasCustomizedWorkspaceDefaults(snapshot)
|
|
213
213
|
|| countPermissionToolOverrides(snapshot) > 0
|
|
214
|
-
|| snapshot.runtimeDefaults.secretStoragePolicy !== DEFAULT_CONFIG.storage.secretPolicy
|
|
214
|
+
|| snapshot.runtimeDefaults.secretStoragePolicy !== DEFAULT_CONFIG.storage.secretPolicy
|
|
215
|
+
|| countDiscoveredLocalBehavior(snapshot) > 0;
|
|
215
216
|
}
|
|
216
217
|
|
|
217
218
|
function hasCommunicationChannelSignals(snapshot: OnboardingSnapshotState): boolean {
|
|
@@ -240,6 +241,18 @@ function describeAgentKnowledge(): string {
|
|
|
240
241
|
}
|
|
241
242
|
|
|
242
243
|
function describeLocalBehavior(snapshot: OnboardingSnapshotState): string {
|
|
244
|
+
const discoveredCount = countDiscoveredLocalBehavior(snapshot);
|
|
245
|
+
if (discoveredCount > 0) {
|
|
246
|
+
const discovery = snapshot.localBehaviorDiscovery;
|
|
247
|
+
const samples = [
|
|
248
|
+
...discovery.personas.names,
|
|
249
|
+
...discovery.skills.names,
|
|
250
|
+
...discovery.routines.names,
|
|
251
|
+
].slice(0, 4);
|
|
252
|
+
const sampleText = samples.length > 0 ? ` Found: ${samples.join(', ')}.` : '';
|
|
253
|
+
return `Import ${discoveredCount} discovered Agent persona/skill/routine file(s) from local Agent folders before creating blank behavior.${sampleText}`;
|
|
254
|
+
}
|
|
255
|
+
|
|
243
256
|
if (!hasLocalBehaviorCustomization(snapshot)) {
|
|
244
257
|
return 'Configure local memory, routines, skills, personas, permissions, and secret handling before the Agent starts doing useful work.';
|
|
245
258
|
}
|
|
@@ -247,6 +260,12 @@ function describeLocalBehavior(snapshot: OnboardingSnapshotState): string {
|
|
|
247
260
|
return 'Review existing local behavior, permission, display, or secret-handling choices before applying Agent setup.';
|
|
248
261
|
}
|
|
249
262
|
|
|
263
|
+
function countDiscoveredLocalBehavior(snapshot: OnboardingSnapshotState): number {
|
|
264
|
+
return snapshot.localBehaviorDiscovery.personas.count
|
|
265
|
+
+ snapshot.localBehaviorDiscovery.skills.count
|
|
266
|
+
+ snapshot.localBehaviorDiscovery.routines.count;
|
|
267
|
+
}
|
|
268
|
+
|
|
250
269
|
function describeCommunicationChannels(snapshot: OnboardingSnapshotState): string {
|
|
251
270
|
const integrationCount = new Set<string>([
|
|
252
271
|
...getExternalIntegrationServiceIds(snapshot),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SecretStorageReview } from '../../config/secrets.ts';
|
|
2
2
|
import { getProviderIdFromModel } from '../../config/provider-model.ts';
|
|
3
3
|
import type { LocalAuthSnapshot } from '@pellux/goodvibes-sdk/platform/security';
|
|
4
|
+
import { summarizeAgentBehaviorDiscovery } from '../../agent/behavior-discovery-summary.ts';
|
|
4
5
|
import { readOnboardingRuntimeState } from './state.ts';
|
|
5
6
|
import type {
|
|
6
7
|
OnboardingAcknowledgementSnapshot,
|
|
@@ -400,6 +401,7 @@ export async function collectOnboardingSnapshot(
|
|
|
400
401
|
records: sortSurfaceRecords(surfaceResult.value),
|
|
401
402
|
},
|
|
402
403
|
providerAccounts: providerAccountsResult.value,
|
|
404
|
+
localBehaviorDiscovery: summarizeAgentBehaviorDiscovery(deps.shellPaths),
|
|
403
405
|
collectionIssues,
|
|
404
406
|
};
|
|
405
407
|
}
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
ServiceInspectionQuery,
|
|
9
9
|
SubscriptionAccessQuery,
|
|
10
10
|
} from '../ui-service-queries.ts';
|
|
11
|
+
import type { AgentBehaviorDiscoverySnapshot } from '../../agent/behavior-discovery-summary.ts';
|
|
11
12
|
|
|
12
13
|
export type OnboardingMode = 'new' | 'edit' | 'reopen';
|
|
13
14
|
|
|
@@ -189,6 +190,7 @@ export interface OnboardingSnapshotState {
|
|
|
189
190
|
readonly bindSettings: OnboardingBindSettingsSnapshot;
|
|
190
191
|
readonly surfaces: OnboardingSurfacesSnapshot;
|
|
191
192
|
readonly providerAccounts: OnboardingProviderAccountsSnapshot | null;
|
|
193
|
+
readonly localBehaviorDiscovery: AgentBehaviorDiscoverySnapshot;
|
|
192
194
|
readonly collectionIssues: readonly OnboardingSnapshotCollectionIssue[];
|
|
193
195
|
}
|
|
194
196
|
|