@mingxy/cerebro 1.16.0 → 1.16.2
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/dist/client.d.ts +14 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +18 -3
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +0 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -2
- package/dist/config.js.map +1 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +61 -31
- package/dist/hooks.js.map +1 -1
- package/dist/tools.d.ts +5 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +12 -4
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +30 -3
- package/src/hooks.ts +33 -13
- package/src/tools.ts +421 -411
package/src/tools.ts
CHANGED
|
@@ -1,411 +1,421 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin";
|
|
2
|
-
import type { CerebroClient } from "./client.js";
|
|
3
|
-
import { isAutoStoreEnabled, setAutoStoreEnabled } from "./index.js";
|
|
4
|
-
|
|
5
|
-
export interface ToolContext {
|
|
6
|
-
agentId?: string;
|
|
7
|
-
getSessionId: () => string | undefined;
|
|
8
|
-
getAgentName?: () => string;
|
|
9
|
-
getProjectPath?: () => string | undefined;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function buildTools(client: CerebroClient, containerTags: string[], context: ToolContext) {
|
|
13
|
-
return {
|
|
14
|
-
memory_store: tool({
|
|
15
|
-
description:
|
|
16
|
-
"Store a new memory in the user's long-term memory. " +
|
|
17
|
-
"Use when the user explicitly asks to remember something, " +
|
|
18
|
-
"or when you identify important preferences, facts, or decisions worth preserving. " +
|
|
19
|
-
"IMPORTANT: Before calling, you MUST analyze: (1) Which category fits best? (2) Is this project-specific or cross-project? (3) Does it contain sensitive data? (4) Are tags accurate and descriptive? " +
|
|
20
|
-
"Every memory MUST have a correct category and at least 1 meaningful tag. " +
|
|
21
|
-
"Memories are automatically scoped to the current project via project_path. " +
|
|
22
|
-
"Set scope='global' for cross-project memories that should be visible everywhere. " +
|
|
23
|
-
"Private memories (visibility='private') are always agent-scoped and not bound to any project — use for sensitive data.",
|
|
24
|
-
args: {
|
|
25
|
-
content: tool.schema.string().describe(
|
|
26
|
-
"The information to remember. MUST be: atomic (one fact per memory), complete (self-contained without context), and precise (no ambiguity). " +
|
|
27
|
-
"BAD: 'fixed some bugs'. GOOD: 'Fixed memory_type validation bug in memory.rs:1480 - LLM returns illegal \"pinned\" value, added match guard to normalize to WORK/EMOTIONAL fallback'."
|
|
28
|
-
),
|
|
29
|
-
tags: tool.schema
|
|
30
|
-
.array(tool.schema.string())
|
|
31
|
-
.optional()
|
|
32
|
-
.describe(
|
|
33
|
-
"REQUIRED. At least 1 tag in snake_case. Tags describe the memory's topic/domain for future retrieval. " +
|
|
34
|
-
"Examples: rust_backend, memory_system, bug_fix, user_preference, project_config. " +
|
|
35
|
-
"NEVER leave empty — if unsure, use a broad tag like the project name or topic area."
|
|
36
|
-
),
|
|
37
|
-
source: tool.schema
|
|
38
|
-
.string()
|
|
39
|
-
.describe("Origin context, e.g. 'conversation', 'code-review', 'user-input', 'debugging', 'architecture-decision'"),
|
|
40
|
-
scope: tool.schema
|
|
41
|
-
.string()
|
|
42
|
-
.optional()
|
|
43
|
-
.describe(
|
|
44
|
-
"'project' (default) = only visible in current project context. 'global' = visible across all projects. " +
|
|
45
|
-
"Rule: if the memory applies generally (user preferences, general knowledge, cross-project patterns) use 'global'. " +
|
|
46
|
-
"If it's specific to one project's code/architecture, use 'project'."
|
|
47
|
-
),
|
|
48
|
-
visibility: tool.schema
|
|
49
|
-
.string()
|
|
50
|
-
.optional()
|
|
51
|
-
.describe(
|
|
52
|
-
"'global' (default) = all agents can see and recall this memory. 'private' = ONLY the current agent can see it. " +
|
|
53
|
-
"MUST use 'private' when content contains: passwords, API keys, tokens, database credentials, SSH keys, personal information (phone, email, address), " +
|
|
54
|
-
"internal company details, or anything the user would NOT want other agents to access. " +
|
|
55
|
-
"WARNING: private memories are invisible to ALL other agents — if in doubt, ask the user. " +
|
|
56
|
-
"Do NOT overuse 'private' for normal work notes — default 'global' is correct for most cases."
|
|
57
|
-
),
|
|
58
|
-
category: tool.schema
|
|
59
|
-
.enum(["cases", "preferences", "entities", "events", "profile", "patterns"])
|
|
60
|
-
.optional()
|
|
61
|
-
.describe(
|
|
62
|
-
"Memory category. MUST be one of these exact values (lowercase): " +
|
|
63
|
-
"'cases' (default) = work records, bug fixes, architecture decisions; " +
|
|
64
|
-
"'preferences' = user likes/dislikes, coding style, tool choices; " +
|
|
65
|
-
"'entities' = projects, tools, people, concepts; " +
|
|
66
|
-
"'events' = time-bound milestones (deployments, releases, incidents); " +
|
|
67
|
-
"'profile' = user identity traits (role, skills, team membership); " +
|
|
68
|
-
"'patterns' = workflows, methodologies, best practices. " +
|
|
69
|
-
"When in doubt, omit this field (defaults to 'cases')."
|
|
70
|
-
),
|
|
71
|
-
},
|
|
72
|
-
async execute(args) {
|
|
73
|
-
const allTags = [...containerTags, ...(args.tags ?? [])];
|
|
74
|
-
const effectiveAgentId = context.getAgentName?.() || context.agentId;
|
|
75
|
-
const result = await client.createMemory(
|
|
76
|
-
args.content,
|
|
77
|
-
allTags,
|
|
78
|
-
args.source,
|
|
79
|
-
args.scope ?? "project",
|
|
80
|
-
effectiveAgentId,
|
|
81
|
-
context.getSessionId(),
|
|
82
|
-
args.visibility,
|
|
83
|
-
args.category,
|
|
84
|
-
context.getProjectPath?.(),
|
|
85
|
-
);
|
|
86
|
-
if (!result) return JSON.stringify({ ok: false, error: "The Cerebro server may be unavailable." });
|
|
87
|
-
return JSON.stringify({ ok: true, id: result.id, tags: result.tags });
|
|
88
|
-
},
|
|
89
|
-
}),
|
|
90
|
-
|
|
91
|
-
memory_search: tool({
|
|
92
|
-
description:
|
|
93
|
-
"Search the user's long-term memory by semantic similarity. " +
|
|
94
|
-
"Use to recall previously stored preferences, facts, or context. " +
|
|
95
|
-
"Searches are automatically filtered by the current project_path. " +
|
|
96
|
-
"Global-scope memories and memories without a project_path are always included in results. " +
|
|
97
|
-
"Private memories are visible only to the creating agent.",
|
|
98
|
-
args: {
|
|
99
|
-
query: tool.schema.string().describe("Natural-language search query"),
|
|
100
|
-
limit: tool.schema
|
|
101
|
-
.number()
|
|
102
|
-
.optional()
|
|
103
|
-
.describe("Max results to return (default 10)"),
|
|
104
|
-
scope: tool.schema
|
|
105
|
-
.string()
|
|
106
|
-
.optional()
|
|
107
|
-
.describe("Optional scope filter"),
|
|
108
|
-
},
|
|
109
|
-
async execute(args) {
|
|
110
|
-
const results = await client.searchMemories(
|
|
111
|
-
args.query,
|
|
112
|
-
args.limit ?? 10,
|
|
113
|
-
args.scope,
|
|
114
|
-
containerTags,
|
|
115
|
-
context.getProjectPath?.(),
|
|
116
|
-
);
|
|
117
|
-
if (results.length === 0) return JSON.stringify({ ok: true, count: 0, results: [] });
|
|
118
|
-
const items = results.map((r) => ({
|
|
119
|
-
id: r.memory.id,
|
|
120
|
-
score: r.score,
|
|
121
|
-
content: r.memory.content.slice(0, 200),
|
|
122
|
-
}));
|
|
123
|
-
return JSON.stringify({ ok: true, count: results.length, results: items });
|
|
124
|
-
},
|
|
125
|
-
}),
|
|
126
|
-
|
|
127
|
-
memory_get: tool({
|
|
128
|
-
description:
|
|
129
|
-
"Retrieve a specific memory by its ID. " +
|
|
130
|
-
"Use when a recalled memory's content was truncated (e.g. medium relevance summary) " +
|
|
131
|
-
"and you need the full details, or when you see [rel:<id>] markers in injected context " +
|
|
132
|
-
"and want to fetch related memories.",
|
|
133
|
-
args: {
|
|
134
|
-
id: tool.schema.string().describe("Memory ID"),
|
|
135
|
-
},
|
|
136
|
-
async execute(args) {
|
|
137
|
-
const memory = await client.getMemory(args.id);
|
|
138
|
-
if (!memory) return JSON.stringify({ ok: false, error: "not found" });
|
|
139
|
-
return JSON.stringify({ ok: true, memory });
|
|
140
|
-
},
|
|
141
|
-
}),
|
|
142
|
-
|
|
143
|
-
memory_update: tool({
|
|
144
|
-
description:
|
|
145
|
-
"Update the content or tags of an existing memory. " +
|
|
146
|
-
"Use when information needs correction or enrichment.",
|
|
147
|
-
args: {
|
|
148
|
-
id: tool.schema.string().describe("Memory ID to update"),
|
|
149
|
-
content: tool.schema.string().describe("New content"),
|
|
150
|
-
tags: tool.schema
|
|
151
|
-
.array(tool.schema.string())
|
|
152
|
-
.optional()
|
|
153
|
-
.describe("Replacement tags"),
|
|
154
|
-
},
|
|
155
|
-
async execute(args) {
|
|
156
|
-
const result = await client.updateMemory(
|
|
157
|
-
args.id,
|
|
158
|
-
args.content,
|
|
159
|
-
args.tags,
|
|
160
|
-
);
|
|
161
|
-
if (!result) return JSON.stringify({ ok: false, error: `Failed to update memory ${args.id}` });
|
|
162
|
-
return JSON.stringify({ ok: true, id: args.id });
|
|
163
|
-
},
|
|
164
|
-
}),
|
|
165
|
-
|
|
166
|
-
memory_profile: tool({
|
|
167
|
-
description:
|
|
168
|
-
"Get the user profile synthesized from stored memories. Shows preferences, patterns, and key information.",
|
|
169
|
-
args: {},
|
|
170
|
-
async execute() {
|
|
171
|
-
const
|
|
172
|
-
if (
|
|
173
|
-
return JSON.stringify({ ok: true,
|
|
174
|
-
},
|
|
175
|
-
}),
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
description:
|
|
179
|
-
"
|
|
180
|
-
args: {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
.
|
|
223
|
-
|
|
224
|
-
.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return JSON.stringify({ ok: true,
|
|
303
|
-
},
|
|
304
|
-
}),
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
description:
|
|
308
|
-
"
|
|
309
|
-
args: {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
},
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
)
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import type { CerebroClient } from "./client.js";
|
|
3
|
+
import { isAutoStoreEnabled, setAutoStoreEnabled } from "./index.js";
|
|
4
|
+
|
|
5
|
+
export interface ToolContext {
|
|
6
|
+
agentId?: string;
|
|
7
|
+
getSessionId: () => string | undefined;
|
|
8
|
+
getAgentName?: () => string;
|
|
9
|
+
getProjectPath?: () => string | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function buildTools(client: CerebroClient, containerTags: string[], context: ToolContext) {
|
|
13
|
+
return {
|
|
14
|
+
memory_store: tool({
|
|
15
|
+
description:
|
|
16
|
+
"Store a new memory in the user's long-term memory. " +
|
|
17
|
+
"Use when the user explicitly asks to remember something, " +
|
|
18
|
+
"or when you identify important preferences, facts, or decisions worth preserving. " +
|
|
19
|
+
"IMPORTANT: Before calling, you MUST analyze: (1) Which category fits best? (2) Is this project-specific or cross-project? (3) Does it contain sensitive data? (4) Are tags accurate and descriptive? " +
|
|
20
|
+
"Every memory MUST have a correct category and at least 1 meaningful tag. " +
|
|
21
|
+
"Memories are automatically scoped to the current project via project_path. " +
|
|
22
|
+
"Set scope='global' for cross-project memories that should be visible everywhere. " +
|
|
23
|
+
"Private memories (visibility='private') are always agent-scoped and not bound to any project — use for sensitive data.",
|
|
24
|
+
args: {
|
|
25
|
+
content: tool.schema.string().describe(
|
|
26
|
+
"The information to remember. MUST be: atomic (one fact per memory), complete (self-contained without context), and precise (no ambiguity). " +
|
|
27
|
+
"BAD: 'fixed some bugs'. GOOD: 'Fixed memory_type validation bug in memory.rs:1480 - LLM returns illegal \"pinned\" value, added match guard to normalize to WORK/EMOTIONAL fallback'."
|
|
28
|
+
),
|
|
29
|
+
tags: tool.schema
|
|
30
|
+
.array(tool.schema.string())
|
|
31
|
+
.optional()
|
|
32
|
+
.describe(
|
|
33
|
+
"REQUIRED. At least 1 tag in snake_case. Tags describe the memory's topic/domain for future retrieval. " +
|
|
34
|
+
"Examples: rust_backend, memory_system, bug_fix, user_preference, project_config. " +
|
|
35
|
+
"NEVER leave empty — if unsure, use a broad tag like the project name or topic area."
|
|
36
|
+
),
|
|
37
|
+
source: tool.schema
|
|
38
|
+
.string()
|
|
39
|
+
.describe("Origin context, e.g. 'conversation', 'code-review', 'user-input', 'debugging', 'architecture-decision'"),
|
|
40
|
+
scope: tool.schema
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe(
|
|
44
|
+
"'project' (default) = only visible in current project context. 'global' = visible across all projects. " +
|
|
45
|
+
"Rule: if the memory applies generally (user preferences, general knowledge, cross-project patterns) use 'global'. " +
|
|
46
|
+
"If it's specific to one project's code/architecture, use 'project'."
|
|
47
|
+
),
|
|
48
|
+
visibility: tool.schema
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe(
|
|
52
|
+
"'global' (default) = all agents can see and recall this memory. 'private' = ONLY the current agent can see it. " +
|
|
53
|
+
"MUST use 'private' when content contains: passwords, API keys, tokens, database credentials, SSH keys, personal information (phone, email, address), " +
|
|
54
|
+
"internal company details, or anything the user would NOT want other agents to access. " +
|
|
55
|
+
"WARNING: private memories are invisible to ALL other agents — if in doubt, ask the user. " +
|
|
56
|
+
"Do NOT overuse 'private' for normal work notes — default 'global' is correct for most cases."
|
|
57
|
+
),
|
|
58
|
+
category: tool.schema
|
|
59
|
+
.enum(["cases", "preferences", "entities", "events", "profile", "patterns"])
|
|
60
|
+
.optional()
|
|
61
|
+
.describe(
|
|
62
|
+
"Memory category. MUST be one of these exact values (lowercase): " +
|
|
63
|
+
"'cases' (default) = work records, bug fixes, architecture decisions; " +
|
|
64
|
+
"'preferences' = user likes/dislikes, coding style, tool choices; " +
|
|
65
|
+
"'entities' = projects, tools, people, concepts; " +
|
|
66
|
+
"'events' = time-bound milestones (deployments, releases, incidents); " +
|
|
67
|
+
"'profile' = user identity traits (role, skills, team membership); " +
|
|
68
|
+
"'patterns' = workflows, methodologies, best practices. " +
|
|
69
|
+
"When in doubt, omit this field (defaults to 'cases')."
|
|
70
|
+
),
|
|
71
|
+
},
|
|
72
|
+
async execute(args) {
|
|
73
|
+
const allTags = [...containerTags, ...(args.tags ?? [])];
|
|
74
|
+
const effectiveAgentId = context.getAgentName?.() || context.agentId;
|
|
75
|
+
const result = await client.createMemory(
|
|
76
|
+
args.content,
|
|
77
|
+
allTags,
|
|
78
|
+
args.source,
|
|
79
|
+
args.scope ?? "project",
|
|
80
|
+
effectiveAgentId,
|
|
81
|
+
context.getSessionId(),
|
|
82
|
+
args.visibility,
|
|
83
|
+
args.category,
|
|
84
|
+
context.getProjectPath?.(),
|
|
85
|
+
);
|
|
86
|
+
if (!result) return JSON.stringify({ ok: false, error: "The Cerebro server may be unavailable." });
|
|
87
|
+
return JSON.stringify({ ok: true, id: result.id, tags: result.tags });
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
|
|
91
|
+
memory_search: tool({
|
|
92
|
+
description:
|
|
93
|
+
"Search the user's long-term memory by semantic similarity. " +
|
|
94
|
+
"Use to recall previously stored preferences, facts, or context. " +
|
|
95
|
+
"Searches are automatically filtered by the current project_path. " +
|
|
96
|
+
"Global-scope memories and memories without a project_path are always included in results. " +
|
|
97
|
+
"Private memories are visible only to the creating agent.",
|
|
98
|
+
args: {
|
|
99
|
+
query: tool.schema.string().describe("Natural-language search query"),
|
|
100
|
+
limit: tool.schema
|
|
101
|
+
.number()
|
|
102
|
+
.optional()
|
|
103
|
+
.describe("Max results to return (default 10)"),
|
|
104
|
+
scope: tool.schema
|
|
105
|
+
.string()
|
|
106
|
+
.optional()
|
|
107
|
+
.describe("Optional scope filter"),
|
|
108
|
+
},
|
|
109
|
+
async execute(args) {
|
|
110
|
+
const results = await client.searchMemories(
|
|
111
|
+
args.query,
|
|
112
|
+
args.limit ?? 10,
|
|
113
|
+
args.scope,
|
|
114
|
+
containerTags,
|
|
115
|
+
context.getProjectPath?.(),
|
|
116
|
+
);
|
|
117
|
+
if (results.length === 0) return JSON.stringify({ ok: true, count: 0, results: [] });
|
|
118
|
+
const items = results.map((r) => ({
|
|
119
|
+
id: r.memory.id,
|
|
120
|
+
score: r.score,
|
|
121
|
+
content: r.memory.content.slice(0, 200),
|
|
122
|
+
}));
|
|
123
|
+
return JSON.stringify({ ok: true, count: results.length, results: items });
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
|
|
127
|
+
memory_get: tool({
|
|
128
|
+
description:
|
|
129
|
+
"Retrieve a specific memory by its ID. " +
|
|
130
|
+
"Use when a recalled memory's content was truncated (e.g. medium relevance summary) " +
|
|
131
|
+
"and you need the full details, or when you see [rel:<id>] markers in injected context " +
|
|
132
|
+
"and want to fetch related memories.",
|
|
133
|
+
args: {
|
|
134
|
+
id: tool.schema.string().describe("Memory ID"),
|
|
135
|
+
},
|
|
136
|
+
async execute(args) {
|
|
137
|
+
const memory = await client.getMemory(args.id);
|
|
138
|
+
if (!memory) return JSON.stringify({ ok: false, error: "not found" });
|
|
139
|
+
return JSON.stringify({ ok: true, memory });
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
|
|
143
|
+
memory_update: tool({
|
|
144
|
+
description:
|
|
145
|
+
"Update the content or tags of an existing memory. " +
|
|
146
|
+
"Use when information needs correction or enrichment.",
|
|
147
|
+
args: {
|
|
148
|
+
id: tool.schema.string().describe("Memory ID to update"),
|
|
149
|
+
content: tool.schema.string().describe("New content"),
|
|
150
|
+
tags: tool.schema
|
|
151
|
+
.array(tool.schema.string())
|
|
152
|
+
.optional()
|
|
153
|
+
.describe("Replacement tags"),
|
|
154
|
+
},
|
|
155
|
+
async execute(args) {
|
|
156
|
+
const result = await client.updateMemory(
|
|
157
|
+
args.id,
|
|
158
|
+
args.content,
|
|
159
|
+
args.tags,
|
|
160
|
+
);
|
|
161
|
+
if (!result) return JSON.stringify({ ok: false, error: `Failed to update memory ${args.id}` });
|
|
162
|
+
return JSON.stringify({ ok: true, id: args.id });
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
|
|
166
|
+
memory_profile: tool({
|
|
167
|
+
description:
|
|
168
|
+
"Get the user profile synthesized from stored memories. Shows preferences, patterns, and key information.",
|
|
169
|
+
args: {},
|
|
170
|
+
async execute() {
|
|
171
|
+
const preferences = await client.getProfile();
|
|
172
|
+
if (preferences.length === 0) return JSON.stringify({ ok: true, count: 0, preferences: [] });
|
|
173
|
+
return JSON.stringify({ ok: true, count: preferences.length, preferences });
|
|
174
|
+
},
|
|
175
|
+
}),
|
|
176
|
+
|
|
177
|
+
memory_profile_stats: tool({
|
|
178
|
+
description:
|
|
179
|
+
"View user profile statistics — total preferences, slot distribution, induction run counts, etc.",
|
|
180
|
+
args: {},
|
|
181
|
+
async execute() {
|
|
182
|
+
const stats = await client.getProfileStats();
|
|
183
|
+
return JSON.stringify({ ok: true, stats });
|
|
184
|
+
},
|
|
185
|
+
}),
|
|
186
|
+
|
|
187
|
+
memory_list: tool({
|
|
188
|
+
description:
|
|
189
|
+
"List the most recent memories. Use to browse what's been remembered without a search query.",
|
|
190
|
+
args: {
|
|
191
|
+
limit: tool.schema
|
|
192
|
+
.number()
|
|
193
|
+
.optional()
|
|
194
|
+
.describe("Max memories to return (default: 20)"),
|
|
195
|
+
},
|
|
196
|
+
async execute(args) {
|
|
197
|
+
const memories = await client.listRecent(args.limit ?? 20);
|
|
198
|
+
if (memories.length === 0) return JSON.stringify({ ok: true, count: 0, memories: [] });
|
|
199
|
+
const items = memories.map((m) => ({
|
|
200
|
+
id: m.id,
|
|
201
|
+
content: m.content.slice(0, 120),
|
|
202
|
+
category: m.category,
|
|
203
|
+
tags: m.tags,
|
|
204
|
+
}));
|
|
205
|
+
return JSON.stringify({ ok: true, count: memories.length, memories: items });
|
|
206
|
+
},
|
|
207
|
+
}),
|
|
208
|
+
|
|
209
|
+
memory_ingest: tool({
|
|
210
|
+
description:
|
|
211
|
+
"Ingest conversation messages for intelligent extraction. The system extracts atomic facts, deduplicates, and reconciles with existing memories. " +
|
|
212
|
+
"Extracted memories are automatically scoped to the current project via project_path. " +
|
|
213
|
+
"Global-scope memories are visible across all projects.",
|
|
214
|
+
args: {
|
|
215
|
+
messages: tool.schema
|
|
216
|
+
.array(
|
|
217
|
+
tool.schema.object({
|
|
218
|
+
role: tool.schema.string().describe("Message role: user, assistant, or system"),
|
|
219
|
+
content: tool.schema.string().describe("Message content"),
|
|
220
|
+
}),
|
|
221
|
+
)
|
|
222
|
+
.describe("Conversation messages to ingest"),
|
|
223
|
+
mode: tool.schema
|
|
224
|
+
.enum(["smart", "raw"])
|
|
225
|
+
.optional()
|
|
226
|
+
.describe("Extraction mode: 'smart' (default) or 'raw'"),
|
|
227
|
+
tags: tool.schema
|
|
228
|
+
.array(tool.schema.string())
|
|
229
|
+
.optional()
|
|
230
|
+
.describe("Tags to apply to extracted memories"),
|
|
231
|
+
session_id: tool.schema
|
|
232
|
+
.string()
|
|
233
|
+
.optional()
|
|
234
|
+
.describe("Session ID to associate with the ingestion"),
|
|
235
|
+
},
|
|
236
|
+
async execute(args) {
|
|
237
|
+
const effectiveAgentId = context.getAgentName?.() || context.agentId;
|
|
238
|
+
const result = await client.ingestMessages(args.messages, {
|
|
239
|
+
mode: args.mode ?? "smart",
|
|
240
|
+
tags: args.tags,
|
|
241
|
+
sessionId: args.session_id,
|
|
242
|
+
agentId: effectiveAgentId,
|
|
243
|
+
projectPath: context.getProjectPath?.(),
|
|
244
|
+
});
|
|
245
|
+
if (result === null) return JSON.stringify({ ok: false, error: "Ingestion failed" });
|
|
246
|
+
return JSON.stringify({ ok: true, result });
|
|
247
|
+
},
|
|
248
|
+
}),
|
|
249
|
+
|
|
250
|
+
memory_stats: tool({
|
|
251
|
+
description:
|
|
252
|
+
"Get statistics about stored memories — counts by category, type, tier, and timeline.",
|
|
253
|
+
args: {},
|
|
254
|
+
async execute() {
|
|
255
|
+
const stats = await client.getStats();
|
|
256
|
+
if (!stats) return JSON.stringify({ ok: false, error: "Failed to get stats" });
|
|
257
|
+
return JSON.stringify({ ok: true, stats });
|
|
258
|
+
},
|
|
259
|
+
}),
|
|
260
|
+
|
|
261
|
+
memory_delete: tool({
|
|
262
|
+
description:
|
|
263
|
+
"Delete a memory by ID. Use when the user asks to forget something.",
|
|
264
|
+
args: {
|
|
265
|
+
id: tool.schema.string().describe("Memory ID to delete"),
|
|
266
|
+
},
|
|
267
|
+
async execute(args) {
|
|
268
|
+
try {
|
|
269
|
+
await client.deleteMemory(args.id);
|
|
270
|
+
return JSON.stringify({ ok: true, id: args.id });
|
|
271
|
+
} catch {
|
|
272
|
+
return JSON.stringify({ ok: false, error: `Failed to delete memory ${args.id}` });
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
}),
|
|
276
|
+
|
|
277
|
+
space_create: tool({
|
|
278
|
+
description:
|
|
279
|
+
"Create a shared space (team or organization) for sharing memories across users and agents.",
|
|
280
|
+
args: {
|
|
281
|
+
name: tool.schema.string().describe("Name of the space"),
|
|
282
|
+
space_type: tool.schema
|
|
283
|
+
.string()
|
|
284
|
+
.describe("Type of space: 'team' or 'organization'"),
|
|
285
|
+
members: tool.schema
|
|
286
|
+
.array(
|
|
287
|
+
tool.schema.object({
|
|
288
|
+
user_id: tool.schema.string().describe("User/tenant ID to add"),
|
|
289
|
+
role: tool.schema.string().describe("Member role: admin, member, or reader"),
|
|
290
|
+
}),
|
|
291
|
+
)
|
|
292
|
+
.optional()
|
|
293
|
+
.describe("Initial members to add"),
|
|
294
|
+
},
|
|
295
|
+
async execute(args) {
|
|
296
|
+
const result = await client.createSpace(
|
|
297
|
+
args.name,
|
|
298
|
+
args.space_type,
|
|
299
|
+
args.members,
|
|
300
|
+
);
|
|
301
|
+
if (!result) return JSON.stringify({ ok: false, error: "Failed to create space" });
|
|
302
|
+
return JSON.stringify({ ok: true, space: result });
|
|
303
|
+
},
|
|
304
|
+
}),
|
|
305
|
+
|
|
306
|
+
space_list: tool({
|
|
307
|
+
description:
|
|
308
|
+
"List all spaces you own or are a member of.",
|
|
309
|
+
args: {},
|
|
310
|
+
async execute() {
|
|
311
|
+
const spaces = await client.listSpaces();
|
|
312
|
+
return JSON.stringify({ ok: true, spaces });
|
|
313
|
+
},
|
|
314
|
+
}),
|
|
315
|
+
|
|
316
|
+
space_add_member: tool({
|
|
317
|
+
description:
|
|
318
|
+
"Add a user to an existing shared space with a specified role.",
|
|
319
|
+
args: {
|
|
320
|
+
space_id: tool.schema.string().describe("Space ID"),
|
|
321
|
+
user_id: tool.schema.string().describe("User/tenant ID to add"),
|
|
322
|
+
role: tool.schema.string().describe("Role: admin, member, or reader"),
|
|
323
|
+
},
|
|
324
|
+
async execute(args) {
|
|
325
|
+
const result = await client.addSpaceMember(
|
|
326
|
+
args.space_id,
|
|
327
|
+
args.user_id,
|
|
328
|
+
args.role,
|
|
329
|
+
);
|
|
330
|
+
if (!result) return JSON.stringify({ ok: false, error: "Failed to add member" });
|
|
331
|
+
return JSON.stringify({ ok: true, result });
|
|
332
|
+
},
|
|
333
|
+
}),
|
|
334
|
+
|
|
335
|
+
memory_share: tool({
|
|
336
|
+
description:
|
|
337
|
+
"Share a memory to a team or organization space. Creates a copy with provenance tracking.",
|
|
338
|
+
args: {
|
|
339
|
+
memory_id: tool.schema.string().describe("Memory ID to share"),
|
|
340
|
+
target_space: tool.schema.string().describe("Target space ID"),
|
|
341
|
+
},
|
|
342
|
+
async execute(args) {
|
|
343
|
+
const result = await client.shareMemory(
|
|
344
|
+
args.memory_id,
|
|
345
|
+
args.target_space,
|
|
346
|
+
);
|
|
347
|
+
if (!result) return JSON.stringify({ ok: false, error: "Failed to share memory" });
|
|
348
|
+
return JSON.stringify({ ok: true, result });
|
|
349
|
+
},
|
|
350
|
+
}),
|
|
351
|
+
|
|
352
|
+
memory_pull: tool({
|
|
353
|
+
description:
|
|
354
|
+
"Pull a shared memory from a team/organization space into your personal space.",
|
|
355
|
+
args: {
|
|
356
|
+
memory_id: tool.schema.string().describe("Memory ID to pull"),
|
|
357
|
+
source_space: tool.schema.string().describe("Source space ID"),
|
|
358
|
+
visibility: tool.schema
|
|
359
|
+
.string()
|
|
360
|
+
.optional()
|
|
361
|
+
.describe("Visibility of the pulled copy"),
|
|
362
|
+
},
|
|
363
|
+
async execute(args) {
|
|
364
|
+
const result = await client.pullMemory(
|
|
365
|
+
args.memory_id,
|
|
366
|
+
args.source_space,
|
|
367
|
+
args.visibility,
|
|
368
|
+
);
|
|
369
|
+
if (!result) return JSON.stringify({ ok: false, error: "Failed to pull memory" });
|
|
370
|
+
return JSON.stringify({ ok: true, result });
|
|
371
|
+
},
|
|
372
|
+
}),
|
|
373
|
+
|
|
374
|
+
memory_reshare: tool({
|
|
375
|
+
description:
|
|
376
|
+
"Refresh a stale shared copy with the latest content and vector from the source memory.",
|
|
377
|
+
args: {
|
|
378
|
+
memory_id: tool.schema.string().describe("Shared copy memory ID to refresh"),
|
|
379
|
+
target_space: tool.schema
|
|
380
|
+
.string()
|
|
381
|
+
.optional()
|
|
382
|
+
.describe("Target space containing the copy (optional)"),
|
|
383
|
+
},
|
|
384
|
+
async execute(args) {
|
|
385
|
+
const result = await client.reshareMemory(
|
|
386
|
+
args.memory_id,
|
|
387
|
+
args.target_space,
|
|
388
|
+
);
|
|
389
|
+
if (!result) return JSON.stringify({ ok: false, error: "Failed to reshare memory" });
|
|
390
|
+
return JSON.stringify({ ok: true, result });
|
|
391
|
+
},
|
|
392
|
+
}),
|
|
393
|
+
|
|
394
|
+
memory_toggle: tool({
|
|
395
|
+
description:
|
|
396
|
+
"Toggle Cerebro auto-store ON or OFF for current session. Does NOT affect manual memory_store calls.",
|
|
397
|
+
args: {
|
|
398
|
+
state: tool.schema
|
|
399
|
+
.string()
|
|
400
|
+
.optional()
|
|
401
|
+
.describe("Set to 'on' or 'off'. Omit to check current status."),
|
|
402
|
+
},
|
|
403
|
+
async execute(args) {
|
|
404
|
+
const sessionId = context.getSessionId();
|
|
405
|
+
if (!sessionId) return JSON.stringify({ ok: false, error: "No active session" });
|
|
406
|
+
|
|
407
|
+
const state = args.state?.toLowerCase();
|
|
408
|
+
if (state === "on") {
|
|
409
|
+
setAutoStoreEnabled(sessionId, true);
|
|
410
|
+
return JSON.stringify({ ok: true, auto_store: true, message: "Cerebro auto-store: ON" });
|
|
411
|
+
} else if (state === "off") {
|
|
412
|
+
setAutoStoreEnabled(sessionId, false);
|
|
413
|
+
return JSON.stringify({ ok: true, auto_store: false, message: "Cerebro auto-store: OFF" });
|
|
414
|
+
} else {
|
|
415
|
+
const current = isAutoStoreEnabled(sessionId);
|
|
416
|
+
return JSON.stringify({ ok: true, auto_store: current, message: `Cerebro auto-store: ${current ? "ON" : "OFF"}` });
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
}),
|
|
420
|
+
};
|
|
421
|
+
}
|