opencode-mem 2.7.4 → 2.7.6

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/README.md CHANGED
@@ -28,7 +28,7 @@ Add to your OpenCode configuration at `~/.config/opencode/opencode.json`:
28
28
 
29
29
  ```jsonc
30
30
  {
31
- "plugins": ["opencode-mem"]
31
+ "plugins": ["opencode-mem"],
32
32
  }
33
33
  ```
34
34
 
@@ -37,10 +37,10 @@ The plugin downloads automatically on next startup. macOS users with Apple Silic
37
37
  ## Usage Examples
38
38
 
39
39
  ```typescript
40
- memory({ mode: "add", content: "Project uses microservices architecture" })
41
- memory({ mode: "search", query: "architecture decisions" })
42
- memory({ mode: "profile" })
43
- memory({ mode: "list", limit: 10 })
40
+ memory({ mode: "add", content: "Project uses microservices architecture" });
41
+ memory({ mode: "search", query: "architecture decisions" });
42
+ memory({ mode: "profile" });
43
+ memory({ mode: "list", limit: 10 });
44
44
  ```
45
45
 
46
46
  Access the web interface at `http://127.0.0.1:4747` for visual memory browsing and management.
@@ -57,24 +57,38 @@ Configure at `~/.config/opencode/opencode-mem.jsonc`:
57
57
  "embeddingModel": "Xenova/nomic-embed-text-v1",
58
58
  "webServerEnabled": true,
59
59
  "webServerPort": 4747,
60
-
60
+
61
61
  "autoCaptureEnabled": true,
62
62
  "autoCaptureLanguage": "auto",
63
63
  "memoryProvider": "openai-chat",
64
64
  "memoryModel": "gpt-4o-mini",
65
65
  "memoryApiUrl": "https://api.openai.com/v1",
66
66
  "memoryApiKey": "sk-...",
67
-
67
+ "memoryTemperature": 0.3,
68
+
68
69
  "showAutoCaptureToasts": true,
69
70
  "showUserProfileToasts": true,
70
71
  "showErrorToasts": true,
71
-
72
+
72
73
  "userProfileAnalysisInterval": 10,
73
- "maxMemories": 10
74
+ "maxMemories": 10,
75
+
76
+ "compaction": {
77
+ "enabled": true,
78
+ "memoryLimit": 10,
79
+ },
80
+ "chatMessage": {
81
+ "enabled": true,
82
+ "maxMemories": 3,
83
+ "excludeCurrentSession": true,
84
+ "maxAgeDays": undefined,
85
+ "injectOn": "first",
86
+ },
74
87
  }
75
88
  ```
76
89
 
77
90
  **API Key Formats:**
91
+
78
92
  ```jsonc
79
93
  "memoryApiKey": "sk-..."
80
94
  "memoryApiKey": "file://~/.config/opencode/api-key.txt"
package/dist/config.d.ts CHANGED
@@ -20,6 +20,7 @@ export declare const CONFIG: {
20
20
  memoryModel: string | undefined;
21
21
  memoryApiUrl: string | undefined;
22
22
  memoryApiKey: string | undefined;
23
+ memoryTemperature: number | false | undefined;
23
24
  aiSessionRetentionDays: number;
24
25
  webServerEnabled: boolean;
25
26
  webServerPort: number;
@@ -38,6 +39,17 @@ export declare const CONFIG: {
38
39
  showAutoCaptureToasts: boolean;
39
40
  showUserProfileToasts: boolean;
40
41
  showErrorToasts: boolean;
42
+ compaction: {
43
+ enabled: boolean | undefined;
44
+ memoryLimit: number | undefined;
45
+ };
46
+ chatMessage: {
47
+ enabled: boolean | undefined;
48
+ maxMemories: number | undefined;
49
+ excludeCurrentSession: boolean | undefined;
50
+ maxAgeDays: number | undefined;
51
+ injectOn: "first" | "always";
52
+ };
41
53
  };
42
54
  export declare function isConfigured(): boolean;
43
55
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAgYA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;oBA2Bb,aAAa,GACb,kBAAkB,GAClB,WAAW;;;;;;;;;;;;;;;;;;;;;;CA4BhB,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA8ZA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;oBA2Bb,aAAa,GACb,kBAAkB,GAClB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAwCT,OAAO,GACP,QAAQ;;CAEf,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
package/dist/config.js CHANGED
@@ -45,6 +45,17 @@ const DEFAULTS = {
45
45
  showAutoCaptureToasts: true,
46
46
  showUserProfileToasts: true,
47
47
  showErrorToasts: true,
48
+ compaction: {
49
+ enabled: true,
50
+ memoryLimit: 10,
51
+ },
52
+ chatMessage: {
53
+ enabled: true,
54
+ maxMemories: 3,
55
+ excludeCurrentSession: true,
56
+ maxAgeDays: undefined,
57
+ injectOn: "first",
58
+ },
48
59
  };
49
60
  function expandPath(path) {
50
61
  if (path.startsWith("~/")) {
@@ -198,13 +209,18 @@ const CONFIG_TEMPLATE = `{
198
209
 
199
210
  // Maximum iterations for multi-turn AI analysis (for openai-responses and anthropic)
200
211
  "autoCaptureMaxIterations": 5,
201
-
212
+
202
213
  // Timeout per iteration in milliseconds (30 seconds default)
203
214
  "autoCaptureIterationTimeout": 30000,
204
-
215
+
205
216
  // Days to keep AI session history before cleanup
206
217
  "aiSessionRetentionDays": 7,
207
218
 
219
+ // Temperature for AI API requests (set to false to omit parameter for models that don't support it)
220
+ // Some reasoning models (like o1, o3, gpt-5) don't support temperature parameter
221
+ // Set to false and add "memoryTemperature": false in config when using such models
222
+ "memoryTemperature": 0.3,
223
+
208
224
  // Language for auto-capture summaries (default: "auto" for auto-detection)
209
225
  // Options: "auto", "en", "id", "zh", "ja", "es", "fr", "de", "ru", "pt", "ar", "ko"
210
226
  // "autoCaptureLanguage": "auto",
@@ -331,6 +347,7 @@ export const CONFIG = {
331
347
  memoryModel: fileConfig.memoryModel,
332
348
  memoryApiUrl: fileConfig.memoryApiUrl,
333
349
  memoryApiKey: resolveSecretValue(fileConfig.memoryApiKey),
350
+ memoryTemperature: fileConfig.memoryTemperature,
334
351
  aiSessionRetentionDays: fileConfig.aiSessionRetentionDays ?? DEFAULTS.aiSessionRetentionDays,
335
352
  webServerEnabled: fileConfig.webServerEnabled ?? DEFAULTS.webServerEnabled,
336
353
  webServerPort: fileConfig.webServerPort ?? DEFAULTS.webServerPort,
@@ -349,6 +366,17 @@ export const CONFIG = {
349
366
  showAutoCaptureToasts: fileConfig.showAutoCaptureToasts ?? DEFAULTS.showAutoCaptureToasts,
350
367
  showUserProfileToasts: fileConfig.showUserProfileToasts ?? DEFAULTS.showUserProfileToasts,
351
368
  showErrorToasts: fileConfig.showErrorToasts ?? DEFAULTS.showErrorToasts,
369
+ compaction: {
370
+ enabled: fileConfig.compaction?.enabled ?? DEFAULTS.compaction.enabled,
371
+ memoryLimit: fileConfig.compaction?.memoryLimit ?? DEFAULTS.compaction.memoryLimit,
372
+ },
373
+ chatMessage: {
374
+ enabled: fileConfig.chatMessage?.enabled ?? DEFAULTS.chatMessage.enabled,
375
+ maxMemories: fileConfig.chatMessage?.maxMemories ?? DEFAULTS.chatMessage.maxMemories,
376
+ excludeCurrentSession: fileConfig.chatMessage?.excludeCurrentSession ?? DEFAULTS.chatMessage.excludeCurrentSession,
377
+ maxAgeDays: fileConfig.chatMessage?.maxAgeDays,
378
+ injectOn: (fileConfig.chatMessage?.injectOn ?? DEFAULTS.chatMessage.injectOn),
379
+ },
352
380
  };
353
381
  export function isConfigured() {
354
382
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAkB/D,eAAO,MAAM,iBAAiB,EAAE,MAiV/B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAkB/D,eAAO,MAAM,iBAAiB,EAAE,MA+Z/B,CAAC"}
package/dist/index.js CHANGED
@@ -98,7 +98,7 @@ export const OpenCodeMemPlugin = async (ctx) => {
98
98
  process.on("SIGTERM", shutdownHandler);
99
99
  return {
100
100
  "chat.message": async (input, output) => {
101
- if (!isConfigured())
101
+ if (!isConfigured() || !CONFIG.chatMessage.enabled)
102
102
  return;
103
103
  try {
104
104
  const textParts = output.parts.filter((p) => p.type === "text");
@@ -108,42 +108,52 @@ export const OpenCodeMemPlugin = async (ctx) => {
108
108
  if (!userMessage.trim())
109
109
  return;
110
110
  userPromptManager.savePrompt(input.sessionID, output.message.id, directory, userMessage);
111
- const searchResult = await memoryClient.searchMemories(userMessage, tags.project.tag);
112
- if (searchResult.success && searchResult.results.length > 0) {
113
- const relevantMemories = searchResult.results
114
- .filter((m) => {
115
- const memorySessionId = m.metadata?.sessionID;
116
- const isFromOtherSession = memorySessionId !== input.sessionID;
117
- const isRelevant = m.similarity > 0.65;
118
- return isFromOtherSession && isRelevant;
119
- })
120
- .slice(0, 3);
121
- if (relevantMemories.length > 0) {
122
- const projectMemories = {
123
- results: relevantMemories.map((m) => ({
124
- id: m.id,
125
- memory: m.memory,
126
- similarity: m.similarity,
127
- title: m.displayName,
128
- metadata: m.metadata,
129
- })),
130
- total: relevantMemories.length,
131
- timing: 0,
132
- };
133
- const userId = tags.user.userEmail || null;
134
- const memoryContext = formatContextForPrompt(userId, projectMemories);
135
- if (memoryContext) {
136
- const contextPart = {
137
- id: `memory-context-${Date.now()}`,
138
- sessionID: input.sessionID,
139
- messageID: output.message.id,
140
- type: "text",
141
- text: memoryContext,
142
- synthetic: true,
143
- };
144
- output.parts.unshift(contextPart);
145
- }
146
- }
111
+ const messagesResponse = await ctx.client.session.messages({
112
+ path: { id: input.sessionID },
113
+ });
114
+ const messages = messagesResponse.data || [];
115
+ const hasNonSyntheticUserMessages = messages.some((m) => m.info.role === "user" &&
116
+ !m.parts.every((p) => p.type !== "text" || p.synthetic === true));
117
+ const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
118
+ const isAfterCompaction = lastMessage?.info?.summary === true;
119
+ const shouldInject = CONFIG.chatMessage.injectOn === "always" ||
120
+ !hasNonSyntheticUserMessages ||
121
+ (isAfterCompaction &&
122
+ messages.filter((m) => m.info.role === "user" &&
123
+ !m.parts.every((p) => p.type !== "text" || p.synthetic === true)).length === 1);
124
+ if (!shouldInject)
125
+ return;
126
+ const listResult = await memoryClient.listMemories(tags.project.tag, CONFIG.chatMessage.maxMemories);
127
+ let memories = listResult.success ? listResult.memories : [];
128
+ if (CONFIG.chatMessage.excludeCurrentSession) {
129
+ memories = memories.filter((m) => m.metadata?.sessionID !== input.sessionID);
130
+ }
131
+ if (CONFIG.chatMessage.maxAgeDays) {
132
+ const cutoffDate = Date.now() - CONFIG.chatMessage.maxAgeDays * 86400000;
133
+ memories = memories.filter((m) => new Date(m.createdAt).getTime() > cutoffDate);
134
+ }
135
+ if (memories.length === 0)
136
+ return;
137
+ const projectMemories = {
138
+ results: memories.map((m) => ({
139
+ similarity: 1.0,
140
+ memory: m.summary,
141
+ })),
142
+ total: memories.length,
143
+ timing: 0,
144
+ };
145
+ const userId = tags.user.userEmail || null;
146
+ const memoryContext = formatContextForPrompt(userId, projectMemories);
147
+ if (memoryContext) {
148
+ const contextPart = {
149
+ id: `memory-context-${Date.now()}`,
150
+ sessionID: input.sessionID,
151
+ messageID: output.message.id,
152
+ type: "text",
153
+ text: memoryContext,
154
+ synthetic: true,
155
+ };
156
+ output.parts.unshift(contextPart);
147
157
  }
148
158
  }
149
159
  catch (error) {
@@ -315,6 +325,47 @@ export const OpenCodeMemPlugin = async (ctx) => {
315
325
  }
316
326
  }, 10000);
317
327
  }
328
+ if (event.type === "session.compacted") {
329
+ if (!isConfigured() || !CONFIG.compaction.enabled)
330
+ return;
331
+ const sessionID = event.properties?.sessionID;
332
+ if (!sessionID)
333
+ return;
334
+ try {
335
+ const tags = getTags(directory);
336
+ const memoriesResult = await memoryClient.searchMemoriesBySessionID(sessionID, tags.project.tag, CONFIG.compaction.memoryLimit);
337
+ if (!memoriesResult.success || memoriesResult.results.length === 0) {
338
+ return;
339
+ }
340
+ const memoryContext = formatMemoriesForCompaction(memoriesResult.results);
341
+ await ctx.client.session.prompt({
342
+ path: { id: sessionID },
343
+ body: {
344
+ parts: [{ type: "text", text: memoryContext }],
345
+ noReply: true,
346
+ },
347
+ });
348
+ if (ctx.client?.tui) {
349
+ await ctx.client.tui
350
+ .showToast({
351
+ body: {
352
+ title: "Memory Restored",
353
+ message: `${memoriesResult.results.length} memories injected after compaction`,
354
+ variant: "success",
355
+ duration: 3000,
356
+ },
357
+ })
358
+ .catch(() => { });
359
+ }
360
+ log("Compaction memory injected", {
361
+ sessionID,
362
+ count: memoriesResult.results.length,
363
+ });
364
+ }
365
+ catch (error) {
366
+ log("Compaction handler error", { error: String(error) });
367
+ }
368
+ }
318
369
  },
319
370
  };
320
371
  };
@@ -331,3 +382,14 @@ function formatSearchResults(query, results, limit) {
331
382
  })),
332
383
  });
333
384
  }
385
+ function formatMemoriesForCompaction(memories) {
386
+ let output = `## Restored Session Memory\n\n`;
387
+ memories.forEach((m, i) => {
388
+ output += `### Memory ${i + 1}\n`;
389
+ output += `${m.memory}\n\n`;
390
+ if (m.tags && m.tags.length > 0) {
391
+ output += `Tags: ${m.tags.join(", ")}\n\n`;
392
+ }
393
+ });
394
+ return output;
395
+ }
@@ -10,6 +10,7 @@ export interface ProviderConfig {
10
10
  apiKey?: string;
11
11
  maxIterations?: number;
12
12
  iterationTimeout?: number;
13
+ memoryTemperature?: number | false;
13
14
  }
14
15
  export declare abstract class BaseAIProvider {
15
16
  protected config: ProviderConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"base-provider.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/base-provider.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,8BAAsB,cAAc;IAClC,SAAS,CAAC,MAAM,EAAE,cAAc,CAAC;gBAErB,MAAM,EAAE,cAAc;IAIlC,QAAQ,CAAC,eAAe,CACtB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,GAAG,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IAE1B,QAAQ,CAAC,eAAe,IAAI,MAAM;IAElC,QAAQ,CAAC,eAAe,IAAI,OAAO;CACpC"}
1
+ {"version":3,"file":"base-provider.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/base-provider.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC;AAED,8BAAsB,cAAc;IAClC,SAAS,CAAC,MAAM,EAAE,cAAc,CAAC;gBAErB,MAAM,EAAE,cAAc;IAIlC,QAAQ,CAAC,eAAe,CACtB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,GAAG,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IAE1B,QAAQ,CAAC,eAAe,IAAI,MAAM;IAElC,QAAQ,CAAC,eAAe,IAAI,OAAO;CACpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"openai-chat-completion.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-chat-completion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAqBlE,qBAAa,4BAA6B,SAAQ,cAAc;IAC9D,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAI1B,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,iCAAiC;IAoCnC,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;CAyO3B"}
1
+ {"version":3,"file":"openai-chat-completion.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-chat-completion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAqBlE,qBAAa,4BAA6B,SAAQ,cAAc;IAC9D,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAI1B,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,iCAAiC;IAoCnC,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;CAwP3B"}
@@ -116,8 +116,10 @@ export class OpenAIChatCompletionProvider extends BaseAIProvider {
116
116
  messages,
117
117
  tools: [toolSchema],
118
118
  tool_choice: { type: "function", function: { name: toolSchema.function.name } },
119
- temperature: 0.3,
120
119
  };
120
+ if (this.config.memoryTemperature !== false) {
121
+ requestBody.temperature = this.config.memoryTemperature ?? 0.3;
122
+ }
121
123
  const headers = {
122
124
  "Content-Type": "application/json",
123
125
  };
@@ -138,9 +140,16 @@ export class OpenAIChatCompletionProvider extends BaseAIProvider {
138
140
  error: errorText,
139
141
  iteration: iterations,
140
142
  });
143
+ let errorMessage = `API error: ${response.status} - ${errorText}`;
144
+ if (response.status === 400 &&
145
+ errorText.includes("unsupported_value") &&
146
+ errorText.includes("temperature")) {
147
+ errorMessage =
148
+ 'Your model does not support the temperature parameter. Add "memoryTemperature": false to your config file to disable it.';
149
+ }
141
150
  return {
142
151
  success: false,
143
- error: `API error: ${response.status} - ${errorText}`,
152
+ error: errorMessage,
144
153
  iterations,
145
154
  };
146
155
  }
@@ -86,6 +86,33 @@ export declare class LocalMemoryClient {
86
86
  totalPages: number;
87
87
  };
88
88
  }>;
89
+ searchMemoriesBySessionID(sessionID: string, containerTag: string, limit?: number): Promise<{
90
+ success: true;
91
+ results: {
92
+ id: any;
93
+ memory: any;
94
+ similarity: number;
95
+ tags: any;
96
+ metadata: any;
97
+ containerTag: any;
98
+ displayName: any;
99
+ userName: any;
100
+ userEmail: any;
101
+ projectPath: any;
102
+ projectName: any;
103
+ gitRepoUrl: any;
104
+ createdAt: any;
105
+ }[];
106
+ total: number;
107
+ timing: number;
108
+ error?: undefined;
109
+ } | {
110
+ success: false;
111
+ error: string;
112
+ results: never[];
113
+ total: number;
114
+ timing: number;
115
+ }>;
89
116
  }
90
117
  export declare const memoryClient: LocalMemoryClient;
91
118
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/services/client.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA4CpD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,aAAa,CAAkB;;YAIzB,UAAU;IAiBlB,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjE,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAIjC,SAAS,IAAI;QACX,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;QACrB,KAAK,EAAE,OAAO,CAAC;KAChB;IAQD,KAAK,IAAI,IAAI;IAIP,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;;;;;;;;;;IA6BlD,SAAS,CACb,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,UAAU,CAAC;QAClB,MAAM,CAAC,EAAE,QAAQ,GAAG,cAAc,GAAG,QAAQ,GAAG,KAAK,CAAC;QACtD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB;;;;;;;;;IA+DG,YAAY,CAAC,QAAQ,EAAE,MAAM;;;;;;;IA2B7B,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,SAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDpD;AAED,eAAO,MAAM,YAAY,mBAA0B,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/services/client.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA4CpD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,aAAa,CAAkB;;YAIzB,UAAU;IAiBlB,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjE,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAIjC,SAAS,IAAI;QACX,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;QACrB,KAAK,EAAE,OAAO,CAAC;KAChB;IAQD,KAAK,IAAI,IAAI;IAIP,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;;;;;;;;;;IA6BlD,SAAS,CACb,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,UAAU,CAAC;QAClB,MAAM,CAAC,EAAE,QAAQ,GAAG,cAAc,GAAG,QAAQ,GAAG,KAAK,CAAC;QACtD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB;;;;;;;;;IA+DG,YAAY,CAAC,QAAQ,EAAE,MAAM;;;;;;;IA2B7B,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,SAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAuD7C,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4C5F;AAED,eAAO,MAAM,YAAY,mBAA0B,CAAC"}
@@ -209,5 +209,43 @@ export class LocalMemoryClient {
209
209
  };
210
210
  }
211
211
  }
212
+ async searchMemoriesBySessionID(sessionID, containerTag, limit = 10) {
213
+ try {
214
+ await this.initialize();
215
+ const { scope, hash } = extractScopeFromContainerTag(containerTag);
216
+ const shards = shardManager.getAllShards(scope, hash);
217
+ if (shards.length === 0) {
218
+ return { success: true, results: [], total: 0, timing: 0 };
219
+ }
220
+ const allMemories = [];
221
+ for (const shard of shards) {
222
+ const db = connectionManager.getConnection(shard.dbPath);
223
+ const memories = vectorSearch.getMemoriesBySessionID(db, sessionID);
224
+ allMemories.push(...memories);
225
+ }
226
+ allMemories.sort((a, b) => b.created_at - a.created_at);
227
+ const results = allMemories.slice(0, limit).map((row) => ({
228
+ id: row.id,
229
+ memory: row.content,
230
+ similarity: 1.0,
231
+ tags: row.tags || [],
232
+ metadata: row.metadata || {},
233
+ containerTag: row.container_tag,
234
+ displayName: row.display_name,
235
+ userName: row.user_name,
236
+ userEmail: row.user_email,
237
+ projectPath: row.project_path,
238
+ projectName: row.project_name,
239
+ gitRepoUrl: row.git_repo_url,
240
+ createdAt: row.created_at,
241
+ }));
242
+ return { success: true, results, total: results.length, timing: 0 };
243
+ }
244
+ catch (error) {
245
+ const errorMessage = error instanceof Error ? error.message : String(error);
246
+ log("searchMemoriesBySessionID: error", { error: errorMessage });
247
+ return { success: false, error: errorMessage, results: [], total: 0, timing: 0 };
248
+ }
249
+ }
212
250
  }
213
251
  export const memoryClient = new LocalMemoryClient();
@@ -9,6 +9,7 @@ export declare class VectorSearch {
9
9
  listMemories(db: Database, containerTag: string, limit: number): any[];
10
10
  getAllMemories(db: Database): any[];
11
11
  getMemoryById(db: Database, memoryId: string): any | null;
12
+ getMemoriesBySessionID(db: Database, sessionID: string): any[];
12
13
  countVectors(db: Database, containerTag: string): number;
13
14
  countAllVectors(db: Database): number;
14
15
  getDistinctTags(db: Database): any[];
@@ -1 +1 @@
1
- {"version":3,"file":"vector-search.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/vector-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExE,qBAAa,YAAY;IACvB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IA0CtD,aAAa,CACX,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,YAAY,EACzB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,GACjB,YAAY,EAAE;IA0FX,kBAAkB,CACtB,MAAM,EAAE,SAAS,EAAE,EACnB,WAAW,EAAE,YAAY,EACzB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,mBAAmB,EAAE,MAAM,EAC3B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC;IAiB1B,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMlD,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,YAAY,GAAG,IAAI;IAYnG,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE;IAWtE,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,GAAG,EAAE;IAKnC,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAKzD,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAMxD,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM;IAMrC,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,GAAG,EAAE;IAepC,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK/C,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;CAIlD;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
1
+ {"version":3,"file":"vector-search.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/vector-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExE,qBAAa,YAAY;IACvB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IA0CtD,aAAa,CACX,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,YAAY,EACzB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,GACjB,YAAY,EAAE;IA0FX,kBAAkB,CACtB,MAAM,EAAE,SAAS,EAAE,EACnB,WAAW,EAAE,YAAY,EACzB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,mBAAmB,EAAE,MAAM,EAC3B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC;IAiB1B,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMlD,YAAY,CACV,EAAE,EAAE,QAAQ,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,EACpB,UAAU,CAAC,EAAE,YAAY,GACxB,IAAI;IAkBP,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE;IAWtE,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,GAAG,EAAE;IAKnC,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAKzD,sBAAsB,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,GAAG,EAAE;IAgB9D,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAMxD,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM;IAMrC,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,GAAG,EAAE;IAepC,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK/C,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;CAIlD;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
@@ -140,6 +140,19 @@ export class VectorSearch {
140
140
  const stmt = db.prepare(`SELECT * FROM memories WHERE id = ?`);
141
141
  return stmt.get(memoryId);
142
142
  }
143
+ getMemoriesBySessionID(db, sessionID) {
144
+ const stmt = db.prepare(`
145
+ SELECT * FROM memories
146
+ WHERE metadata LIKE ?
147
+ ORDER BY created_at DESC
148
+ `);
149
+ const rows = stmt.all(`%"sessionID":"${sessionID}"%`);
150
+ return rows.map((row) => ({
151
+ ...row,
152
+ tags: row.tags ? row.tags.split(",") : [],
153
+ metadata: row.metadata ? JSON.parse(row.metadata) : {},
154
+ }));
155
+ }
143
156
  countVectors(db, containerTag) {
144
157
  const stmt = db.prepare(`SELECT COUNT(*) as count FROM memories WHERE container_tag = ?`);
145
158
  const result = stmt.get(containerTag);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-mem",
3
- "version": "2.7.4",
3
+ "version": "2.7.6",
4
4
  "description": "OpenCode plugin that gives coding agents persistent memory using local vector database",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
@@ -37,7 +37,7 @@
37
37
  "@xenova/transformers": "^2.17.2",
38
38
  "franc-min": "^6.2.0",
39
39
  "iso-639-3": "^3.0.1",
40
- "sqlite-vec": "^0.1.7-alpha.2"
40
+ "sqlite-vec": "0.1.7-alpha.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/bun": "^1.3.8",