opencode-supermemory 2.0.1 → 2.0.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/README.md CHANGED
@@ -199,28 +199,34 @@ Create `~/.config/opencode/supermemory.jsonc`:
199
199
  {
200
200
  // API key (can also use SUPERMEMORY_API_KEY env var)
201
201
  "apiKey": "sm_...",
202
-
202
+
203
203
  // Min similarity for memory retrieval (0-1)
204
204
  "similarityThreshold": 0.6,
205
-
205
+
206
206
  // Max memories injected per request
207
207
  "maxMemories": 5,
208
-
208
+
209
209
  // Max project memories listed
210
210
  "maxProjectMemories": 10,
211
-
211
+
212
212
  // Max profile facts injected
213
213
  "maxProfileItems": 5,
214
-
214
+
215
215
  // Include user profile in context
216
216
  "injectProfile": true,
217
-
218
- // Prefix for container tags
217
+
218
+ // Prefix for container tags (used when userContainerTag/projectContainerTag not set)
219
219
  "containerTagPrefix": "opencode",
220
-
220
+
221
+ // Optional: Set exact user container tag (overrides auto-generated tag)
222
+ "userContainerTag": "my-custom-user-tag",
223
+
224
+ // Optional: Set exact project container tag (overrides auto-generated tag)
225
+ "projectContainerTag": "my-project-tag",
226
+
221
227
  // Extra keyword patterns for memory detection (regex)
222
228
  "keywordPatterns": ["log\\s+this", "write\\s+down"],
223
-
229
+
224
230
  // Context usage ratio that triggers compaction (0-1)
225
231
  "compactionThreshold": 0.80
226
232
  }
@@ -228,6 +234,30 @@ Create `~/.config/opencode/supermemory.jsonc`:
228
234
 
229
235
  All fields optional. Env var `SUPERMEMORY_API_KEY` takes precedence over config file.
230
236
 
237
+ ### Container Tag Selection
238
+
239
+ By default, container tags are auto-generated using `containerTagPrefix` plus a hash:
240
+ - User tag: `{prefix}_user_{hash(git_email)}`
241
+ - Project tag: `{prefix}_project_{hash(directory)}`
242
+
243
+ You can override this by specifying exact container tags:
244
+
245
+ ```jsonc
246
+ {
247
+ // Use a specific container tag for user memories
248
+ "userContainerTag": "my-team-workspace",
249
+
250
+ // Use a specific container tag for project memories
251
+ "projectContainerTag": "my-awesome-project"
252
+ }
253
+ ```
254
+
255
+ This is useful when you want to:
256
+ - Share memories across team members (same `userContainerTag`)
257
+ - Sync memories between different machines for the same project
258
+ - Organize memories using your own naming scheme
259
+ - Integrate with existing Supermemory container tags from other tools
260
+
231
261
  ## Usage with Oh My OpenCode
232
262
 
233
263
  If you're using [Oh My OpenCode](https://github.com/code-yeongyu/oh-my-opencode), disable its built-in auto-compact hook to let supermemory handle context compaction:
package/dist/config.d.ts CHANGED
@@ -6,6 +6,8 @@ export declare const CONFIG: {
6
6
  maxProfileItems: number;
7
7
  injectProfile: boolean;
8
8
  containerTagPrefix: string;
9
+ userContainerTag: string | undefined;
10
+ projectContainerTag: string | undefined;
9
11
  filterPrompt: string;
10
12
  keywordPatterns: string[];
11
13
  compactionThreshold: number;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAiGA,eAAO,MAAM,mBAAmB,oBAAc,CAAC;AAE/C,eAAO,MAAM,MAAM;;;;;;;;;;CAalB,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAmGA,eAAO,MAAM,mBAAmB,oBAAc,CAAC;AAE/C,eAAO,MAAM,MAAM;;;;;;;;;;;;CAelB,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
package/dist/index.js CHANGED
@@ -13773,6 +13773,8 @@ var CONFIG = {
13773
13773
  maxProfileItems: fileConfig.maxProfileItems ?? DEFAULTS.maxProfileItems,
13774
13774
  injectProfile: fileConfig.injectProfile ?? DEFAULTS.injectProfile,
13775
13775
  containerTagPrefix: fileConfig.containerTagPrefix ?? DEFAULTS.containerTagPrefix,
13776
+ userContainerTag: fileConfig.userContainerTag,
13777
+ projectContainerTag: fileConfig.projectContainerTag,
13776
13778
  filterPrompt: fileConfig.filterPrompt ?? DEFAULTS.filterPrompt,
13777
13779
  keywordPatterns: [
13778
13780
  ...DEFAULT_KEYWORD_PATTERNS,
@@ -13801,8 +13803,8 @@ function log(message, data) {
13801
13803
  }
13802
13804
 
13803
13805
  // src/services/client.ts
13804
- var SUPERMEMORY_API_URL = "https://api.supermemory.ai";
13805
13806
  var TIMEOUT_MS = 30000;
13807
+ var MAX_CONVERSATION_CHARS = 1e5;
13806
13808
  function withTimeout(promise2, ms) {
13807
13809
  return Promise.race([
13808
13810
  promise2,
@@ -13812,6 +13814,19 @@ function withTimeout(promise2, ms) {
13812
13814
 
13813
13815
  class SupermemoryClient {
13814
13816
  client = null;
13817
+ formatConversationMessage(message) {
13818
+ const content = typeof message.content === "string" ? message.content : message.content.map((part) => part.type === "text" ? part.text : `[image] ${part.imageUrl.url}`).join(`
13819
+ `);
13820
+ const trimmed = content.trim();
13821
+ if (trimmed.length === 0) {
13822
+ return `[${message.role}]`;
13823
+ }
13824
+ return `[${message.role}] ${trimmed}`;
13825
+ }
13826
+ formatConversationTranscript(messages) {
13827
+ return messages.map((message, idx) => `${idx + 1}. ${this.formatConversationMessage(message)}`).join(`
13828
+ `);
13829
+ }
13815
13830
  getClient() {
13816
13831
  if (!this.client) {
13817
13832
  if (!isConfigured()) {
@@ -13904,34 +13919,64 @@ class SupermemoryClient {
13904
13919
  }
13905
13920
  }
13906
13921
  async ingestConversation(conversationId, messages, containerTags, metadata) {
13907
- log("ingestConversation: start", { conversationId, messageCount: messages.length });
13908
- try {
13909
- const response = await withTimeout(fetch(`${SUPERMEMORY_API_URL}/conversations`, {
13910
- method: "POST",
13911
- headers: {
13912
- "Content-Type": "application/json",
13913
- Authorization: `Bearer ${SUPERMEMORY_API_KEY}`
13914
- },
13915
- body: JSON.stringify({
13916
- conversationId,
13917
- messages,
13918
- containerTags,
13919
- metadata
13920
- })
13921
- }), TIMEOUT_MS);
13922
- if (!response.ok) {
13923
- const errorText = await response.text();
13924
- log("ingestConversation: error response", { status: response.status, error: errorText });
13925
- return { success: false, error: `HTTP ${response.status}: ${errorText}` };
13922
+ log("ingestConversation: start", {
13923
+ conversationId,
13924
+ messageCount: messages.length,
13925
+ containerTags
13926
+ });
13927
+ if (messages.length === 0) {
13928
+ return { success: false, error: "No messages to ingest" };
13929
+ }
13930
+ const uniqueTags = [...new Set(containerTags)].filter((tag) => tag.length > 0);
13931
+ if (uniqueTags.length === 0) {
13932
+ return { success: false, error: "At least one containerTag is required" };
13933
+ }
13934
+ const transcript = this.formatConversationTranscript(messages);
13935
+ const rawContent = `[Conversation ${conversationId}]
13936
+ ${transcript}`;
13937
+ const content = rawContent.length > MAX_CONVERSATION_CHARS ? `${rawContent.slice(0, MAX_CONVERSATION_CHARS)}
13938
+ ...[truncated]` : rawContent;
13939
+ const ingestMetadata = {
13940
+ type: "conversation",
13941
+ conversationId,
13942
+ messageCount: messages.length,
13943
+ originalContainerTags: uniqueTags,
13944
+ ...metadata
13945
+ };
13946
+ const savedIds = [];
13947
+ let firstError = null;
13948
+ for (const tag of uniqueTags) {
13949
+ const result = await this.addMemory(content, tag, ingestMetadata);
13950
+ if (result.success) {
13951
+ savedIds.push(result.id);
13952
+ } else if (!firstError) {
13953
+ firstError = result.error || "Failed to store conversation";
13926
13954
  }
13927
- const result = await response.json();
13928
- log("ingestConversation: success", { conversationId, status: result.status });
13929
- return { success: true, ...result };
13930
- } catch (error45) {
13931
- const errorMessage = error45 instanceof Error ? error45.message : String(error45);
13932
- log("ingestConversation: error", { error: errorMessage });
13933
- return { success: false, error: errorMessage };
13934
13955
  }
13956
+ if (savedIds.length === 0) {
13957
+ log("ingestConversation: error", { conversationId, error: firstError });
13958
+ return {
13959
+ success: false,
13960
+ error: firstError || "Failed to ingest conversation"
13961
+ };
13962
+ }
13963
+ const status = savedIds.length === uniqueTags.length ? "stored" : "partial";
13964
+ const response = {
13965
+ id: savedIds[0],
13966
+ conversationId,
13967
+ status
13968
+ };
13969
+ log("ingestConversation: success", {
13970
+ conversationId,
13971
+ status,
13972
+ storedCount: savedIds.length,
13973
+ requestedCount: uniqueTags.length
13974
+ });
13975
+ return {
13976
+ success: true,
13977
+ ...response,
13978
+ storedMemoryIds: savedIds
13979
+ };
13935
13980
  }
13936
13981
  }
13937
13982
  var supermemoryClient = new SupermemoryClient;
@@ -13998,6 +14043,9 @@ function getGitEmail() {
13998
14043
  }
13999
14044
  }
14000
14045
  function getUserTag() {
14046
+ if (CONFIG.userContainerTag) {
14047
+ return CONFIG.userContainerTag;
14048
+ }
14001
14049
  const email3 = getGitEmail();
14002
14050
  if (email3) {
14003
14051
  return `${CONFIG.containerTagPrefix}_user_${sha256(email3)}`;
@@ -14006,6 +14054,9 @@ function getUserTag() {
14006
14054
  return `${CONFIG.containerTagPrefix}_user_${sha256(fallback)}`;
14007
14055
  }
14008
14056
  function getProjectTag(directory) {
14057
+ if (CONFIG.projectContainerTag) {
14058
+ return CONFIG.projectContainerTag;
14059
+ }
14009
14060
  return `${CONFIG.containerTagPrefix}_project_${sha256(directory)}`;
14010
14061
  }
14011
14062
  function getTags(directory) {
@@ -1,7 +1,9 @@
1
1
  import Supermemory from "supermemory";
2
- import type { MemoryType, ConversationMessage } from "../types/index.js";
2
+ import type { ConversationMessage, MemoryType } from "../types/index.js";
3
3
  export declare class SupermemoryClient {
4
4
  private client;
5
+ private formatConversationMessage;
6
+ private formatConversationTranscript;
5
7
  private getClient;
6
8
  searchMemories(query: string, containerTag: string): Promise<{
7
9
  results: Array<Supermemory.Search.SearchMemoriesResponse.Result>;
@@ -65,6 +67,7 @@ export declare class SupermemoryClient {
65
67
  success: false;
66
68
  error: string;
67
69
  } | {
70
+ storedMemoryIds: string[];
68
71
  id: string;
69
72
  conversationId: string;
70
73
  status: string;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/services/client.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,aAAa,CAAC;AAGtC,OAAO,KAAK,EACV,UAAU,EACV,mBAAmB,EAEpB,MAAM,mBAAmB,CAAC;AAe3B,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAA4B;IAE1C,OAAO,CAAC,SAAS;IAcX,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;;;;;;;;;;IAsBlD,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;;;;;;;;;;IAmB/C,SAAS,CACb,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,UAAU,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE;;;;;;;;;IAqBnE,YAAY,CAAC,QAAQ,EAAE,MAAM;;;;;;;IAgB7B,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,SAAK;;;;;;;;;;;;;;;IAqB7C,kBAAkB,CACtB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,aAAa,EAAE,MAAM,EAAE,EACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;;;;;;;;;;CAoCvD;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/services/client.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,aAAa,CAAC;AAGtC,OAAO,KAAK,EAEV,mBAAmB,EACnB,UAAU,EACX,MAAM,mBAAmB,CAAC;AAc3B,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAA4B;IAE1C,OAAO,CAAC,yBAAyB;IAmBjC,OAAO,CAAC,4BAA4B;IAMpC,OAAO,CAAC,SAAS;IAcX,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;;;;;;;;;;IAsBlD,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;;;;;;;;;;IAmB/C,SAAS,CACb,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,UAAU,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE;;;;;;;;;IAqBnE,YAAY,CAAC,QAAQ,EAAE,MAAM;;;;;;;IAgB7B,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,SAAK;;;;;;;;;;;;;;;IAqB7C,kBAAkB,CACtB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,aAAa,EAAE,MAAM,EAAE,EACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;;;;;;;;;;;CA0EvD;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/services/tags.ts"],"names":[],"mappings":"AAQA,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAO3C;AAED,wBAAgB,UAAU,IAAI,MAAM,CAOnC;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAK5E"}
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/services/tags.ts"],"names":[],"mappings":"AAQA,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAO3C;AAED,wBAAgB,UAAU,IAAI,MAAM,CAanC;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAQvD;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAK5E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-supermemory",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "OpenCode plugin that gives coding agents persistent memory using Supermemory",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",