@xiaoxiamimengfb/my-opencode-mem 2.12.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.
Files changed (152) hide show
  1. package/README.md +155 -0
  2. package/dist/config.d.ts +58 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +411 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +427 -0
  8. package/dist/plugin.d.ts +5 -0
  9. package/dist/plugin.d.ts.map +1 -0
  10. package/dist/plugin.js +4 -0
  11. package/dist/services/ai/ai-provider-factory.d.ts +8 -0
  12. package/dist/services/ai/ai-provider-factory.d.ts.map +1 -0
  13. package/dist/services/ai/ai-provider-factory.js +28 -0
  14. package/dist/services/ai/opencode-provider.d.ts +30 -0
  15. package/dist/services/ai/opencode-provider.d.ts.map +1 -0
  16. package/dist/services/ai/opencode-provider.js +332 -0
  17. package/dist/services/ai/provider-config.d.ts +17 -0
  18. package/dist/services/ai/provider-config.d.ts.map +1 -0
  19. package/dist/services/ai/provider-config.js +14 -0
  20. package/dist/services/ai/providers/anthropic-messages.d.ts +12 -0
  21. package/dist/services/ai/providers/anthropic-messages.d.ts.map +1 -0
  22. package/dist/services/ai/providers/anthropic-messages.js +184 -0
  23. package/dist/services/ai/providers/base-provider.d.ts +25 -0
  24. package/dist/services/ai/providers/base-provider.d.ts.map +1 -0
  25. package/dist/services/ai/providers/base-provider.js +23 -0
  26. package/dist/services/ai/providers/google-gemini.d.ts +16 -0
  27. package/dist/services/ai/providers/google-gemini.d.ts.map +1 -0
  28. package/dist/services/ai/providers/google-gemini.js +228 -0
  29. package/dist/services/ai/providers/openai-chat-completion.d.ts +13 -0
  30. package/dist/services/ai/providers/openai-chat-completion.d.ts.map +1 -0
  31. package/dist/services/ai/providers/openai-chat-completion.js +277 -0
  32. package/dist/services/ai/providers/openai-responses.d.ts +14 -0
  33. package/dist/services/ai/providers/openai-responses.d.ts.map +1 -0
  34. package/dist/services/ai/providers/openai-responses.js +182 -0
  35. package/dist/services/ai/session/ai-session-manager.d.ts +21 -0
  36. package/dist/services/ai/session/ai-session-manager.d.ts.map +1 -0
  37. package/dist/services/ai/session/ai-session-manager.js +166 -0
  38. package/dist/services/ai/session/session-types.d.ts +43 -0
  39. package/dist/services/ai/session/session-types.d.ts.map +1 -0
  40. package/dist/services/ai/session/session-types.js +1 -0
  41. package/dist/services/ai/tools/tool-schema.d.ts +41 -0
  42. package/dist/services/ai/tools/tool-schema.d.ts.map +1 -0
  43. package/dist/services/ai/tools/tool-schema.js +24 -0
  44. package/dist/services/ai/validators/user-profile-validator.d.ts +13 -0
  45. package/dist/services/ai/validators/user-profile-validator.d.ts.map +1 -0
  46. package/dist/services/ai/validators/user-profile-validator.js +111 -0
  47. package/dist/services/api-handlers.d.ts +164 -0
  48. package/dist/services/api-handlers.d.ts.map +1 -0
  49. package/dist/services/api-handlers.js +901 -0
  50. package/dist/services/auto-capture.d.ts +3 -0
  51. package/dist/services/auto-capture.d.ts.map +1 -0
  52. package/dist/services/auto-capture.js +306 -0
  53. package/dist/services/cleanup-service.d.ts +23 -0
  54. package/dist/services/cleanup-service.d.ts.map +1 -0
  55. package/dist/services/cleanup-service.js +102 -0
  56. package/dist/services/client.d.ts +118 -0
  57. package/dist/services/client.d.ts.map +1 -0
  58. package/dist/services/client.js +251 -0
  59. package/dist/services/context.d.ts +11 -0
  60. package/dist/services/context.d.ts.map +1 -0
  61. package/dist/services/context.js +24 -0
  62. package/dist/services/deduplication-service.d.ts +30 -0
  63. package/dist/services/deduplication-service.d.ts.map +1 -0
  64. package/dist/services/deduplication-service.js +124 -0
  65. package/dist/services/embedding.d.ts +15 -0
  66. package/dist/services/embedding.d.ts.map +1 -0
  67. package/dist/services/embedding.js +106 -0
  68. package/dist/services/jsonc.d.ts +7 -0
  69. package/dist/services/jsonc.d.ts.map +1 -0
  70. package/dist/services/jsonc.js +76 -0
  71. package/dist/services/language-detector.d.ts +3 -0
  72. package/dist/services/language-detector.d.ts.map +1 -0
  73. package/dist/services/language-detector.js +16 -0
  74. package/dist/services/logger.d.ts +2 -0
  75. package/dist/services/logger.d.ts.map +1 -0
  76. package/dist/services/logger.js +51 -0
  77. package/dist/services/migration-service.d.ts +42 -0
  78. package/dist/services/migration-service.d.ts.map +1 -0
  79. package/dist/services/migration-service.js +250 -0
  80. package/dist/services/privacy.d.ts +3 -0
  81. package/dist/services/privacy.d.ts.map +1 -0
  82. package/dist/services/privacy.js +7 -0
  83. package/dist/services/secret-resolver.d.ts +2 -0
  84. package/dist/services/secret-resolver.d.ts.map +1 -0
  85. package/dist/services/secret-resolver.js +55 -0
  86. package/dist/services/sqlite/connection-manager.d.ts +13 -0
  87. package/dist/services/sqlite/connection-manager.d.ts.map +1 -0
  88. package/dist/services/sqlite/connection-manager.js +74 -0
  89. package/dist/services/sqlite/shard-manager.d.ts +23 -0
  90. package/dist/services/sqlite/shard-manager.d.ts.map +1 -0
  91. package/dist/services/sqlite/shard-manager.js +288 -0
  92. package/dist/services/sqlite/sqlite-bootstrap.d.ts +2 -0
  93. package/dist/services/sqlite/sqlite-bootstrap.d.ts.map +1 -0
  94. package/dist/services/sqlite/sqlite-bootstrap.js +8 -0
  95. package/dist/services/sqlite/types.d.ts +42 -0
  96. package/dist/services/sqlite/types.d.ts.map +1 -0
  97. package/dist/services/sqlite/types.js +1 -0
  98. package/dist/services/sqlite/vector-search.d.ts +29 -0
  99. package/dist/services/sqlite/vector-search.d.ts.map +1 -0
  100. package/dist/services/sqlite/vector-search.js +268 -0
  101. package/dist/services/tags.d.ts +24 -0
  102. package/dist/services/tags.d.ts.map +1 -0
  103. package/dist/services/tags.js +146 -0
  104. package/dist/services/user-memory-learning.d.ts +3 -0
  105. package/dist/services/user-memory-learning.d.ts.map +1 -0
  106. package/dist/services/user-memory-learning.js +231 -0
  107. package/dist/services/user-profile/profile-context.d.ts +2 -0
  108. package/dist/services/user-profile/profile-context.d.ts.map +1 -0
  109. package/dist/services/user-profile/profile-context.js +40 -0
  110. package/dist/services/user-profile/profile-utils.d.ts +3 -0
  111. package/dist/services/user-profile/profile-utils.d.ts.map +1 -0
  112. package/dist/services/user-profile/profile-utils.js +45 -0
  113. package/dist/services/user-profile/types.d.ts +46 -0
  114. package/dist/services/user-profile/types.d.ts.map +1 -0
  115. package/dist/services/user-profile/types.js +1 -0
  116. package/dist/services/user-profile/user-profile-manager.d.ts +23 -0
  117. package/dist/services/user-profile/user-profile-manager.d.ts.map +1 -0
  118. package/dist/services/user-profile/user-profile-manager.js +292 -0
  119. package/dist/services/user-prompt/user-prompt-manager.d.ts +41 -0
  120. package/dist/services/user-prompt/user-prompt-manager.d.ts.map +1 -0
  121. package/dist/services/user-prompt/user-prompt-manager.js +192 -0
  122. package/dist/services/vector-backends/backend-factory.d.ts +3 -0
  123. package/dist/services/vector-backends/backend-factory.d.ts.map +1 -0
  124. package/dist/services/vector-backends/backend-factory.js +104 -0
  125. package/dist/services/vector-backends/exact-scan-backend.d.ts +39 -0
  126. package/dist/services/vector-backends/exact-scan-backend.d.ts.map +1 -0
  127. package/dist/services/vector-backends/exact-scan-backend.js +63 -0
  128. package/dist/services/vector-backends/types.d.ts +51 -0
  129. package/dist/services/vector-backends/types.d.ts.map +1 -0
  130. package/dist/services/vector-backends/types.js +1 -0
  131. package/dist/services/vector-backends/usearch-backend.d.ts +47 -0
  132. package/dist/services/vector-backends/usearch-backend.d.ts.map +1 -0
  133. package/dist/services/vector-backends/usearch-backend.js +174 -0
  134. package/dist/services/web-server-worker.d.ts +2 -0
  135. package/dist/services/web-server-worker.d.ts.map +1 -0
  136. package/dist/services/web-server-worker.js +283 -0
  137. package/dist/services/web-server.d.ts +31 -0
  138. package/dist/services/web-server.d.ts.map +1 -0
  139. package/dist/services/web-server.js +356 -0
  140. package/dist/types/index.d.ts +19 -0
  141. package/dist/types/index.d.ts.map +1 -0
  142. package/dist/types/index.js +1 -0
  143. package/dist/web/app.d.ts +2 -0
  144. package/dist/web/app.d.ts.map +1 -0
  145. package/dist/web/app.js +1194 -0
  146. package/dist/web/favicon.ico +0 -0
  147. package/dist/web/i18n.d.ts +2 -0
  148. package/dist/web/i18n.d.ts.map +1 -0
  149. package/dist/web/i18n.js +265 -0
  150. package/dist/web/index.html +284 -0
  151. package/dist/web/styles.css +1631 -0
  152. package/package.json +71 -0
package/dist/index.js ADDED
@@ -0,0 +1,427 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { memoryClient } from "./services/client.js";
3
+ import { formatContextForPrompt } from "./services/context.js";
4
+ import { getTags } from "./services/tags.js";
5
+ import { stripPrivateContent, isFullyPrivate } from "./services/privacy.js";
6
+ import { performAutoCapture } from "./services/auto-capture.js";
7
+ import { performUserProfileLearning } from "./services/user-memory-learning.js";
8
+ import { userPromptManager } from "./services/user-prompt/user-prompt-manager.js";
9
+ import { startWebServer, WebServer } from "./services/web-server.js";
10
+ import { isConfigured, CONFIG } from "./config.js";
11
+ import { log } from "./services/logger.js";
12
+ import { getLanguageName } from "./services/language-detector.js";
13
+ import { setStatePath, setConnectedProviders } from "./services/ai/opencode-provider.js";
14
+ export const OpenCodeMemPlugin = async (ctx) => {
15
+ const { directory } = ctx;
16
+ const tags = getTags(directory);
17
+ let webServer = null;
18
+ let idleTimeout = null;
19
+ if (!isConfigured()) {
20
+ }
21
+ const GLOBAL_PLUGIN_WARMUP_KEY = Symbol.for("opencode-mem.plugin.warmedup");
22
+ if (!globalThis[GLOBAL_PLUGIN_WARMUP_KEY] && isConfigured()) {
23
+ try {
24
+ await memoryClient.warmup();
25
+ globalThis[GLOBAL_PLUGIN_WARMUP_KEY] = true;
26
+ }
27
+ catch (error) {
28
+ log("Plugin warmup failed", { error: String(error) });
29
+ }
30
+ }
31
+ // Wire opencode state path and provider list — fire-and-forget to avoid blocking init
32
+ // These calls can hang if opencode isn't fully bootstrapped yet
33
+ (async () => {
34
+ try {
35
+ const pathResult = await ctx.client.path.get();
36
+ if (pathResult.data?.state) {
37
+ setStatePath(pathResult.data.state);
38
+ }
39
+ const providerResult = await ctx.client.provider.list();
40
+ if (providerResult.data?.connected) {
41
+ setConnectedProviders(providerResult.data.connected);
42
+ }
43
+ }
44
+ catch (error) {
45
+ log("Failed to initialize opencode provider state", { error: String(error) });
46
+ }
47
+ })();
48
+ if (CONFIG.webServerEnabled) {
49
+ startWebServer({
50
+ port: CONFIG.webServerPort,
51
+ host: CONFIG.webServerHost,
52
+ enabled: CONFIG.webServerEnabled,
53
+ })
54
+ .then((server) => {
55
+ webServer = server;
56
+ const url = webServer.getUrl();
57
+ webServer.setOnTakeoverCallback(async () => {
58
+ if (ctx.client?.tui) {
59
+ ctx.client.tui
60
+ .showToast({
61
+ body: {
62
+ title: "Memory Explorer",
63
+ message: "Took over web server ownership",
64
+ variant: "success",
65
+ duration: 3000,
66
+ },
67
+ })
68
+ .catch(() => { });
69
+ }
70
+ });
71
+ if (webServer.isServerOwner()) {
72
+ if (ctx.client?.tui) {
73
+ ctx.client.tui
74
+ .showToast({
75
+ body: {
76
+ title: "Memory Explorer",
77
+ message: `Web UI started at ${url}`,
78
+ variant: "success",
79
+ duration: 5000,
80
+ },
81
+ })
82
+ .catch(() => { });
83
+ }
84
+ }
85
+ else {
86
+ if (ctx.client?.tui) {
87
+ ctx.client.tui
88
+ .showToast({
89
+ body: {
90
+ title: "Memory Explorer",
91
+ message: `Web UI available at ${url}`,
92
+ variant: "info",
93
+ duration: 3000,
94
+ },
95
+ })
96
+ .catch(() => { });
97
+ }
98
+ }
99
+ })
100
+ .catch((error) => {
101
+ log("Web server failed to start", { error: String(error) });
102
+ if (ctx.client?.tui) {
103
+ ctx.client.tui
104
+ .showToast({
105
+ body: {
106
+ title: "Memory Explorer Error",
107
+ message: `Failed to start: ${String(error)}`,
108
+ variant: "error",
109
+ duration: 5000,
110
+ },
111
+ })
112
+ .catch(() => { });
113
+ }
114
+ });
115
+ }
116
+ const shutdownHandler = async () => {
117
+ try {
118
+ if (webServer) {
119
+ await webServer.stop();
120
+ }
121
+ memoryClient.close();
122
+ process.exit(0);
123
+ }
124
+ catch (error) {
125
+ log("Shutdown error", { error: String(error) });
126
+ process.exit(1);
127
+ }
128
+ };
129
+ process.on("SIGINT", shutdownHandler);
130
+ process.on("SIGTERM", shutdownHandler);
131
+ return {
132
+ "chat.message": async (input, output) => {
133
+ if (!isConfigured() || !CONFIG.chatMessage.enabled)
134
+ return;
135
+ try {
136
+ const textParts = output.parts.filter((p) => p.type === "text");
137
+ if (textParts.length === 0)
138
+ return;
139
+ const userMessage = textParts.map((p) => p.text).join("\n");
140
+ if (!userMessage.trim())
141
+ return;
142
+ userPromptManager.savePrompt(input.sessionID, output.message.id, directory, userMessage);
143
+ const messagesResponse = await ctx.client.session.messages({
144
+ path: { id: input.sessionID },
145
+ });
146
+ const messages = messagesResponse.data || [];
147
+ const hasNonSyntheticUserMessages = messages.some((m) => m.info.role === "user" &&
148
+ !m.parts.every((p) => p.type !== "text" || p.synthetic === true));
149
+ const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
150
+ const isAfterCompaction = lastMessage?.info?.summary === true;
151
+ const shouldInject = CONFIG.chatMessage.injectOn === "always" ||
152
+ !hasNonSyntheticUserMessages ||
153
+ (isAfterCompaction &&
154
+ messages.filter((m) => m.info.role === "user" &&
155
+ !m.parts.every((p) => p.type !== "text" || p.synthetic === true)).length === 1);
156
+ if (!shouldInject)
157
+ return;
158
+ const listResult = await memoryClient.listMemories(tags.project.tag, CONFIG.chatMessage.maxMemories);
159
+ let memories = listResult.success ? listResult.memories : [];
160
+ if (CONFIG.chatMessage.excludeCurrentSession) {
161
+ memories = memories.filter((m) => m.metadata?.sessionID !== input.sessionID);
162
+ }
163
+ if (CONFIG.chatMessage.maxAgeDays) {
164
+ const cutoffDate = Date.now() - CONFIG.chatMessage.maxAgeDays * 86400000;
165
+ memories = memories.filter((m) => new Date(m.createdAt).getTime() > cutoffDate);
166
+ }
167
+ if (memories.length === 0)
168
+ return;
169
+ const projectMemories = {
170
+ results: memories.map((m) => ({
171
+ similarity: 1.0,
172
+ memory: m.summary,
173
+ })),
174
+ total: memories.length,
175
+ timing: 0,
176
+ };
177
+ const userId = tags.user.userEmail || null;
178
+ const memoryContext = formatContextForPrompt(userId, projectMemories);
179
+ if (memoryContext) {
180
+ const contextPart = {
181
+ id: `prt-memory-context-${Date.now()}`,
182
+ sessionID: input.sessionID,
183
+ messageID: output.message.id,
184
+ type: "text",
185
+ text: memoryContext,
186
+ synthetic: true,
187
+ };
188
+ output.parts.unshift(contextPart);
189
+ }
190
+ }
191
+ catch (error) {
192
+ log("chat.message: ERROR", { error: String(error) });
193
+ if (ctx.client?.tui && CONFIG.showErrorToasts) {
194
+ await ctx.client.tui
195
+ .showToast({
196
+ body: {
197
+ title: "Memory System Error",
198
+ message: String(error),
199
+ variant: "error",
200
+ duration: 5000,
201
+ },
202
+ })
203
+ .catch(() => { });
204
+ }
205
+ }
206
+ },
207
+ tool: {
208
+ memory: tool({
209
+ description: `Manage and query project memory (MATCH USER LANGUAGE: ${getLanguageName(CONFIG.autoCaptureLanguage || "en")}). Use 'search' with technical keywords/tags, 'add' to store knowledge, 'profile' for preferences.`,
210
+ args: {
211
+ mode: tool.schema.enum(["add", "search", "profile", "list", "forget", "help"]).optional(),
212
+ content: tool.schema.string().optional(),
213
+ query: tool.schema.string().optional(),
214
+ tags: tool.schema.string().optional(),
215
+ type: tool.schema.string().optional(),
216
+ memoryId: tool.schema.string().optional(),
217
+ limit: tool.schema.number().optional(),
218
+ },
219
+ async execute(args, toolCtx) {
220
+ if (!isConfigured()) {
221
+ return JSON.stringify({
222
+ success: false,
223
+ error: "Memory system not configured properly.",
224
+ });
225
+ }
226
+ const needsWarmup = !(await memoryClient.isReady());
227
+ if (needsWarmup) {
228
+ return JSON.stringify({ success: false, error: "Memory system is initializing." });
229
+ }
230
+ const mode = args.mode || "help";
231
+ const langName = getLanguageName(CONFIG.autoCaptureLanguage || "en");
232
+ try {
233
+ switch (mode) {
234
+ case "help":
235
+ return JSON.stringify({
236
+ success: true,
237
+ message: "Memory System Usage Guide",
238
+ commands: [
239
+ {
240
+ command: "add",
241
+ description: `Store new memory (MATCH USER LANGUAGE: ${langName})`,
242
+ args: ["content", "type?", "tags?"],
243
+ },
244
+ {
245
+ command: "search",
246
+ description: `Search memories via keywords (MATCH USER LANGUAGE: ${langName})`,
247
+ args: ["query"],
248
+ },
249
+ { command: "profile", description: "View user profile", args: [] },
250
+ { command: "list", description: "List recent memories", args: ["limit?"] },
251
+ { command: "forget", description: "Remove memory", args: ["memoryId"] },
252
+ ],
253
+ tagGuidance: "Use technical keywords for search. Tags rank highest.",
254
+ });
255
+ case "add":
256
+ if (!args.content)
257
+ return JSON.stringify({ success: false, error: "content required" });
258
+ const sanitizedContent = stripPrivateContent(args.content);
259
+ if (isFullyPrivate(args.content))
260
+ return JSON.stringify({ success: false, error: "Private content blocked" });
261
+ const tagInfo = tags.project;
262
+ const parsedTags = args.tags
263
+ ? args.tags.split(",").map((t) => t.trim().toLowerCase())
264
+ : undefined;
265
+ const result = await memoryClient.addMemory(sanitizedContent, tagInfo.tag, {
266
+ type: args.type,
267
+ tags: parsedTags,
268
+ displayName: tagInfo.displayName,
269
+ userName: tagInfo.userName,
270
+ userEmail: tagInfo.userEmail,
271
+ projectPath: tagInfo.projectPath,
272
+ projectName: tagInfo.projectName,
273
+ gitRepoUrl: tagInfo.gitRepoUrl,
274
+ });
275
+ return JSON.stringify({
276
+ success: result.success,
277
+ message: `Memory added`,
278
+ id: result.id,
279
+ tags: parsedTags,
280
+ });
281
+ case "search":
282
+ if (!args.query)
283
+ return JSON.stringify({ success: false, error: "query required" });
284
+ const searchRes = await memoryClient.searchMemories(args.query, tags.project.tag);
285
+ if (!searchRes.success)
286
+ return JSON.stringify({ success: false, error: searchRes.error });
287
+ return formatSearchResults(args.query, searchRes, args.limit);
288
+ case "profile":
289
+ const { userProfileManager } = await import("./services/user-profile/user-profile-manager.js");
290
+ const profile = userProfileManager.getActiveProfile(tags.user.userEmail || "unknown");
291
+ if (!profile)
292
+ return JSON.stringify({ success: true, profile: null });
293
+ const pData = JSON.parse(profile.profileData);
294
+ return JSON.stringify({
295
+ success: true,
296
+ profile: {
297
+ ...pData,
298
+ version: profile.version,
299
+ lastAnalyzed: profile.lastAnalyzedAt,
300
+ },
301
+ });
302
+ case "list":
303
+ const listRes = await memoryClient.listMemories(tags.project.tag, args.limit || 20);
304
+ if (!listRes.success)
305
+ return JSON.stringify({ success: false, error: listRes.error });
306
+ return JSON.stringify({
307
+ success: true,
308
+ count: listRes.memories?.length,
309
+ memories: listRes.memories?.map((m) => ({
310
+ id: m.id,
311
+ content: m.summary,
312
+ createdAt: m.createdAt,
313
+ })),
314
+ });
315
+ case "forget":
316
+ if (!args.memoryId)
317
+ return JSON.stringify({ success: false, error: "memoryId required" });
318
+ const delRes = await memoryClient.deleteMemory(args.memoryId);
319
+ return JSON.stringify({ success: delRes.success, message: `Memory removed` });
320
+ default:
321
+ return JSON.stringify({ success: false, error: `Unknown mode: ${mode}` });
322
+ }
323
+ }
324
+ catch (error) {
325
+ return JSON.stringify({ success: false, error: String(error) });
326
+ }
327
+ },
328
+ }),
329
+ },
330
+ event: async (input) => {
331
+ const event = input.event;
332
+ if (event.type === "session.idle") {
333
+ if (!isConfigured())
334
+ return;
335
+ const sessionID = event.properties?.sessionID;
336
+ if (!sessionID)
337
+ return;
338
+ if (idleTimeout)
339
+ clearTimeout(idleTimeout);
340
+ idleTimeout = setTimeout(async () => {
341
+ try {
342
+ await performAutoCapture(ctx, sessionID, directory);
343
+ if (webServer?.isServerOwner()) {
344
+ await performUserProfileLearning(ctx, directory);
345
+ const { cleanupService } = await import("./services/cleanup-service.js");
346
+ if (await cleanupService.shouldRunCleanup())
347
+ await cleanupService.runCleanup();
348
+ const { connectionManager } = await import("./services/sqlite/connection-manager.js");
349
+ connectionManager.checkpointAll();
350
+ }
351
+ }
352
+ catch (error) {
353
+ log("Idle processing error", { error: String(error) });
354
+ }
355
+ finally {
356
+ idleTimeout = null;
357
+ }
358
+ }, 10000);
359
+ }
360
+ if (event.type === "session.compacted") {
361
+ if (!isConfigured() || !CONFIG.compaction.enabled)
362
+ return;
363
+ const sessionID = event.properties?.sessionID;
364
+ if (!sessionID)
365
+ return;
366
+ try {
367
+ const tags = getTags(directory);
368
+ const memoriesResult = await memoryClient.searchMemoriesBySessionID(sessionID, tags.project.tag, CONFIG.compaction.memoryLimit);
369
+ if (!memoriesResult.success || memoriesResult.results.length === 0) {
370
+ return;
371
+ }
372
+ const memoryContext = formatMemoriesForCompaction(memoriesResult.results);
373
+ await ctx.client.session.prompt({
374
+ path: { id: sessionID },
375
+ body: {
376
+ parts: [{ id: `prt-compaction-${Date.now()}`, type: "text", text: memoryContext }],
377
+ noReply: true,
378
+ },
379
+ });
380
+ if (ctx.client?.tui) {
381
+ await ctx.client.tui
382
+ .showToast({
383
+ body: {
384
+ title: "Memory Restored",
385
+ message: `${memoriesResult.results.length} memories injected after compaction`,
386
+ variant: "success",
387
+ duration: 3000,
388
+ },
389
+ })
390
+ .catch(() => { });
391
+ }
392
+ log("Compaction memory injected", {
393
+ sessionID,
394
+ count: memoriesResult.results.length,
395
+ });
396
+ }
397
+ catch (error) {
398
+ log("Compaction handler error", { error: String(error) });
399
+ }
400
+ }
401
+ },
402
+ };
403
+ };
404
+ function formatSearchResults(query, results, limit) {
405
+ const memoryResults = results.results || [];
406
+ return JSON.stringify({
407
+ success: true,
408
+ query,
409
+ count: memoryResults.length,
410
+ results: memoryResults.slice(0, limit || 10).map((r) => ({
411
+ id: r.id,
412
+ content: r.memory || r.chunk,
413
+ similarity: Math.round(r.similarity * 100),
414
+ })),
415
+ });
416
+ }
417
+ function formatMemoriesForCompaction(memories) {
418
+ let output = `## Restored Session Memory\n\n`;
419
+ memories.forEach((m, i) => {
420
+ output += `### Memory ${i + 1}\n`;
421
+ output += `${m.memory}\n\n`;
422
+ if (m.tags && m.tags.length > 0) {
423
+ output += `Tags: ${m.tags.join(", ")}\n\n`;
424
+ }
425
+ });
426
+ return output;
427
+ }
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ declare const OpenCodeMemPlugin: import("@opencode-ai/plugin").Plugin;
3
+ export { OpenCodeMemPlugin };
4
+ export default OpenCodeMemPlugin;
5
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";AACA,QAAA,MAAQ,iBAAiB,sCAA+B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAC7B,eAAe,iBAAiB,CAAC"}
package/dist/plugin.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ const { OpenCodeMemPlugin } = await import("./index.js");
3
+ export { OpenCodeMemPlugin };
4
+ export default OpenCodeMemPlugin;
@@ -0,0 +1,8 @@
1
+ import { BaseAIProvider, type ProviderConfig } from "./providers/base-provider.js";
2
+ import type { AIProviderType } from "./session/session-types.js";
3
+ export declare class AIProviderFactory {
4
+ static createProvider(providerType: AIProviderType, config: ProviderConfig): BaseAIProvider;
5
+ static getSupportedProviders(): AIProviderType[];
6
+ static cleanupExpiredSessions(): number;
7
+ }
8
+ //# sourceMappingURL=ai-provider-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-provider-factory.d.ts","sourceRoot":"","sources":["../../../src/services/ai/ai-provider-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAMnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,cAAc;IAmB3F,MAAM,CAAC,qBAAqB,IAAI,cAAc,EAAE;IAIhD,MAAM,CAAC,sBAAsB,IAAI,MAAM;CAGxC"}
@@ -0,0 +1,28 @@
1
+ import { BaseAIProvider } from "./providers/base-provider.js";
2
+ import { OpenAIChatCompletionProvider } from "./providers/openai-chat-completion.js";
3
+ import { OpenAIResponsesProvider } from "./providers/openai-responses.js";
4
+ import { AnthropicMessagesProvider } from "./providers/anthropic-messages.js";
5
+ import { GoogleGeminiProvider } from "./providers/google-gemini.js";
6
+ import { aiSessionManager } from "./session/ai-session-manager.js";
7
+ export class AIProviderFactory {
8
+ static createProvider(providerType, config) {
9
+ switch (providerType) {
10
+ case "openai-chat":
11
+ return new OpenAIChatCompletionProvider(config, aiSessionManager);
12
+ case "openai-responses":
13
+ return new OpenAIResponsesProvider(config, aiSessionManager);
14
+ case "anthropic":
15
+ return new AnthropicMessagesProvider(config, aiSessionManager);
16
+ case "google-gemini":
17
+ return new GoogleGeminiProvider(config, aiSessionManager);
18
+ default:
19
+ throw new Error(`Unknown provider type: ${providerType}`);
20
+ }
21
+ }
22
+ static getSupportedProviders() {
23
+ return ["openai-chat", "openai-responses", "anthropic", "google-gemini"];
24
+ }
25
+ static cleanupExpiredSessions() {
26
+ return aiSessionManager.cleanupExpiredSessions();
27
+ }
28
+ }
@@ -0,0 +1,30 @@
1
+ import type { ZodType } from "zod";
2
+ type OAuthAuth = {
3
+ type: "oauth";
4
+ refresh: string;
5
+ access: string;
6
+ expires: number;
7
+ };
8
+ type ApiAuth = {
9
+ type: "api";
10
+ key: string;
11
+ };
12
+ type Auth = OAuthAuth | ApiAuth;
13
+ export declare function setStatePath(path: string): void;
14
+ export declare function getStatePath(): string;
15
+ export declare function setConnectedProviders(providers: string[]): void;
16
+ export declare function isProviderConnected(providerName: string): boolean;
17
+ export declare function readOpencodeAuth(statePath: string, providerName: string): Auth;
18
+ export declare function createOAuthFetch(statePath: string, providerName: string): (input: string | Request | URL, init?: RequestInit) => Promise<Response>;
19
+ export declare function createOpencodeAIProvider(providerName: string, auth: Auth, statePath?: string): import("vercel-minimax-ai-provider").MinimaxOpenAIProvider;
20
+ export declare function generateStructuredOutput<T>(options: {
21
+ providerName: string;
22
+ modelId: string;
23
+ statePath: string;
24
+ systemPrompt: string;
25
+ userPrompt: string;
26
+ schema: ZodType<T>;
27
+ temperature?: number;
28
+ }): Promise<T>;
29
+ export {};
30
+ //# sourceMappingURL=opencode-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-provider.d.ts","sourceRoot":"","sources":["../../../src/services/ai/opencode-provider.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEnC,KAAK,SAAS,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AACrF,KAAK,OAAO,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAC5C,KAAK,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;AAMhC,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,YAAY,IAAI,MAAM,CAKrC;AAeD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAa/D;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CA2BjE;AAkBD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAyC9E;AAQD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAkJ1E;AAGD,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,8DAyC5F;AAGD,wBAAsB,wBAAwB,CAAC,CAAC,EAAE,OAAO,EAAE;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,CAAC,CAAC,CAWb"}