@pellux/goodvibes-agent 0.1.108 → 0.1.109
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 +4 -0
- package/package.json +1 -1
- package/src/agent/memory-prompt.ts +35 -0
- package/src/input/agent-workspace-snapshot.ts +5 -2
- package/src/input/agent-workspace-types.ts +1 -0
- package/src/input/commands/brief-runtime.ts +1 -1
- package/src/renderer/agent-workspace.ts +1 -1
- package/src/runtime/bootstrap-core.ts +1 -1
- package/src/runtime/bootstrap.ts +3 -1
- package/src/tools/agent-local-registry-tool.ts +208 -9
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.109",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "GoodVibes personal operator assistant TUI with a proactive Agent product brain, isolated Agent Knowledge, local profiles, routines, skills, personas, and explicit build delegation.",
|
|
6
6
|
"type": "module",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { MemoryRecord, MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_LIMIT = 10;
|
|
4
|
+
export const MIN_PROMPT_MEMORY_CONFIDENCE = 50;
|
|
5
|
+
|
|
6
|
+
export function isPromptActiveMemory(record: MemoryRecord): boolean {
|
|
7
|
+
return record.reviewState === 'reviewed' && record.confidence >= MIN_PROMPT_MEMORY_CONFIDENCE;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function sortMemoryForPrompt(left: MemoryRecord, right: MemoryRecord): number {
|
|
11
|
+
if (right.confidence !== left.confidence) return right.confidence - left.confidence;
|
|
12
|
+
return right.updatedAt - left.updatedAt;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function formatMemoryLine(record: MemoryRecord): string {
|
|
16
|
+
const tags = record.tags.length > 0 ? ` tags=${record.tags.join(',')}` : '';
|
|
17
|
+
const provenance = record.provenance.length > 0
|
|
18
|
+
? ` source=${record.provenance.slice(0, 2).map((entry) => `${entry.kind}:${entry.ref}`).join(',')}`
|
|
19
|
+
: '';
|
|
20
|
+
return `- [${record.scope}/${record.cls} ${record.confidence}%${tags}${provenance}] ${record.summary}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function buildReviewedMemoryPrompt(memoryRegistry: MemoryRegistry, limit = DEFAULT_LIMIT): string | null {
|
|
24
|
+
const records = memoryRegistry.getAll()
|
|
25
|
+
.filter(isPromptActiveMemory)
|
|
26
|
+
.sort(sortMemoryForPrompt)
|
|
27
|
+
.slice(0, Math.max(0, limit));
|
|
28
|
+
|
|
29
|
+
if (records.length === 0) return null;
|
|
30
|
+
return [
|
|
31
|
+
'## Reviewed GoodVibes Agent Memory',
|
|
32
|
+
'Use these local, reviewed, non-secret memory records to avoid asking repeat questions and to preserve durable user preferences, constraints, and operating facts.',
|
|
33
|
+
...records.map(formatMemoryLine),
|
|
34
|
+
].join('\n');
|
|
35
|
+
}
|
|
@@ -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 { isPromptActiveMemory } from '../agent/memory-prompt.ts';
|
|
7
8
|
import { getAgentRuntimeProfilesRoot, listAgentRuntimeProfiles, listAgentRuntimeProfileTemplates } from '../agent/runtime-profile.ts';
|
|
8
9
|
import { buildAgentWorkspaceChannels } from './agent-workspace-channels.ts';
|
|
9
10
|
import { buildAgentWorkspaceSetupChecklist } from './agent-workspace-setup.ts';
|
|
@@ -174,15 +175,16 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
|
|
|
174
175
|
const memorySnapshot = (() => {
|
|
175
176
|
try {
|
|
176
177
|
const memory = context.clients?.agentKnowledgeApi?.memory;
|
|
177
|
-
if (!memory) return { count: 0, reviewQueueCount: 0, items: [] };
|
|
178
|
+
if (!memory) return { count: 0, reviewQueueCount: 0, promptActiveCount: 0, items: [] };
|
|
178
179
|
const records = [...memory.getAll()].sort((left, right) => right.updatedAt - left.updatedAt);
|
|
179
180
|
return {
|
|
180
181
|
count: records.length,
|
|
181
182
|
reviewQueueCount: memory.reviewQueue(100).length,
|
|
183
|
+
promptActiveCount: records.filter(isPromptActiveMemory).length,
|
|
182
184
|
items: records.map(summarizeMemoryItem),
|
|
183
185
|
};
|
|
184
186
|
} catch {
|
|
185
|
-
return { count: 0, reviewQueueCount: 0, items: [] };
|
|
187
|
+
return { count: 0, reviewQueueCount: 0, promptActiveCount: 0, items: [] };
|
|
186
188
|
}
|
|
187
189
|
})();
|
|
188
190
|
const personaSnapshot = (() => {
|
|
@@ -323,6 +325,7 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
|
|
|
323
325
|
sessionMemoryCount,
|
|
324
326
|
localMemoryCount: memorySnapshot.count,
|
|
325
327
|
localMemoryReviewQueueCount: memorySnapshot.reviewQueueCount,
|
|
328
|
+
localMemoryPromptActiveCount: memorySnapshot.promptActiveCount,
|
|
326
329
|
localMemories: memorySnapshot.items,
|
|
327
330
|
localRoutineCount: routineSnapshot.count,
|
|
328
331
|
enabledRoutineCount: routineSnapshot.enabled,
|
|
@@ -150,6 +150,7 @@ export interface AgentWorkspaceRuntimeSnapshot {
|
|
|
150
150
|
readonly sessionMemoryCount: number;
|
|
151
151
|
readonly localMemoryCount: number;
|
|
152
152
|
readonly localMemoryReviewQueueCount: number;
|
|
153
|
+
readonly localMemoryPromptActiveCount: number;
|
|
153
154
|
readonly localMemories: readonly AgentWorkspaceLocalLibraryItem[];
|
|
154
155
|
readonly localRoutineCount: number;
|
|
155
156
|
readonly enabledRoutineCount: number;
|
|
@@ -96,7 +96,7 @@ export function formatAgentOperatorBriefing(ctx: CommandContext): string {
|
|
|
96
96
|
'',
|
|
97
97
|
'Readiness',
|
|
98
98
|
` setup: ${setupReady}/${snapshot.setupChecklist.length} ready; ${setupRecommended} recommended; ${setupBlocked} blocked`,
|
|
99
|
-
` local memory: ${plural(snapshot.localMemoryCount, 'record')}; review queue ${snapshot.localMemoryReviewQueueCount}`,
|
|
99
|
+
` local memory: ${plural(snapshot.localMemoryCount, 'record')}; prompt-active ${snapshot.localMemoryPromptActiveCount}; review queue ${snapshot.localMemoryReviewQueueCount}`,
|
|
100
100
|
` personas: ${plural(snapshot.localPersonaCount, 'persona')}; active ${snapshot.activePersonaName}`,
|
|
101
101
|
` skills: ${snapshot.enabledSkillCount}/${snapshot.localSkillCount} enabled; bundles ${snapshot.enabledSkillBundleCount}/${snapshot.localSkillBundleCount}; active ${snapshot.activeSkillCount}`,
|
|
102
102
|
` routines: ${snapshot.enabledRoutineCount}/${snapshot.localRoutineCount} enabled`,
|
|
@@ -303,7 +303,7 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
303
303
|
} else if (category.id === 'memory') {
|
|
304
304
|
base.push(
|
|
305
305
|
{ text: `Session memories: ${snapshot.sessionMemoryCount}`, fg: PALETTE.info },
|
|
306
|
-
{ text: `Agent memory: ${snapshot.localMemoryCount}; review queue: ${snapshot.localMemoryReviewQueueCount}`, fg: PALETTE.info },
|
|
306
|
+
{ text: `Agent memory: ${snapshot.localMemoryCount}; prompt-active: ${snapshot.localMemoryPromptActiveCount}; review queue: ${snapshot.localMemoryReviewQueueCount}`, fg: PALETTE.info },
|
|
307
307
|
{ text: `Local routines: ${snapshot.localRoutineCount}; enabled: ${snapshot.enabledRoutineCount}`, fg: PALETTE.info },
|
|
308
308
|
{ text: `Local skills: ${snapshot.localSkillCount}; enabled: ${snapshot.enabledSkillCount}; bundles: ${snapshot.localSkillBundleCount}; active skills: ${snapshot.activeSkillCount}`, fg: PALETTE.info },
|
|
309
309
|
{ text: `Local personas: ${snapshot.localPersonaCount}; active: ${snapshot.activePersonaName}`, fg: PALETTE.info },
|
|
@@ -228,7 +228,7 @@ export async function initializeBootstrapCore(
|
|
|
228
228
|
overflowHandler: services.overflowHandler,
|
|
229
229
|
changeTracker: services.sessionChangeTracker,
|
|
230
230
|
});
|
|
231
|
-
registerAgentLocalRegistryTool(toolRegistry, services.shellPaths);
|
|
231
|
+
registerAgentLocalRegistryTool(toolRegistry, services.shellPaths, services.memoryRegistry);
|
|
232
232
|
installAgentToolPolicyGuard(toolRegistry, {
|
|
233
233
|
getLastUserMessage: () => conversation.getLastUserMessage(),
|
|
234
234
|
});
|
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -43,12 +43,13 @@ import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
|
43
43
|
import { buildActivePersonaPrompt } from '../agent/persona-registry.ts';
|
|
44
44
|
import { buildEnabledSkillsPrompt } from '../agent/skill-registry.ts';
|
|
45
45
|
import { buildEnabledRoutinesPrompt } from '../agent/routine-registry.ts';
|
|
46
|
+
import { buildReviewedMemoryPrompt } from '../agent/memory-prompt.ts';
|
|
46
47
|
|
|
47
48
|
const GOODVIBES_AGENT_OPERATOR_POLICY = [
|
|
48
49
|
'## GoodVibes Agent Operator Policy',
|
|
49
50
|
'- Default to serial, proactive assistant work in the main conversation. Answer, inspect, summarize, remember useful non-secret facts, configure local Agent state, use read-only connected-service/operator routes, and take safe non-destructive actions without spawning local agents or WRFC.',
|
|
50
51
|
'- GoodVibes Agent connects to GoodVibes services owned outside this package. Do not start, stop, restart, install, expose, or mutate connected-service network/listener posture from Agent.',
|
|
51
|
-
'- Use the `agent_local_registry` tool when a reusable persona, skill, or routine would improve future work. Keep those records local, non-secret, source/provenance tagged, and reviewable. Starting a routine means applying its steps in this same serial conversation, not creating a background job.',
|
|
52
|
+
'- Use the `agent_local_registry` tool when a durable memory, reusable persona, skill, skill bundle, or routine would improve future work. Keep those records local, non-secret, source/provenance tagged, and reviewable. Review memory with a confidence score when it should shape future turns. Starting a routine means applying its steps in this same serial conversation, not creating a background job.',
|
|
52
53
|
'- WRFC is never the default Agent reasoning path. Do not create local WRFC chains for planning, research, operations, knowledge, memory, configuration, approvals, automation observability, or ordinary assistant work.',
|
|
53
54
|
'- GoodVibes Agent is not the coding TUI. Do not use the `agent` tool to spawn local Engineer, Reviewer, Tester, Verifier, or batch-spawn roots from Agent.',
|
|
54
55
|
'- When the user explicitly asks to build, implement, fix, patch, or review code, preserve the full original user ask and delegate one build request to GoodVibes TUI through the public shared-session/build-delegation contract. Include clear executionIntent and request WRFC only for explicit build/fix/review work or when the user explicitly asks for WRFC/agent review.',
|
|
@@ -206,6 +207,7 @@ export async function bootstrapRuntime(
|
|
|
206
207
|
return joinPromptParts(
|
|
207
208
|
runtime.systemPrompt,
|
|
208
209
|
GOODVIBES_AGENT_OPERATOR_POLICY,
|
|
210
|
+
buildReviewedMemoryPrompt(services.memoryRegistry),
|
|
209
211
|
buildEnabledRoutinesPrompt(services.shellPaths),
|
|
210
212
|
buildEnabledSkillsPrompt(services.shellPaths),
|
|
211
213
|
buildActivePersonaPrompt(services.shellPaths),
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
|
|
2
2
|
import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
|
|
3
3
|
import type { ShellPathService } from '@/runtime/index.ts';
|
|
4
|
+
import {
|
|
5
|
+
MemoryRegistry,
|
|
6
|
+
type MemoryClass,
|
|
7
|
+
type MemoryRecord,
|
|
8
|
+
type MemoryScope,
|
|
9
|
+
} from '@pellux/goodvibes-sdk/platform/state';
|
|
4
10
|
import { AgentPersonaRegistry, type AgentPersonaRecord } from '../agent/persona-registry.ts';
|
|
5
11
|
import { AgentRoutineRegistry, type AgentRoutineRecord } from '../agent/routine-registry.ts';
|
|
6
|
-
import { AgentSkillRegistry, type AgentSkillRecord } from '../agent/skill-registry.ts';
|
|
12
|
+
import { AgentSkillRegistry, type AgentSkillBundleRecord, type AgentSkillRecord } from '../agent/skill-registry.ts';
|
|
13
|
+
import { assertNoSecretLikeMemoryText } from '../agent/memory-safety.ts';
|
|
7
14
|
|
|
8
|
-
export type AgentLocalRegistryDomain = 'persona' | 'skill' | 'routine';
|
|
15
|
+
export type AgentLocalRegistryDomain = 'memory' | 'persona' | 'skill' | 'skill_bundle' | 'routine';
|
|
9
16
|
export type AgentLocalRegistryAction =
|
|
10
17
|
| 'list'
|
|
11
18
|
| 'search'
|
|
@@ -25,11 +32,18 @@ export interface AgentLocalRegistryToolArgs {
|
|
|
25
32
|
readonly action?: unknown;
|
|
26
33
|
readonly id?: unknown;
|
|
27
34
|
readonly query?: unknown;
|
|
35
|
+
readonly cls?: unknown;
|
|
36
|
+
readonly scope?: unknown;
|
|
37
|
+
readonly summary?: unknown;
|
|
38
|
+
readonly detail?: unknown;
|
|
39
|
+
readonly confidence?: unknown;
|
|
28
40
|
readonly name?: unknown;
|
|
29
41
|
readonly description?: unknown;
|
|
30
42
|
readonly body?: unknown;
|
|
31
43
|
readonly procedure?: unknown;
|
|
32
44
|
readonly steps?: unknown;
|
|
45
|
+
readonly skills?: unknown;
|
|
46
|
+
readonly skillIds?: unknown;
|
|
33
47
|
readonly triggers?: unknown;
|
|
34
48
|
readonly tags?: unknown;
|
|
35
49
|
readonly reason?: unknown;
|
|
@@ -37,7 +51,7 @@ export interface AgentLocalRegistryToolArgs {
|
|
|
37
51
|
readonly provenance?: unknown;
|
|
38
52
|
}
|
|
39
53
|
|
|
40
|
-
const DOMAINS: readonly AgentLocalRegistryDomain[] = ['persona', 'skill', 'routine'];
|
|
54
|
+
const DOMAINS: readonly AgentLocalRegistryDomain[] = ['memory', 'persona', 'skill', 'skill_bundle', 'routine'];
|
|
41
55
|
const ACTIONS: readonly AgentLocalRegistryAction[] = [
|
|
42
56
|
'list',
|
|
43
57
|
'search',
|
|
@@ -52,6 +66,8 @@ const ACTIONS: readonly AgentLocalRegistryAction[] = [
|
|
|
52
66
|
'clear_active',
|
|
53
67
|
'start',
|
|
54
68
|
];
|
|
69
|
+
const MEMORY_CLASSES: readonly MemoryClass[] = ['decision', 'constraint', 'incident', 'pattern', 'fact', 'risk', 'runbook', 'architecture', 'ownership'];
|
|
70
|
+
const MEMORY_SCOPES: readonly MemoryScope[] = ['session', 'project', 'team'];
|
|
55
71
|
|
|
56
72
|
function isDomain(value: unknown): value is AgentLocalRegistryDomain {
|
|
57
73
|
return typeof value === 'string' && DOMAINS.includes(value as AgentLocalRegistryDomain);
|
|
@@ -73,6 +89,28 @@ function readStringList(value: unknown): readonly string[] {
|
|
|
73
89
|
return value.filter((entry): entry is string => typeof entry === 'string').map((entry) => entry.trim()).filter(Boolean);
|
|
74
90
|
}
|
|
75
91
|
|
|
92
|
+
function readOptionalNumber(value: unknown): number | undefined {
|
|
93
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
94
|
+
if (typeof value !== 'string' || !value.trim()) return undefined;
|
|
95
|
+
const parsed = Number(value);
|
|
96
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function readOptionalConfidence(value: unknown): number | undefined {
|
|
100
|
+
const confidence = readOptionalNumber(value);
|
|
101
|
+
if (confidence === undefined) return undefined;
|
|
102
|
+
if (confidence < 0 || confidence > 100) throw new Error('confidence must be between 0 and 100.');
|
|
103
|
+
return confidence;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isMemoryClass(value: unknown): value is MemoryClass {
|
|
107
|
+
return typeof value === 'string' && MEMORY_CLASSES.includes(value as MemoryClass);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isMemoryScope(value: unknown): value is MemoryScope {
|
|
111
|
+
return typeof value === 'string' && MEMORY_SCOPES.includes(value as MemoryScope);
|
|
112
|
+
}
|
|
113
|
+
|
|
76
114
|
function registryError(message: string): { readonly success: false; readonly error: string } {
|
|
77
115
|
return { success: false, error: message };
|
|
78
116
|
}
|
|
@@ -99,6 +137,104 @@ function requireDescription(args: AgentLocalRegistryToolArgs): string {
|
|
|
99
137
|
return description;
|
|
100
138
|
}
|
|
101
139
|
|
|
140
|
+
function requireSummary(args: AgentLocalRegistryToolArgs): string {
|
|
141
|
+
const summary = readString(args.summary || args.description);
|
|
142
|
+
if (!summary) throw new Error('summary is required.');
|
|
143
|
+
return summary;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function requireMemoryClass(args: AgentLocalRegistryToolArgs): MemoryClass {
|
|
147
|
+
const cls = args.cls || 'fact';
|
|
148
|
+
if (!isMemoryClass(cls)) throw new Error(`Invalid memory class. Valid: ${MEMORY_CLASSES.join(', ')}.`);
|
|
149
|
+
return cls;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function readMemoryScope(args: AgentLocalRegistryToolArgs): MemoryScope {
|
|
153
|
+
const scope = args.scope || 'project';
|
|
154
|
+
if (!isMemoryScope(scope)) throw new Error(`Invalid memory scope. Valid: ${MEMORY_SCOPES.join(', ')}.`);
|
|
155
|
+
return scope;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function formatMemory(record: MemoryRecord): string {
|
|
159
|
+
const tags = record.tags.length > 0 ? ` tags=${record.tags.join(',')}` : '';
|
|
160
|
+
return `${record.id} ${record.scope}/${record.cls} ${record.reviewState} ${record.confidence}%${tags} ${record.summary}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function handleMemory(registry: MemoryRegistry, action: AgentLocalRegistryAction, args: AgentLocalRegistryToolArgs): Promise<string> {
|
|
164
|
+
if (action === 'list') {
|
|
165
|
+
const records = registry.getAll();
|
|
166
|
+
return records.length === 0
|
|
167
|
+
? 'Agent-local memory\nNo Agent-local memory records.'
|
|
168
|
+
: ['Agent-local memory', ...records.map(formatMemory)].join('\n');
|
|
169
|
+
}
|
|
170
|
+
if (action === 'search') {
|
|
171
|
+
const query = readString(args.query);
|
|
172
|
+
const records = registry.search({ query, limit: 10 });
|
|
173
|
+
return records.length === 0
|
|
174
|
+
? `Agent-local memory search\nNo Agent-local memory records matched "${query}".`
|
|
175
|
+
: [`Agent-local memory search: ${query || '(all)'}`, ...records.map(formatMemory)].join('\n');
|
|
176
|
+
}
|
|
177
|
+
if (action === 'get') {
|
|
178
|
+
const record = registry.get(requireId(args));
|
|
179
|
+
if (!record) return `Unknown Agent-local memory: ${readString(args.id)}`;
|
|
180
|
+
return [
|
|
181
|
+
formatMemory(record),
|
|
182
|
+
`created: ${new Date(record.createdAt).toISOString()}`,
|
|
183
|
+
`updated: ${new Date(record.updatedAt).toISOString()}`,
|
|
184
|
+
`provenance: ${record.provenance.map((entry) => `${entry.kind}:${entry.ref}`).join(', ') || '(none)'}`,
|
|
185
|
+
'',
|
|
186
|
+
record.detail || '(no detail)',
|
|
187
|
+
].join('\n');
|
|
188
|
+
}
|
|
189
|
+
if (action === 'create') {
|
|
190
|
+
const summary = requireSummary(args);
|
|
191
|
+
const detail = readString(args.detail || args.body);
|
|
192
|
+
const tags = readStringList(args.tags);
|
|
193
|
+
assertNoSecretLikeMemoryText([summary, detail, ...tags]);
|
|
194
|
+
const record = await registry.add({
|
|
195
|
+
scope: readMemoryScope(args),
|
|
196
|
+
cls: requireMemoryClass(args),
|
|
197
|
+
summary,
|
|
198
|
+
detail,
|
|
199
|
+
tags: [...tags],
|
|
200
|
+
provenance: [{ kind: 'event', ref: readString(args.provenance) || 'agent-local-registry-tool' }],
|
|
201
|
+
});
|
|
202
|
+
return `Created Agent-local memory ${record.id}: ${record.summary}`;
|
|
203
|
+
}
|
|
204
|
+
if (action === 'update') {
|
|
205
|
+
const summary = readString(args.summary || args.description);
|
|
206
|
+
const detail = readString(args.detail || args.body);
|
|
207
|
+
const tags = args.tags === undefined ? undefined : [...readStringList(args.tags)];
|
|
208
|
+
assertNoSecretLikeMemoryText([summary, detail, ...(tags ?? [])]);
|
|
209
|
+
const record = registry.update(requireId(args), {
|
|
210
|
+
summary: summary || undefined,
|
|
211
|
+
detail: detail || undefined,
|
|
212
|
+
tags,
|
|
213
|
+
scope: args.scope === undefined ? undefined : readMemoryScope(args),
|
|
214
|
+
});
|
|
215
|
+
if (!record) return `Unknown Agent-local memory: ${readString(args.id)}`;
|
|
216
|
+
return `Updated Agent-local memory ${record.id}: ${record.summary}`;
|
|
217
|
+
}
|
|
218
|
+
if (action === 'review') {
|
|
219
|
+
const record = registry.review(requireId(args), {
|
|
220
|
+
state: 'reviewed',
|
|
221
|
+
confidence: readOptionalConfidence(args.confidence),
|
|
222
|
+
reviewedBy: 'agent',
|
|
223
|
+
});
|
|
224
|
+
if (!record) return `Unknown Agent-local memory: ${readString(args.id)}`;
|
|
225
|
+
return `Reviewed Agent-local memory ${record.id}.`;
|
|
226
|
+
}
|
|
227
|
+
if (action === 'stale') {
|
|
228
|
+
const record = registry.review(requireId(args), {
|
|
229
|
+
state: 'stale',
|
|
230
|
+
staleReason: readString(args.reason) || 'Marked stale by Agent.',
|
|
231
|
+
});
|
|
232
|
+
if (!record) return `Unknown Agent-local memory: ${readString(args.id)}`;
|
|
233
|
+
return `Marked Agent-local memory ${record.id} stale.`;
|
|
234
|
+
}
|
|
235
|
+
throw new Error(`Action ${action} is not valid for memory.`);
|
|
236
|
+
}
|
|
237
|
+
|
|
102
238
|
function formatPersona(persona: AgentPersonaRecord, activeId: string | null): string {
|
|
103
239
|
const active = persona.id === activeId ? 'active' : 'inactive';
|
|
104
240
|
return `${persona.id} ${active} ${persona.reviewState} ${persona.name} - ${persona.description}`;
|
|
@@ -109,6 +245,11 @@ function formatSkill(skill: AgentSkillRecord): string {
|
|
|
109
245
|
return `${skill.id} ${enabled} ${skill.reviewState} ${skill.name} - ${skill.description}`;
|
|
110
246
|
}
|
|
111
247
|
|
|
248
|
+
function formatSkillBundle(bundle: AgentSkillBundleRecord): string {
|
|
249
|
+
const enabled = bundle.enabled ? 'enabled' : 'disabled';
|
|
250
|
+
return `${bundle.id} ${enabled} ${bundle.reviewState} ${bundle.name} - ${bundle.description} skills=${bundle.skillIds.join(',')}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
112
253
|
function formatRoutine(routine: AgentRoutineRecord): string {
|
|
113
254
|
const enabled = routine.enabled ? 'enabled' : 'disabled';
|
|
114
255
|
return `${routine.id} ${enabled} ${routine.reviewState} starts=${routine.startCount} ${routine.name} - ${routine.description}`;
|
|
@@ -127,6 +268,12 @@ function listSkills(records: readonly AgentSkillRecord[], title: string): string
|
|
|
127
268
|
: [title, ...records.map(formatSkill)].join('\n');
|
|
128
269
|
}
|
|
129
270
|
|
|
271
|
+
function listSkillBundles(records: readonly AgentSkillBundleRecord[], title: string): string {
|
|
272
|
+
return records.length === 0
|
|
273
|
+
? `${title}\nNo Agent-local skill bundles.`
|
|
274
|
+
: [title, ...records.map(formatSkillBundle)].join('\n');
|
|
275
|
+
}
|
|
276
|
+
|
|
130
277
|
function listRoutines(records: readonly AgentRoutineRecord[], title: string): string {
|
|
131
278
|
return records.length === 0
|
|
132
279
|
? `${title}\nNo Agent-local routines.`
|
|
@@ -232,6 +379,49 @@ function handleSkill(shellPaths: ShellPathService, action: AgentLocalRegistryAct
|
|
|
232
379
|
throw new Error(`Action ${action} is not valid for skills.`);
|
|
233
380
|
}
|
|
234
381
|
|
|
382
|
+
function handleSkillBundle(shellPaths: ShellPathService, action: AgentLocalRegistryAction, args: AgentLocalRegistryToolArgs): string {
|
|
383
|
+
const registry = AgentSkillRegistry.fromShellPaths(shellPaths);
|
|
384
|
+
if (action === 'list') return listSkillBundles(registry.listBundles(), 'Agent-local skill bundles');
|
|
385
|
+
if (action === 'search') return listSkillBundles(registry.searchBundles(readString(args.query)), 'Agent-local skill bundles search');
|
|
386
|
+
if (action === 'get') {
|
|
387
|
+
const bundle = registry.getBundle(requireId(args));
|
|
388
|
+
if (!bundle) return `Unknown Agent-local skill bundle: ${readString(args.id)}`;
|
|
389
|
+
return [
|
|
390
|
+
formatSkillBundle(bundle),
|
|
391
|
+
`skills: ${bundle.skillIds.join(', ')}`,
|
|
392
|
+
'',
|
|
393
|
+
bundle.description,
|
|
394
|
+
].join('\n');
|
|
395
|
+
}
|
|
396
|
+
if (action === 'create') {
|
|
397
|
+
const bundle = registry.createBundle({
|
|
398
|
+
name: requireName(args),
|
|
399
|
+
description: requireDescription(args),
|
|
400
|
+
skillIds: readStringList(args.skillIds ?? args.skills),
|
|
401
|
+
enabled: args.enabled === true,
|
|
402
|
+
source: 'agent',
|
|
403
|
+
provenance: readString(args.provenance) || 'agent-local-registry-tool',
|
|
404
|
+
});
|
|
405
|
+
return `Created Agent-local skill bundle ${bundle.id}: ${bundle.name}`;
|
|
406
|
+
}
|
|
407
|
+
if (action === 'update') {
|
|
408
|
+
const bundle = registry.updateBundle(requireId(args), {
|
|
409
|
+
name: readString(args.name) || undefined,
|
|
410
|
+
description: readString(args.description) || undefined,
|
|
411
|
+
skillIds: args.skillIds === undefined && args.skills === undefined ? undefined : readStringList(args.skillIds ?? args.skills),
|
|
412
|
+
provenance: readString(args.provenance) || 'agent-local-registry-tool',
|
|
413
|
+
});
|
|
414
|
+
return `Updated Agent-local skill bundle ${bundle.id}: ${bundle.name}`;
|
|
415
|
+
}
|
|
416
|
+
if (action === 'enable' || action === 'disable') {
|
|
417
|
+
const bundle = registry.setBundleEnabled(requireId(args), action === 'enable');
|
|
418
|
+
return `${action === 'enable' ? 'Enabled' : 'Disabled'} Agent-local skill bundle ${bundle.id}: ${bundle.name}`;
|
|
419
|
+
}
|
|
420
|
+
if (action === 'review') return `Reviewed Agent-local skill bundle ${registry.markBundleReviewed(requireId(args)).id}.`;
|
|
421
|
+
if (action === 'stale') return `Marked Agent-local skill bundle ${registry.markBundleStale(requireId(args), readString(args.reason)).id} stale.`;
|
|
422
|
+
throw new Error(`Action ${action} is not valid for skill bundles.`);
|
|
423
|
+
}
|
|
424
|
+
|
|
235
425
|
function handleRoutine(shellPaths: ShellPathService, action: AgentLocalRegistryAction, args: AgentLocalRegistryToolArgs): string {
|
|
236
426
|
const registry = AgentRoutineRegistry.fromShellPaths(shellPaths);
|
|
237
427
|
if (action === 'list') return listRoutines(registry.list(), 'Agent-local routines');
|
|
@@ -289,14 +479,14 @@ function handleRoutine(shellPaths: ShellPathService, action: AgentLocalRegistryA
|
|
|
289
479
|
throw new Error(`Action ${action} is not valid for routines.`);
|
|
290
480
|
}
|
|
291
481
|
|
|
292
|
-
export function createAgentLocalRegistryTool(shellPaths: ShellPathService): Tool {
|
|
482
|
+
export function createAgentLocalRegistryTool(shellPaths: ShellPathService, memoryRegistry: MemoryRegistry): Tool {
|
|
293
483
|
return {
|
|
294
484
|
definition: {
|
|
295
485
|
name: 'agent_local_registry',
|
|
296
486
|
description: [
|
|
297
|
-
'Inspect and maintain GoodVibes Agent-local personas, skills, and routines from the main conversation.',
|
|
298
|
-
'Use this for safe self-improvement: create or refine reusable behavior, enable skills/routines, choose personas, review/stale records, and start routines in the same serial conversation.',
|
|
299
|
-
'This tool cannot delete records, create schedules, mutate
|
|
487
|
+
'Inspect and maintain GoodVibes Agent-local memory, personas, skills, and routines from the main conversation.',
|
|
488
|
+
'Use this for safe self-improvement: remember durable non-secret facts, create or refine reusable behavior, bundle related skills, enable skills/routines, choose personas, review/stale records, and start routines in the same serial conversation.',
|
|
489
|
+
'This tool cannot delete records, create schedules, mutate connected services, send messages, run background jobs, or delegate build work.',
|
|
300
490
|
].join(' '),
|
|
301
491
|
parameters: {
|
|
302
492
|
type: 'object',
|
|
@@ -305,11 +495,18 @@ export function createAgentLocalRegistryTool(shellPaths: ShellPathService): Tool
|
|
|
305
495
|
action: { type: 'string', enum: [...ACTIONS] },
|
|
306
496
|
id: { type: 'string' },
|
|
307
497
|
query: { type: 'string' },
|
|
498
|
+
cls: { type: 'string', enum: [...MEMORY_CLASSES], description: 'Memory class when domain is memory.' },
|
|
499
|
+
scope: { type: 'string', enum: [...MEMORY_SCOPES], description: 'Memory scope when domain is memory.' },
|
|
500
|
+
summary: { type: 'string', description: 'Memory summary when domain is memory.' },
|
|
501
|
+
detail: { type: 'string', description: 'Memory detail when domain is memory.' },
|
|
502
|
+
confidence: { type: 'number', description: 'Memory review confidence from 0 to 100.' },
|
|
308
503
|
name: { type: 'string' },
|
|
309
504
|
description: { type: 'string' },
|
|
310
505
|
body: { type: 'string', description: 'Persona body/instructions.' },
|
|
311
506
|
procedure: { type: 'string', description: 'Skill procedure.' },
|
|
312
507
|
steps: { type: 'string', description: 'Routine steps.' },
|
|
508
|
+
skills: { type: 'array', items: { type: 'string' }, description: 'Skill ids for skill_bundle create/update.' },
|
|
509
|
+
skillIds: { type: 'array', items: { type: 'string' }, description: 'Skill ids for skill_bundle create/update.' },
|
|
313
510
|
triggers: { type: 'array', items: { type: 'string' } },
|
|
314
511
|
tags: { type: 'array', items: { type: 'string' } },
|
|
315
512
|
reason: { type: 'string' },
|
|
@@ -326,8 +523,10 @@ export function createAgentLocalRegistryTool(shellPaths: ShellPathService): Tool
|
|
|
326
523
|
if (!isDomain(args.domain)) return registryError(`Unknown domain. Valid: ${DOMAINS.join(', ')}.`);
|
|
327
524
|
if (!isAction(args.action)) return registryError(`Unknown action. Valid: ${ACTIONS.join(', ')}.`);
|
|
328
525
|
try {
|
|
526
|
+
if (args.domain === 'memory') return registryOutput(await handleMemory(memoryRegistry, args.action, args));
|
|
329
527
|
if (args.domain === 'persona') return registryOutput(handlePersona(shellPaths, args.action, args));
|
|
330
528
|
if (args.domain === 'skill') return registryOutput(handleSkill(shellPaths, args.action, args));
|
|
529
|
+
if (args.domain === 'skill_bundle') return registryOutput(handleSkillBundle(shellPaths, args.action, args));
|
|
331
530
|
return registryOutput(handleRoutine(shellPaths, args.action, args));
|
|
332
531
|
} catch (error) {
|
|
333
532
|
return registryError(error instanceof Error ? error.message : String(error));
|
|
@@ -336,6 +535,6 @@ export function createAgentLocalRegistryTool(shellPaths: ShellPathService): Tool
|
|
|
336
535
|
};
|
|
337
536
|
}
|
|
338
537
|
|
|
339
|
-
export function registerAgentLocalRegistryTool(registry: ToolRegistry, shellPaths: ShellPathService): void {
|
|
340
|
-
registry.register(createAgentLocalRegistryTool(shellPaths));
|
|
538
|
+
export function registerAgentLocalRegistryTool(registry: ToolRegistry, shellPaths: ShellPathService, memoryRegistry: MemoryRegistry): void {
|
|
539
|
+
registry.register(createAgentLocalRegistryTool(shellPaths, memoryRegistry));
|
|
341
540
|
}
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.1.
|
|
9
|
+
let _version = '0.1.109';
|
|
10
10
|
let _sdkVersion = '0.33.35';
|
|
11
11
|
try {
|
|
12
12
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|