@mingxy/cerebro 1.4.0

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/src/tools.ts ADDED
@@ -0,0 +1,372 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import type { OmemClient } from "./client.js";
3
+
4
+ function extractMemoryIds(result: unknown): string[] {
5
+ if (!result) return [];
6
+ if (Array.isArray(result)) {
7
+ return (result as Array<{ id?: string }>).map((m) => m.id).filter(Boolean) as string[];
8
+ }
9
+ if (typeof result === "object" && result !== null) {
10
+ const r = result as Record<string, unknown>;
11
+ if (Array.isArray(r.memories)) {
12
+ return (r.memories as Array<{ id?: string }>).map((m) => m.id).filter(Boolean) as string[];
13
+ }
14
+ if (Array.isArray(r.results)) {
15
+ return (r.results as Array<{ id?: string; memory?: { id?: string } }>)
16
+ .map((m) => m.id ?? m.memory?.id)
17
+ .filter(Boolean) as string[];
18
+ }
19
+ }
20
+ return [];
21
+ }
22
+
23
+ export interface ToolContext {
24
+ agentId?: string;
25
+ getSessionId: () => string | undefined;
26
+ }
27
+
28
+ export function buildTools(client: OmemClient, containerTags: string[], context: ToolContext) {
29
+ return {
30
+ memory_store: tool({
31
+ description:
32
+ "Store a new memory in the user's long-term memory. " +
33
+ "Use when the user explicitly asks to remember something, " +
34
+ "or when you identify important preferences, facts, or decisions worth preserving.",
35
+ args: {
36
+ content: tool.schema.string().describe("The information to remember"),
37
+ tags: tool.schema
38
+ .array(tool.schema.string())
39
+ .optional()
40
+ .describe("Optional categorization tags"),
41
+ source: tool.schema
42
+ .string()
43
+ .describe("Origin context, e.g. 'conversation', 'code-review', 'user-input'"),
44
+ scope: tool.schema
45
+ .string()
46
+ .optional()
47
+ .describe("Memory scope: 'project' (default, only visible in this project) or 'global' (visible across all projects)"),
48
+ },
49
+ async execute(args) {
50
+ const allTags = [...containerTags, ...(args.tags ?? [])];
51
+ const result = await client.createMemory(
52
+ args.content,
53
+ allTags,
54
+ args.source,
55
+ args.scope ?? "project",
56
+ context.agentId,
57
+ context.getSessionId(),
58
+ );
59
+ if (!result) return JSON.stringify({ ok: false, error: "The omem server may be unavailable." });
60
+ return JSON.stringify({ ok: true, id: result.id, tags: result.tags });
61
+ },
62
+ }),
63
+
64
+ memory_search: tool({
65
+ description:
66
+ "Search the user's long-term memory by semantic similarity. " +
67
+ "Use to recall previously stored preferences, facts, or context.",
68
+ args: {
69
+ query: tool.schema.string().describe("Natural-language search query"),
70
+ limit: tool.schema
71
+ .number()
72
+ .optional()
73
+ .describe("Max results to return (default 10)"),
74
+ scope: tool.schema
75
+ .string()
76
+ .optional()
77
+ .describe("Optional scope filter"),
78
+ },
79
+ async execute(args) {
80
+ const results = await client.searchMemories(
81
+ args.query,
82
+ args.limit ?? 10,
83
+ args.scope,
84
+ containerTags,
85
+ );
86
+ if (results.length === 0) return JSON.stringify({ ok: true, count: 0, results: [] });
87
+ const items = results.map((r) => ({
88
+ id: r.memory.id,
89
+ score: r.score,
90
+ content: r.memory.content.slice(0, 200),
91
+ }));
92
+ return JSON.stringify({ ok: true, count: results.length, results: items });
93
+ },
94
+ }),
95
+
96
+ memory_get: tool({
97
+ description: "Retrieve a specific memory by its ID.",
98
+ args: {
99
+ id: tool.schema.string().describe("Memory ID"),
100
+ },
101
+ async execute(args) {
102
+ const memory = await client.getMemory(args.id);
103
+ if (!memory) return JSON.stringify({ ok: false, error: "not found" });
104
+ return JSON.stringify({ ok: true, memory });
105
+ },
106
+ }),
107
+
108
+ memory_update: tool({
109
+ description:
110
+ "Update the content or tags of an existing memory. " +
111
+ "Use when information needs correction or enrichment.",
112
+ args: {
113
+ id: tool.schema.string().describe("Memory ID to update"),
114
+ content: tool.schema.string().describe("New content"),
115
+ tags: tool.schema
116
+ .array(tool.schema.string())
117
+ .optional()
118
+ .describe("Replacement tags"),
119
+ },
120
+ async execute(args) {
121
+ const result = await client.updateMemory(
122
+ args.id,
123
+ args.content,
124
+ args.tags,
125
+ );
126
+ if (!result) return JSON.stringify({ ok: false, error: `Failed to update memory ${args.id}` });
127
+ return JSON.stringify({ ok: true, id: args.id });
128
+ },
129
+ }),
130
+
131
+ memory_profile: tool({
132
+ description:
133
+ "Get the user profile synthesized from stored memories. Shows preferences, patterns, and key information.",
134
+ args: {},
135
+ async execute() {
136
+ const profile = await client.getProfile();
137
+ if (!profile) return JSON.stringify({ ok: false, error: "Failed to get profile" });
138
+ return JSON.stringify({ ok: true, profile });
139
+ },
140
+ }),
141
+
142
+ memory_list: tool({
143
+ description:
144
+ "List the most recent memories. Use to browse what's been remembered without a search query.",
145
+ args: {
146
+ limit: tool.schema
147
+ .number()
148
+ .optional()
149
+ .describe("Max memories to return (default: 20)"),
150
+ },
151
+ async execute(args) {
152
+ const memories = await client.listRecent(args.limit ?? 20);
153
+ if (memories.length === 0) return JSON.stringify({ ok: true, count: 0, memories: [] });
154
+ const items = memories.map((m) => ({
155
+ id: m.id,
156
+ content: m.content.slice(0, 120),
157
+ category: m.category,
158
+ tags: m.tags,
159
+ }));
160
+ return JSON.stringify({ ok: true, count: memories.length, memories: items });
161
+ },
162
+ }),
163
+
164
+ session_recalls: tool({
165
+ description:
166
+ "List the injection records for a session. Use to see what memories have been recalled into the current session.",
167
+ args: {
168
+ session_id: tool.schema
169
+ .string()
170
+ .describe("Session ID to query recall records for"),
171
+ },
172
+ async execute(args) {
173
+ const recalls = await client.listSessionRecalls(args.session_id);
174
+ if (recalls.length === 0) return JSON.stringify({ ok: true, count: 0, recalls: [] });
175
+ return JSON.stringify({ ok: true, count: recalls.length, recalls });
176
+ },
177
+ }),
178
+
179
+ memory_ingest: tool({
180
+ description:
181
+ "Ingest conversation messages for intelligent extraction. The system extracts atomic facts, deduplicates, and reconciles with existing memories.",
182
+ args: {
183
+ messages: tool.schema
184
+ .array(
185
+ tool.schema.object({
186
+ role: tool.schema.string().describe("Message role: user, assistant, or system"),
187
+ content: tool.schema.string().describe("Message content"),
188
+ }),
189
+ )
190
+ .describe("Conversation messages to ingest"),
191
+ mode: tool.schema
192
+ .enum(["smart", "raw"])
193
+ .optional()
194
+ .describe("Extraction mode: 'smart' (default) or 'raw'"),
195
+ tags: tool.schema
196
+ .array(tool.schema.string())
197
+ .optional()
198
+ .describe("Tags to apply to extracted memories"),
199
+ session_id: tool.schema
200
+ .string()
201
+ .optional()
202
+ .describe("Session ID to associate with the ingestion"),
203
+ },
204
+ async execute(args) {
205
+ const result = await client.ingestMessages(args.messages, {
206
+ mode: args.mode ?? "smart",
207
+ tags: args.tags,
208
+ sessionId: args.session_id,
209
+ });
210
+ if (result === null) return JSON.stringify({ ok: false, error: "Ingestion failed" });
211
+ if (args.session_id) {
212
+ const memoryIds = extractMemoryIds(result);
213
+ if (memoryIds.length > 0) {
214
+ await client.recordSessionRecall(
215
+ args.session_id,
216
+ memoryIds,
217
+ "manual",
218
+ args.messages.map((m) => m.content).join("\n").slice(0, 200),
219
+ 0,
220
+ 0,
221
+ ).catch(() => {});
222
+ }
223
+ }
224
+ return JSON.stringify({ ok: true, result });
225
+ },
226
+ }),
227
+
228
+ memory_stats: tool({
229
+ description:
230
+ "Get statistics about stored memories — counts by category, type, tier, and timeline.",
231
+ args: {},
232
+ async execute() {
233
+ const stats = await client.getStats();
234
+ if (!stats) return JSON.stringify({ ok: false, error: "Failed to get stats" });
235
+ return JSON.stringify({ ok: true, stats });
236
+ },
237
+ }),
238
+
239
+ memory_delete: tool({
240
+ description:
241
+ "Delete a memory by ID. Use when the user asks to forget something.",
242
+ args: {
243
+ id: tool.schema.string().describe("Memory ID to delete"),
244
+ },
245
+ async execute(args) {
246
+ try {
247
+ await client.deleteMemory(args.id);
248
+ return JSON.stringify({ ok: true, id: args.id });
249
+ } catch {
250
+ return JSON.stringify({ ok: false, error: `Failed to delete memory ${args.id}` });
251
+ }
252
+ },
253
+ }),
254
+
255
+ space_create: tool({
256
+ description:
257
+ "Create a shared space (team or organization) for sharing memories across users and agents.",
258
+ args: {
259
+ name: tool.schema.string().describe("Name of the space"),
260
+ space_type: tool.schema
261
+ .string()
262
+ .describe("Type of space: 'team' or 'organization'"),
263
+ members: tool.schema
264
+ .array(
265
+ tool.schema.object({
266
+ user_id: tool.schema.string().describe("User/tenant ID to add"),
267
+ role: tool.schema.string().describe("Member role: admin, member, or reader"),
268
+ }),
269
+ )
270
+ .optional()
271
+ .describe("Initial members to add"),
272
+ },
273
+ async execute(args) {
274
+ const result = await client.createSpace(
275
+ args.name,
276
+ args.space_type,
277
+ args.members,
278
+ );
279
+ if (!result) return JSON.stringify({ ok: false, error: "Failed to create space" });
280
+ return JSON.stringify({ ok: true, space: result });
281
+ },
282
+ }),
283
+
284
+ space_list: tool({
285
+ description:
286
+ "List all spaces you own or are a member of.",
287
+ args: {},
288
+ async execute() {
289
+ const spaces = await client.listSpaces();
290
+ return JSON.stringify({ ok: true, spaces });
291
+ },
292
+ }),
293
+
294
+ space_add_member: tool({
295
+ description:
296
+ "Add a user to an existing shared space with a specified role.",
297
+ args: {
298
+ space_id: tool.schema.string().describe("Space ID"),
299
+ user_id: tool.schema.string().describe("User/tenant ID to add"),
300
+ role: tool.schema.string().describe("Role: admin, member, or reader"),
301
+ },
302
+ async execute(args) {
303
+ const result = await client.addSpaceMember(
304
+ args.space_id,
305
+ args.user_id,
306
+ args.role,
307
+ );
308
+ if (!result) return JSON.stringify({ ok: false, error: "Failed to add member" });
309
+ return JSON.stringify({ ok: true, result });
310
+ },
311
+ }),
312
+
313
+ memory_share: tool({
314
+ description:
315
+ "Share a memory to a team or organization space. Creates a copy with provenance tracking.",
316
+ args: {
317
+ memory_id: tool.schema.string().describe("Memory ID to share"),
318
+ target_space: tool.schema.string().describe("Target space ID"),
319
+ },
320
+ async execute(args) {
321
+ const result = await client.shareMemory(
322
+ args.memory_id,
323
+ args.target_space,
324
+ );
325
+ if (!result) return JSON.stringify({ ok: false, error: "Failed to share memory" });
326
+ return JSON.stringify({ ok: true, result });
327
+ },
328
+ }),
329
+
330
+ memory_pull: tool({
331
+ description:
332
+ "Pull a shared memory from a team/organization space into your personal space.",
333
+ args: {
334
+ memory_id: tool.schema.string().describe("Memory ID to pull"),
335
+ source_space: tool.schema.string().describe("Source space ID"),
336
+ visibility: tool.schema
337
+ .string()
338
+ .optional()
339
+ .describe("Visibility of the pulled copy"),
340
+ },
341
+ async execute(args) {
342
+ const result = await client.pullMemory(
343
+ args.memory_id,
344
+ args.source_space,
345
+ args.visibility,
346
+ );
347
+ if (!result) return JSON.stringify({ ok: false, error: "Failed to pull memory" });
348
+ return JSON.stringify({ ok: true, result });
349
+ },
350
+ }),
351
+
352
+ memory_reshare: tool({
353
+ description:
354
+ "Refresh a stale shared copy with the latest content and vector from the source memory.",
355
+ args: {
356
+ memory_id: tool.schema.string().describe("Shared copy memory ID to refresh"),
357
+ target_space: tool.schema
358
+ .string()
359
+ .optional()
360
+ .describe("Target space containing the copy (optional)"),
361
+ },
362
+ async execute(args) {
363
+ const result = await client.reshareMemory(
364
+ args.memory_id,
365
+ args.target_space,
366
+ );
367
+ if (!result) return JSON.stringify({ ok: false, error: "Failed to reshare memory" });
368
+ return JSON.stringify({ ok: true, result });
369
+ },
370
+ }),
371
+ };
372
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "outDir": "dist",
11
+ "rootDir": "src",
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "resolveJsonModule": true,
19
+ "isolatedModules": true,
20
+ "types": ["node"]
21
+ },
22
+ "include": ["src/**/*.ts"],
23
+ "exclude": ["node_modules", "dist"]
24
+ }