opencode-mem 2.6.2 → 2.7.1
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/index.d.ts.map +1 -1
- package/dist/index.js +24 -8
- package/dist/services/ai/providers/openai-chat-completion.d.ts.map +1 -1
- package/dist/services/ai/providers/openai-chat-completion.js +0 -4
- package/dist/services/ai/providers/openai-responses.d.ts.map +1 -1
- package/dist/services/ai/providers/openai-responses.js +0 -10
- package/dist/services/auto-capture.d.ts.map +1 -1
- package/dist/services/auto-capture.js +9 -19
- package/dist/services/cleanup-service.d.ts.map +1 -1
- package/dist/services/cleanup-service.js +0 -13
- package/dist/services/client.d.ts.map +1 -1
- package/dist/services/client.js +4 -6
- package/dist/services/deduplication-service.d.ts.map +1 -1
- package/dist/services/deduplication-service.js +4 -9
- package/dist/services/embedding.d.ts +3 -0
- package/dist/services/embedding.d.ts.map +1 -1
- package/dist/services/embedding.js +26 -6
- package/dist/services/logger.d.ts.map +1 -1
- package/dist/services/logger.js +17 -1
- package/dist/services/migration-service.d.ts.map +1 -1
- package/dist/services/migration-service.js +1 -9
- package/dist/services/sqlite/connection-manager.d.ts +1 -0
- package/dist/services/sqlite/connection-manager.d.ts.map +1 -1
- package/dist/services/sqlite/connection-manager.js +11 -5
- package/dist/services/sqlite/shard-manager.d.ts.map +1 -1
- package/dist/services/sqlite/shard-manager.js +0 -3
- package/dist/services/sqlite/vector-search.d.ts.map +1 -1
- package/dist/services/sqlite/vector-search.js +6 -5
- package/dist/services/user-memory-learning.d.ts.map +1 -1
- package/dist/services/user-memory-learning.js +6 -20
- package/dist/services/user-prompt/user-prompt-manager.d.ts +1 -0
- package/dist/services/user-prompt/user-prompt-manager.d.ts.map +1 -1
- package/dist/services/user-prompt/user-prompt-manager.js +6 -0
- package/dist/services/web-server.d.ts.map +1 -1
- package/dist/services/web-server.js +0 -1
- package/dist/web/app.js +22 -2
- package/dist/web/index.html +12 -1
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -14,15 +14,14 @@ export const OpenCodeMemPlugin = async (ctx) => {
|
|
|
14
14
|
const { directory } = ctx;
|
|
15
15
|
const tags = getTags(directory);
|
|
16
16
|
let webServer = null;
|
|
17
|
+
let idleTimeout = null;
|
|
17
18
|
if (!isConfigured()) {
|
|
18
|
-
log("Plugin disabled - memory system not configured");
|
|
19
19
|
}
|
|
20
20
|
const GLOBAL_PLUGIN_WARMUP_KEY = Symbol.for("opencode-mem.plugin.warmedup");
|
|
21
21
|
if (!globalThis[GLOBAL_PLUGIN_WARMUP_KEY] && isConfigured()) {
|
|
22
22
|
try {
|
|
23
23
|
await memoryClient.warmup();
|
|
24
24
|
globalThis[GLOBAL_PLUGIN_WARMUP_KEY] = true;
|
|
25
|
-
log("Plugin warmup completed");
|
|
26
25
|
}
|
|
27
26
|
catch (error) {
|
|
28
27
|
log("Plugin warmup failed", { error: String(error) });
|
|
@@ -292,12 +291,29 @@ export const OpenCodeMemPlugin = async (ctx) => {
|
|
|
292
291
|
if (!isConfigured())
|
|
293
292
|
return;
|
|
294
293
|
const sessionID = event.properties?.sessionID;
|
|
295
|
-
if (sessionID)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
294
|
+
if (!sessionID)
|
|
295
|
+
return;
|
|
296
|
+
if (idleTimeout)
|
|
297
|
+
clearTimeout(idleTimeout);
|
|
298
|
+
idleTimeout = setTimeout(async () => {
|
|
299
|
+
try {
|
|
300
|
+
await performAutoCapture(ctx, sessionID, directory);
|
|
301
|
+
if (webServer?.isServerOwner()) {
|
|
302
|
+
await performUserProfileLearning(ctx, directory);
|
|
303
|
+
const { cleanupService } = await import("./services/cleanup-service.js");
|
|
304
|
+
if (await cleanupService.shouldRunCleanup())
|
|
305
|
+
await cleanupService.runCleanup();
|
|
306
|
+
const { connectionManager } = await import("./services/sqlite/connection-manager.js");
|
|
307
|
+
connectionManager.checkpointAll();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
log("Idle processing error", { error: String(error) });
|
|
312
|
+
}
|
|
313
|
+
finally {
|
|
314
|
+
idleTimeout = null;
|
|
315
|
+
}
|
|
316
|
+
}, 10000);
|
|
301
317
|
}
|
|
302
318
|
},
|
|
303
319
|
};
|
|
@@ -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;
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-responses.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-responses.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAsBvF,qBAAa,uBAAwB,SAAQ,cAAc;IACzD,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAIpB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"openai-responses.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-responses.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAsBvF,qBAAa,uBAAwB,SAAQ,cAAc;IACzD,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAIpB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IAoH1B,OAAO,CAAC,eAAe;IA+BvB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,gBAAgB;CAsBzB"}
|
|
@@ -89,10 +89,6 @@ export class OpenAIResponsesProvider extends BaseAIProvider {
|
|
|
89
89
|
iterations,
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
-
log("No tool call found, retrying", {
|
|
93
|
-
iteration: iterations,
|
|
94
|
-
expectedTool: toolSchema.function.name,
|
|
95
|
-
});
|
|
96
92
|
currentPrompt = this.buildRetryPrompt(data);
|
|
97
93
|
}
|
|
98
94
|
catch (error) {
|
|
@@ -119,7 +115,6 @@ export class OpenAIResponsesProvider extends BaseAIProvider {
|
|
|
119
115
|
}
|
|
120
116
|
extractToolCall(data, expectedToolName) {
|
|
121
117
|
if (!data.output || !Array.isArray(data.output)) {
|
|
122
|
-
log("Extract tool call: no output array", { hasOutput: !!data.output });
|
|
123
118
|
return null;
|
|
124
119
|
}
|
|
125
120
|
for (const item of data.output) {
|
|
@@ -146,11 +141,6 @@ export class OpenAIResponsesProvider extends BaseAIProvider {
|
|
|
146
141
|
}
|
|
147
142
|
}
|
|
148
143
|
}
|
|
149
|
-
log("No matching function call found", {
|
|
150
|
-
expectedTool: expectedToolName,
|
|
151
|
-
foundTypes: data.output.map((item) => item.type),
|
|
152
|
-
foundNames: data.output.map((item) => item.name).filter(Boolean),
|
|
153
|
-
});
|
|
154
144
|
return null;
|
|
155
145
|
}
|
|
156
146
|
buildRetryPrompt(data) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-capture.d.ts","sourceRoot":"","sources":["../../src/services/auto-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"auto-capture.d.ts","sourceRoot":"","sources":["../../src/services/auto-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAgBvD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA2Ff"}
|
|
@@ -4,12 +4,19 @@ import { log } from "./logger.js";
|
|
|
4
4
|
import { CONFIG } from "../config.js";
|
|
5
5
|
import { userPromptManager } from "./user-prompt/user-prompt-manager.js";
|
|
6
6
|
const MAX_TOOL_INPUT_LENGTH = 100;
|
|
7
|
+
let isCaptureRunning = false;
|
|
7
8
|
export async function performAutoCapture(ctx, sessionID, directory) {
|
|
9
|
+
if (isCaptureRunning)
|
|
10
|
+
return;
|
|
11
|
+
isCaptureRunning = true;
|
|
8
12
|
try {
|
|
9
13
|
const prompt = userPromptManager.getLastUncapturedPrompt(sessionID);
|
|
10
14
|
if (!prompt) {
|
|
11
15
|
return;
|
|
12
16
|
}
|
|
17
|
+
if (!userPromptManager.claimPrompt(prompt.id)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
13
20
|
if (!ctx.client) {
|
|
14
21
|
throw new Error("Client not available");
|
|
15
22
|
}
|
|
@@ -17,13 +24,11 @@ export async function performAutoCapture(ctx, sessionID, directory) {
|
|
|
17
24
|
path: { id: sessionID },
|
|
18
25
|
});
|
|
19
26
|
if (!response.data) {
|
|
20
|
-
log("Auto-capture: no messages in session", { sessionID });
|
|
21
27
|
return;
|
|
22
28
|
}
|
|
23
29
|
const messages = response.data;
|
|
24
30
|
const promptIndex = messages.findIndex((m) => m.info?.id === prompt.messageId);
|
|
25
31
|
if (promptIndex === -1) {
|
|
26
|
-
log("Auto-capture: prompt message not found", { sessionID, messageId: prompt.messageId });
|
|
27
32
|
return;
|
|
28
33
|
}
|
|
29
34
|
const aiMessages = messages.slice(promptIndex + 1);
|
|
@@ -39,7 +44,6 @@ export async function performAutoCapture(ctx, sessionID, directory) {
|
|
|
39
44
|
const context = buildMarkdownContext(prompt.content, textResponses, toolCalls, latestMemory);
|
|
40
45
|
const summaryResult = await generateSummary(context, sessionID, prompt.content);
|
|
41
46
|
if (!summaryResult || summaryResult.type === "skip") {
|
|
42
|
-
log("Auto-capture: skipped non-technical conversation", { sessionID });
|
|
43
47
|
userPromptManager.deletePrompt(prompt.id);
|
|
44
48
|
return;
|
|
45
49
|
}
|
|
@@ -74,20 +78,8 @@ export async function performAutoCapture(ctx, sessionID, directory) {
|
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (CONFIG.showErrorToasts) {
|
|
80
|
-
await ctx.client?.tui
|
|
81
|
-
.showToast({
|
|
82
|
-
body: {
|
|
83
|
-
title: "Auto-Capture Failed",
|
|
84
|
-
message: String(error),
|
|
85
|
-
variant: "error",
|
|
86
|
-
duration: 5000,
|
|
87
|
-
},
|
|
88
|
-
})
|
|
89
|
-
.catch(() => { });
|
|
90
|
-
}
|
|
81
|
+
finally {
|
|
82
|
+
isCaptureRunning = false;
|
|
91
83
|
}
|
|
92
84
|
}
|
|
93
85
|
function extractAIContent(messages) {
|
|
@@ -133,8 +125,6 @@ function extractAIContent(messages) {
|
|
|
133
125
|
async function getLatestProjectMemory(containerTag) {
|
|
134
126
|
try {
|
|
135
127
|
const result = await memoryClient.listMemories(containerTag, 1);
|
|
136
|
-
log("Auto-capture: latest memory list result", { result });
|
|
137
|
-
log("Auto-capture: container tag", { containerTag });
|
|
138
128
|
if (!result.success || result.memories.length === 0) {
|
|
139
129
|
return null;
|
|
140
130
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup-service.d.ts","sourceRoot":"","sources":["../../src/services/cleanup-service.ts"],"names":[],"mappings":"AAOA,UAAU,aAAa;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,SAAS,CAAkB;IAE7B,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAcpC,UAAU,IAAI,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"cleanup-service.d.ts","sourceRoot":"","sources":["../../src/services/cleanup-service.ts"],"names":[],"mappings":"AAOA,UAAU,aAAa;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,SAAS,CAAkB;IAE7B,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAcpC,UAAU,IAAI,OAAO,CAAC,aAAa,CAAC;IAsF1C,SAAS;;;;;;CAQV;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
|
|
@@ -26,7 +26,6 @@ export class CleanupService {
|
|
|
26
26
|
this.isRunning = true;
|
|
27
27
|
this.lastCleanupTime = Date.now();
|
|
28
28
|
try {
|
|
29
|
-
log("Cleanup: starting", { retentionDays: CONFIG.autoCleanupRetentionDays });
|
|
30
29
|
const cutoffTime = Date.now() - CONFIG.autoCleanupRetentionDays * 24 * 60 * 60 * 1000;
|
|
31
30
|
const userShards = shardManager.getAllShards("user", "");
|
|
32
31
|
const projectShards = shardManager.getAllShards("project", "");
|
|
@@ -60,9 +59,6 @@ export class CleanupService {
|
|
|
60
59
|
continue;
|
|
61
60
|
}
|
|
62
61
|
if (protectedMemoryIds.has(memory.id)) {
|
|
63
|
-
if (linkedMemoryIds.has(memory.id)) {
|
|
64
|
-
log("Cleanup: skipped linked memory", { memoryId: memory.id });
|
|
65
|
-
}
|
|
66
62
|
continue;
|
|
67
63
|
}
|
|
68
64
|
vectorSearch.deleteVector(db, memory.id);
|
|
@@ -81,15 +77,6 @@ export class CleanupService {
|
|
|
81
77
|
}
|
|
82
78
|
}
|
|
83
79
|
const promptsDeleted = promptCleanupResult.deleted - linkedMemoryIds.size;
|
|
84
|
-
log("Cleanup: completed", {
|
|
85
|
-
totalDeleted,
|
|
86
|
-
userDeleted,
|
|
87
|
-
projectDeleted,
|
|
88
|
-
promptsDeleted,
|
|
89
|
-
linkedMemoriesProtected: linkedMemoryIds.size,
|
|
90
|
-
pinnedMemoriesSkipped: pinnedSkipped,
|
|
91
|
-
cutoffTime: new Date(cutoffTime).toISOString(),
|
|
92
|
-
});
|
|
93
80
|
return {
|
|
94
81
|
deletedCount: totalDeleted,
|
|
95
82
|
userCount: userDeleted,
|
|
@@ -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;;;;;;;;;;;;;
|
|
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"}
|
package/dist/services/client.js
CHANGED
|
@@ -84,7 +84,6 @@ export class LocalMemoryClient {
|
|
|
84
84
|
const { scope, hash } = extractScopeFromContainerTag(containerTag);
|
|
85
85
|
const shards = shardManager.getAllShards(scope, hash);
|
|
86
86
|
if (shards.length === 0) {
|
|
87
|
-
log("searchMemories: no shards found", { containerTag });
|
|
88
87
|
return { success: true, results: [], total: 0, timing: 0 };
|
|
89
88
|
}
|
|
90
89
|
const results = await vectorSearch.searchAcrossShards(shards, queryVector, containerTag, CONFIG.maxMemories, CONFIG.similarityThreshold, query);
|
|
@@ -142,9 +141,10 @@ export class LocalMemoryClient {
|
|
|
142
141
|
async deleteMemory(memoryId) {
|
|
143
142
|
try {
|
|
144
143
|
await this.initialize();
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
|
|
144
|
+
const userShards = shardManager.getAllShards("user", "");
|
|
145
|
+
const projectShards = shardManager.getAllShards("project", "");
|
|
146
|
+
const allShards = [...userShards, ...projectShards];
|
|
147
|
+
for (const shard of allShards) {
|
|
148
148
|
const db = connectionManager.getConnection(shard.dbPath);
|
|
149
149
|
const memory = vectorSearch.getMemoryById(db, memoryId);
|
|
150
150
|
if (memory) {
|
|
@@ -153,7 +153,6 @@ export class LocalMemoryClient {
|
|
|
153
153
|
return { success: true };
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
-
log("deleteMemory: not found", { memoryId });
|
|
157
156
|
return { success: false, error: "Memory not found" };
|
|
158
157
|
}
|
|
159
158
|
catch (error) {
|
|
@@ -168,7 +167,6 @@ export class LocalMemoryClient {
|
|
|
168
167
|
const { scope, hash } = extractScopeFromContainerTag(containerTag);
|
|
169
168
|
const shards = shardManager.getAllShards(scope, hash);
|
|
170
169
|
if (shards.length === 0) {
|
|
171
|
-
log("listMemories: no shards found", { containerTag });
|
|
172
170
|
return {
|
|
173
171
|
success: true,
|
|
174
172
|
memories: [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deduplication-service.d.ts","sourceRoot":"","sources":["../../src/services/deduplication-service.ts"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACtB,cAAc,EAAE;QACd,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,UAAU,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAED,UAAU,mBAAmB;IAC3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,mBAAmB,EAAE,cAAc,EAAE,CAAC;CACvC;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,SAAS,CAAkB;IAE7B,yBAAyB,IAAI,OAAO,CAAC,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"deduplication-service.d.ts","sourceRoot":"","sources":["../../src/services/deduplication-service.ts"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACtB,cAAc,EAAE;QACd,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,UAAU,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAED,UAAU,mBAAmB;IAC3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,mBAAmB,EAAE,cAAc,EAAE,CAAC;CACvC;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,SAAS,CAAkB;IAE7B,yBAAyB,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAwG/D,OAAO,CAAC,gBAAgB;IAoBxB,SAAS;;;;;CAOV;AAED,eAAO,MAAM,oBAAoB,sBAA6B,CAAC"}
|
|
@@ -14,9 +14,6 @@ export class DeduplicationService {
|
|
|
14
14
|
}
|
|
15
15
|
this.isRunning = true;
|
|
16
16
|
try {
|
|
17
|
-
log("Deduplication: starting", {
|
|
18
|
-
threshold: CONFIG.deduplicationSimilarityThreshold,
|
|
19
|
-
});
|
|
20
17
|
const userShards = shardManager.getAllShards("user", "");
|
|
21
18
|
const projectShards = shardManager.getAllShards("project", "");
|
|
22
19
|
const allShards = [...userShards, ...projectShards];
|
|
@@ -53,9 +50,10 @@ export class DeduplicationService {
|
|
|
53
50
|
}
|
|
54
51
|
}
|
|
55
52
|
const uniqueMemories = Array.from(contentMap.values()).map((arr) => arr[0]);
|
|
53
|
+
const processedIds = new Set();
|
|
56
54
|
for (let i = 0; i < uniqueMemories.length; i++) {
|
|
57
55
|
const mem1 = uniqueMemories[i];
|
|
58
|
-
if (!mem1.vector)
|
|
56
|
+
if (!mem1.vector || processedIds.has(mem1.id))
|
|
59
57
|
continue;
|
|
60
58
|
const vector1 = new Float32Array(new Uint8Array(mem1.vector).buffer);
|
|
61
59
|
const similarGroup = {
|
|
@@ -69,7 +67,7 @@ export class DeduplicationService {
|
|
|
69
67
|
};
|
|
70
68
|
for (let j = i + 1; j < uniqueMemories.length; j++) {
|
|
71
69
|
const mem2 = uniqueMemories[j];
|
|
72
|
-
if (!mem2.vector)
|
|
70
|
+
if (!mem2.vector || processedIds.has(mem2.id))
|
|
73
71
|
continue;
|
|
74
72
|
if (mem1.container_tag !== mem2.container_tag)
|
|
75
73
|
continue;
|
|
@@ -81,6 +79,7 @@ export class DeduplicationService {
|
|
|
81
79
|
content: mem2.content,
|
|
82
80
|
similarity,
|
|
83
81
|
});
|
|
82
|
+
processedIds.add(mem2.id);
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
if (similarGroup.duplicates.length > 0) {
|
|
@@ -88,10 +87,6 @@ export class DeduplicationService {
|
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
|
-
log("Deduplication: completed", {
|
|
92
|
-
exactDeleted,
|
|
93
|
-
nearDuplicateGroupsFound: nearDuplicateGroups.length,
|
|
94
|
-
});
|
|
95
90
|
return {
|
|
96
91
|
exactDuplicatesDeleted: exactDeleted,
|
|
97
92
|
nearDuplicateGroups,
|
|
@@ -2,11 +2,14 @@ export declare class EmbeddingService {
|
|
|
2
2
|
private pipe;
|
|
3
3
|
private initPromise;
|
|
4
4
|
isWarmedUp: boolean;
|
|
5
|
+
private cache;
|
|
6
|
+
private cachedModelName;
|
|
5
7
|
static getInstance(): EmbeddingService;
|
|
6
8
|
warmup(progressCallback?: (progress: any) => void): Promise<void>;
|
|
7
9
|
private initializeModel;
|
|
8
10
|
embed(text: string): Promise<Float32Array>;
|
|
9
11
|
embedWithTimeout(text: string): Promise<Float32Array>;
|
|
12
|
+
clearCache(): void;
|
|
10
13
|
}
|
|
11
14
|
export declare const embeddingService: EmbeddingService;
|
|
12
15
|
//# sourceMappingURL=embedding.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../../src/services/embedding.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../../src/services/embedding.ts"],"names":[],"mappings":"AAoBA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,WAAW,CAA8B;IAC1C,UAAU,EAAE,OAAO,CAAS;IACnC,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,eAAe,CAAuB;IAE9C,MAAM,CAAC,WAAW,IAAI,gBAAgB;IAOhC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;YAOzD,eAAe;IAiBvB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAmD1C,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI3D,UAAU,IAAI,IAAI;CAGnB;AAED,eAAO,MAAM,gBAAgB,kBAAiC,CAAC"}
|
|
@@ -7,6 +7,7 @@ env.allowRemoteModels = true;
|
|
|
7
7
|
env.cacheDir = join(CONFIG.storagePath, ".cache");
|
|
8
8
|
const TIMEOUT_MS = 30000;
|
|
9
9
|
const GLOBAL_EMBEDDING_KEY = Symbol.for("opencode-mem.embedding.instance");
|
|
10
|
+
const MAX_CACHE_SIZE = 100;
|
|
10
11
|
function withTimeout(promise, ms) {
|
|
11
12
|
return Promise.race([
|
|
12
13
|
promise,
|
|
@@ -17,6 +18,8 @@ export class EmbeddingService {
|
|
|
17
18
|
pipe = null;
|
|
18
19
|
initPromise = null;
|
|
19
20
|
isWarmedUp = false;
|
|
21
|
+
cache = new Map();
|
|
22
|
+
cachedModelName = null;
|
|
20
23
|
static getInstance() {
|
|
21
24
|
if (!globalThis[GLOBAL_EMBEDDING_KEY]) {
|
|
22
25
|
globalThis[GLOBAL_EMBEDDING_KEY] = new EmbeddingService();
|
|
@@ -34,16 +37,13 @@ export class EmbeddingService {
|
|
|
34
37
|
async initializeModel(progressCallback) {
|
|
35
38
|
try {
|
|
36
39
|
if (CONFIG.embeddingApiUrl && CONFIG.embeddingApiKey) {
|
|
37
|
-
log("Using OpenAI-compatible API for embeddings");
|
|
38
40
|
this.isWarmedUp = true;
|
|
39
41
|
return;
|
|
40
42
|
}
|
|
41
|
-
log("Downloading embedding model", { model: CONFIG.embeddingModel });
|
|
42
43
|
this.pipe = await pipeline("feature-extraction", CONFIG.embeddingModel, {
|
|
43
44
|
progress_callback: progressCallback,
|
|
44
45
|
});
|
|
45
46
|
this.isWarmedUp = true;
|
|
46
|
-
log("Embedding model ready");
|
|
47
47
|
}
|
|
48
48
|
catch (error) {
|
|
49
49
|
this.initPromise = null;
|
|
@@ -52,12 +52,20 @@ export class EmbeddingService {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
async embed(text) {
|
|
55
|
+
if (this.cachedModelName !== CONFIG.embeddingModel) {
|
|
56
|
+
this.clearCache();
|
|
57
|
+
this.cachedModelName = CONFIG.embeddingModel;
|
|
58
|
+
}
|
|
59
|
+
const cached = this.cache.get(text);
|
|
60
|
+
if (cached)
|
|
61
|
+
return cached;
|
|
55
62
|
if (!this.isWarmedUp && !this.initPromise) {
|
|
56
63
|
await this.warmup();
|
|
57
64
|
}
|
|
58
65
|
if (this.initPromise) {
|
|
59
66
|
await this.initPromise;
|
|
60
67
|
}
|
|
68
|
+
let result;
|
|
61
69
|
if (CONFIG.embeddingApiUrl && CONFIG.embeddingApiKey) {
|
|
62
70
|
const response = await fetch(`${CONFIG.embeddingApiUrl}/embeddings`, {
|
|
63
71
|
method: "POST",
|
|
@@ -74,13 +82,25 @@ export class EmbeddingService {
|
|
|
74
82
|
throw new Error(`API embedding failed: ${response.statusText}`);
|
|
75
83
|
}
|
|
76
84
|
const data = await response.json();
|
|
77
|
-
|
|
85
|
+
result = new Float32Array(data.data[0].embedding);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const output = await this.pipe(text, { pooling: "mean", normalize: true });
|
|
89
|
+
result = new Float32Array(output.data);
|
|
78
90
|
}
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
if (this.cache.size >= MAX_CACHE_SIZE) {
|
|
92
|
+
const firstKey = this.cache.keys().next().value;
|
|
93
|
+
if (firstKey !== undefined)
|
|
94
|
+
this.cache.delete(firstKey);
|
|
95
|
+
}
|
|
96
|
+
this.cache.set(text, result);
|
|
97
|
+
return result;
|
|
81
98
|
}
|
|
82
99
|
async embedWithTimeout(text) {
|
|
83
100
|
return withTimeout(this.embed(text), TIMEOUT_MS);
|
|
84
101
|
}
|
|
102
|
+
clearCache() {
|
|
103
|
+
this.cache.clear();
|
|
104
|
+
}
|
|
85
105
|
}
|
|
86
106
|
export const embeddingService = EmbeddingService.getInstance();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/services/logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/services/logger.ts"],"names":[],"mappings":"AA0CA,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,QAOlD"}
|
package/dist/services/logger.js
CHANGED
|
@@ -1,15 +1,31 @@
|
|
|
1
|
-
import { appendFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
1
|
+
import { appendFileSync, writeFileSync, existsSync, mkdirSync, statSync, renameSync, unlinkSync, } from "fs";
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
const LOG_DIR = join(homedir(), ".opencode-mem");
|
|
5
5
|
const LOG_FILE = join(LOG_DIR, "opencode-mem.log");
|
|
6
|
+
const MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
6
7
|
const GLOBAL_LOGGER_KEY = Symbol.for("opencode-mem.logger.initialized");
|
|
8
|
+
function rotateLog() {
|
|
9
|
+
try {
|
|
10
|
+
if (!existsSync(LOG_FILE))
|
|
11
|
+
return;
|
|
12
|
+
const stats = statSync(LOG_FILE);
|
|
13
|
+
if (stats.size < MAX_LOG_SIZE)
|
|
14
|
+
return;
|
|
15
|
+
const oldLog = LOG_FILE + ".old";
|
|
16
|
+
if (existsSync(oldLog))
|
|
17
|
+
unlinkSync(oldLog);
|
|
18
|
+
renameSync(LOG_FILE, oldLog);
|
|
19
|
+
}
|
|
20
|
+
catch { }
|
|
21
|
+
}
|
|
7
22
|
function ensureLoggerInitialized() {
|
|
8
23
|
if (globalThis[GLOBAL_LOGGER_KEY])
|
|
9
24
|
return;
|
|
10
25
|
if (!existsSync(LOG_DIR)) {
|
|
11
26
|
mkdirSync(LOG_DIR, { recursive: true });
|
|
12
27
|
}
|
|
28
|
+
rotateLog();
|
|
13
29
|
writeFileSync(LOG_FILE, `\n--- Session started: ${new Date().toISOString()} ---\n`, {
|
|
14
30
|
flag: "a",
|
|
15
31
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-service.d.ts","sourceRoot":"","sources":["../../src/services/migration-service.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,KAAK,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,GAAG,UAAU,CAAC;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,aAAa,GAAG,UAAU,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,gBAAgB,CAAC,CAAwC;IAE3D,uBAAuB,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAoDrD,iBAAiB,CACrB,QAAQ,EAAE,aAAa,GAAG,UAAU,EACpC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,GACvD,OAAO,CAAC,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"migration-service.d.ts","sourceRoot":"","sources":["../../src/services/migration-service.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,KAAK,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,GAAG,UAAU,CAAC;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,aAAa,GAAG,UAAU,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,gBAAgB,CAAC,CAAwC;IAE3D,uBAAuB,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAoDrD,iBAAiB,CACrB,QAAQ,EAAE,aAAa,GAAG,UAAU,EACpC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,GACvD,OAAO,CAAC,eAAe,CAAC;YA2Cb,mBAAmB;YA8CnB,gBAAgB;IA2I9B,OAAO,CAAC,cAAc;IAMtB,SAAS;;;;;CAOV;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
|
|
@@ -57,7 +57,6 @@ export class MigrationService {
|
|
|
57
57
|
this.progressCallback = progressCallback;
|
|
58
58
|
const startTime = Date.now();
|
|
59
59
|
try {
|
|
60
|
-
log("Migration: starting", { strategy, model: CONFIG.embeddingModel });
|
|
61
60
|
const mismatch = await this.detectDimensionMismatch();
|
|
62
61
|
if (!mismatch.needsMigration) {
|
|
63
62
|
return {
|
|
@@ -108,10 +107,6 @@ export class MigrationService {
|
|
|
108
107
|
});
|
|
109
108
|
shardManager.deleteShard(shardInfo.shardId);
|
|
110
109
|
deletedShards++;
|
|
111
|
-
log("Migration: deleted shard", {
|
|
112
|
-
shardId: shardInfo.shardId,
|
|
113
|
-
vectorCount: shardInfo.vectorCount,
|
|
114
|
-
});
|
|
115
110
|
}
|
|
116
111
|
catch (error) {
|
|
117
112
|
log("Migration: error deleting shard", {
|
|
@@ -135,6 +130,7 @@ export class MigrationService {
|
|
|
135
130
|
}
|
|
136
131
|
async reEmbedMigration(mismatch, startTime) {
|
|
137
132
|
await embeddingService.warmup();
|
|
133
|
+
embeddingService.clearCache();
|
|
138
134
|
const totalMemories = mismatch.shardMismatches.reduce((sum, s) => sum + s.vectorCount, 0);
|
|
139
135
|
this.reportProgress({
|
|
140
136
|
phase: "preparing",
|
|
@@ -217,10 +213,6 @@ export class MigrationService {
|
|
|
217
213
|
processedCount++;
|
|
218
214
|
}
|
|
219
215
|
}
|
|
220
|
-
log("Migration: re-embedded shard", {
|
|
221
|
-
shardId: shardInfo.shardId,
|
|
222
|
-
count: tempMemories.length,
|
|
223
|
-
});
|
|
224
216
|
}
|
|
225
217
|
catch (error) {
|
|
226
218
|
log("Migration: error processing shard", {
|
|
@@ -8,6 +8,7 @@ export declare class ConnectionManager {
|
|
|
8
8
|
getConnection(dbPath: string): Database;
|
|
9
9
|
closeConnection(dbPath: string): void;
|
|
10
10
|
closeAll(): void;
|
|
11
|
+
checkpointAll(): void;
|
|
11
12
|
}
|
|
12
13
|
export declare const connectionManager: ConnectionManager;
|
|
13
14
|
//# sourceMappingURL=connection-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/connection-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAOtC,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/connection-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAOtC,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,eAAe;IA2EvB,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,aAAa;IAoBrB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAmBvC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IASrC,QAAQ,IAAI,IAAI;IAYhB,aAAa,IAAI,IAAI;CAStB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
|
|
@@ -21,12 +21,10 @@ export class ConnectionManager {
|
|
|
21
21
|
}
|
|
22
22
|
try {
|
|
23
23
|
Database.setCustomSQLite(customPath);
|
|
24
|
-
log("Using custom SQLite library", { path: customPath });
|
|
25
24
|
}
|
|
26
25
|
catch (error) {
|
|
27
26
|
const errorStr = String(error);
|
|
28
27
|
if (errorStr.includes("SQLite already loaded")) {
|
|
29
|
-
log("SQLite already loaded, skipping custom path configuration");
|
|
30
28
|
}
|
|
31
29
|
else {
|
|
32
30
|
throw new Error(`Failed to load custom SQLite library: ${error}\n` + `Path: ${customPath}`);
|
|
@@ -48,12 +46,10 @@ export class ConnectionManager {
|
|
|
48
46
|
if (foundPath) {
|
|
49
47
|
try {
|
|
50
48
|
Database.setCustomSQLite(foundPath);
|
|
51
|
-
log("Auto-detected and using Homebrew SQLite", { path: foundPath });
|
|
52
49
|
}
|
|
53
50
|
catch (error) {
|
|
54
51
|
const errorStr = String(error);
|
|
55
52
|
if (errorStr.includes("SQLite already loaded")) {
|
|
56
|
-
log("SQLite already loaded, skipping auto-detected path configuration");
|
|
57
53
|
}
|
|
58
54
|
else {
|
|
59
55
|
throw new Error(`Failed to load Homebrew SQLite: ${error}\n` + `Path: ${foundPath}`);
|
|
@@ -81,6 +77,7 @@ export class ConnectionManager {
|
|
|
81
77
|
this.sqliteConfigured = true;
|
|
82
78
|
}
|
|
83
79
|
initDatabase(db) {
|
|
80
|
+
db.run("PRAGMA busy_timeout = 5000");
|
|
84
81
|
db.run("PRAGMA journal_mode = WAL");
|
|
85
82
|
db.run("PRAGMA synchronous = NORMAL");
|
|
86
83
|
db.run("PRAGMA cache_size = -64000");
|
|
@@ -105,7 +102,6 @@ export class ConnectionManager {
|
|
|
105
102
|
const hasTags = columns.some((c) => c.name === "tags");
|
|
106
103
|
if (!hasTags && columns.length > 0) {
|
|
107
104
|
db.run("ALTER TABLE memories ADD COLUMN tags TEXT");
|
|
108
|
-
log("Migrated schema: added tags column to memories table");
|
|
109
105
|
}
|
|
110
106
|
db.run(`
|
|
111
107
|
CREATE VIRTUAL TABLE IF NOT EXISTS vec_tags USING vec0(
|
|
@@ -152,5 +148,15 @@ export class ConnectionManager {
|
|
|
152
148
|
}
|
|
153
149
|
this.connections.clear();
|
|
154
150
|
}
|
|
151
|
+
checkpointAll() {
|
|
152
|
+
for (const [path, db] of this.connections) {
|
|
153
|
+
try {
|
|
154
|
+
db.run("PRAGMA wal_checkpoint(PASSIVE)");
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
log("Error checkpointing database", { path, error: String(error) });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
155
161
|
}
|
|
156
162
|
export const connectionManager = new ConnectionManager();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shard-manager.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/shard-manager.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAW;IAC7B,OAAO,CAAC,YAAY,CAAS;;IAQ7B,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,iBAAiB;IAKzB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAsB9E,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE;IAgCvE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS;
|
|
1
|
+
{"version":3,"file":"shard-manager.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/shard-manager.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAW;IAC7B,OAAO,CAAC,YAAY,CAAS;;IAQ7B,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,iBAAiB;IAKzB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAsB9E,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE;IAgCvE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS;IA2BxF,OAAO,CAAC,WAAW;IA2DnB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS;IAetE,OAAO,CAAC,iBAAiB;IAOzB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO3C,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO3C,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAkBhD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAqBnC;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
|
|
@@ -100,7 +100,6 @@ export class ShardManager {
|
|
|
100
100
|
const result = stmt.run(scope, scopeHash, shardIndex, storedPath, now);
|
|
101
101
|
const db = connectionManager.getConnection(fullPath);
|
|
102
102
|
this.initShardDb(db);
|
|
103
|
-
log("Shard created", { scope, scopeHash, shardIndex, fullPath });
|
|
104
103
|
return {
|
|
105
104
|
id: Number(result.lastInsertRowid),
|
|
106
105
|
scope,
|
|
@@ -180,7 +179,6 @@ export class ShardManager {
|
|
|
180
179
|
UPDATE shards SET is_active = 0 WHERE id = ?
|
|
181
180
|
`);
|
|
182
181
|
stmt.run(shardId);
|
|
183
|
-
log("Shard marked read-only", { shardId });
|
|
184
182
|
}
|
|
185
183
|
incrementVectorCount(shardId) {
|
|
186
184
|
const stmt = this.metadataDb.prepare(`
|
|
@@ -228,7 +226,6 @@ export class ShardManager {
|
|
|
228
226
|
}
|
|
229
227
|
const deleteStmt = this.metadataDb.prepare(`DELETE FROM shards WHERE id = ?`);
|
|
230
228
|
deleteStmt.run(shardId);
|
|
231
|
-
log("Shard deleted", { shardId, dbPath: fullPath });
|
|
232
229
|
}
|
|
233
230
|
}
|
|
234
231
|
}
|
|
@@ -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;
|
|
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,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"}
|
|
@@ -94,16 +94,17 @@ export class VectorSearch {
|
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
96
|
async searchAcrossShards(shards, queryVector, containerTag, limit, similarityThreshold, queryText) {
|
|
97
|
-
const
|
|
98
|
-
for (const shard of shards) {
|
|
97
|
+
const shardPromises = shards.map(async (shard) => {
|
|
99
98
|
try {
|
|
100
|
-
|
|
101
|
-
allResults.push(...results);
|
|
99
|
+
return this.searchInShard(shard, queryVector, containerTag, limit, queryText);
|
|
102
100
|
}
|
|
103
101
|
catch (error) {
|
|
104
102
|
log("Shard search error", { shardId: shard.id, error: String(error) });
|
|
103
|
+
return [];
|
|
105
104
|
}
|
|
106
|
-
}
|
|
105
|
+
});
|
|
106
|
+
const resultsArray = await Promise.all(shardPromises);
|
|
107
|
+
const allResults = resultsArray.flat();
|
|
107
108
|
allResults.sort((a, b) => b.similarity - a.similarity);
|
|
108
109
|
return allResults.filter((r) => r.similarity >= similarityThreshold).slice(0, limit);
|
|
109
110
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-memory-learning.d.ts","sourceRoot":"","sources":["../../src/services/user-memory-learning.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"user-memory-learning.d.ts","sourceRoot":"","sources":["../../src/services/user-memory-learning.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAWvD,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAsEf"}
|
|
@@ -3,7 +3,11 @@ import { log } from "./logger.js";
|
|
|
3
3
|
import { CONFIG } from "../config.js";
|
|
4
4
|
import { userPromptManager } from "./user-prompt/user-prompt-manager.js";
|
|
5
5
|
import { userProfileManager } from "./user-profile/user-profile-manager.js";
|
|
6
|
+
let isLearningRunning = false;
|
|
6
7
|
export async function performUserProfileLearning(ctx, directory) {
|
|
8
|
+
if (isLearningRunning)
|
|
9
|
+
return;
|
|
10
|
+
isLearningRunning = true;
|
|
7
11
|
try {
|
|
8
12
|
const count = userPromptManager.countUnanalyzedForUserLearning();
|
|
9
13
|
const threshold = CONFIG.userProfileAnalysisInterval;
|
|
@@ -20,7 +24,6 @@ export async function performUserProfileLearning(ctx, directory) {
|
|
|
20
24
|
const context = buildUserAnalysisContext(prompts, existingProfile);
|
|
21
25
|
const updatedProfileData = await analyzeUserProfile(context, existingProfile);
|
|
22
26
|
if (!updatedProfileData) {
|
|
23
|
-
log("User memory learning: no profile updates", { promptCount: prompts.length });
|
|
24
27
|
userPromptManager.markMultipleAsUserLearningCaptured(prompts.map((p) => p.id));
|
|
25
28
|
return;
|
|
26
29
|
}
|
|
@@ -45,25 +48,8 @@ export async function performUserProfileLearning(ctx, directory) {
|
|
|
45
48
|
.catch(() => { });
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
log("User memory learning error", {
|
|
51
|
-
error: String(error),
|
|
52
|
-
stack: errorStack,
|
|
53
|
-
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
54
|
-
});
|
|
55
|
-
if (CONFIG.showErrorToasts) {
|
|
56
|
-
await ctx.client?.tui
|
|
57
|
-
.showToast({
|
|
58
|
-
body: {
|
|
59
|
-
title: "User Profile Update Failed",
|
|
60
|
-
message: String(error),
|
|
61
|
-
variant: "error",
|
|
62
|
-
duration: 5000,
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
.catch(() => { });
|
|
66
|
-
}
|
|
51
|
+
finally {
|
|
52
|
+
isLearningRunning = false;
|
|
67
53
|
}
|
|
68
54
|
}
|
|
69
55
|
function generateChangeSummary(oldProfile, newProfile) {
|
|
@@ -18,6 +18,7 @@ export declare class UserPromptManager {
|
|
|
18
18
|
getLastUncapturedPrompt(sessionId: string): UserPrompt | null;
|
|
19
19
|
deletePrompt(promptId: string): void;
|
|
20
20
|
markAsCaptured(promptId: string): void;
|
|
21
|
+
claimPrompt(promptId: string): boolean;
|
|
21
22
|
countUncapturedPrompts(): number;
|
|
22
23
|
getUncapturedPrompts(limit: number): UserPrompt[];
|
|
23
24
|
markMultipleAsCaptured(promptIds: string[]): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-prompt-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-prompt/user-prompt-manager.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;
|
|
1
|
+
{"version":3,"file":"user-prompt-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-prompt/user-prompt-manager.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IAiCpB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAa9F,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAc7D,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKtC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAQtC,sBAAsB,IAAI,MAAM;IAMhC,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAUjD,8BAA8B,IAAI,MAAM;IAQxC,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYtD,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKlD,kCAAkC,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAU7D,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE;IAiBpF,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK5D,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAOlD,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAgBtD,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IAiBpF,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE;IAQ5C,OAAO,CAAC,WAAW;CAapB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
|
|
@@ -25,6 +25,7 @@ export class UserPromptManager {
|
|
|
25
25
|
linked_memory_id TEXT
|
|
26
26
|
)
|
|
27
27
|
`);
|
|
28
|
+
this.db.run("UPDATE user_prompts SET captured = 0 WHERE captured = 2");
|
|
28
29
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_user_prompts_session ON user_prompts(session_id)");
|
|
29
30
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_user_prompts_captured ON user_prompts(captured)");
|
|
30
31
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_user_prompts_created ON user_prompts(created_at DESC)");
|
|
@@ -62,6 +63,11 @@ export class UserPromptManager {
|
|
|
62
63
|
const stmt = this.db.prepare(`UPDATE user_prompts SET captured = 1 WHERE id = ?`);
|
|
63
64
|
stmt.run(promptId);
|
|
64
65
|
}
|
|
66
|
+
claimPrompt(promptId) {
|
|
67
|
+
const stmt = this.db.prepare(`UPDATE user_prompts SET captured = 2 WHERE id = ? AND captured = 0`);
|
|
68
|
+
const result = stmt.run(promptId);
|
|
69
|
+
return result.changes > 0;
|
|
70
|
+
}
|
|
65
71
|
countUncapturedPrompts() {
|
|
66
72
|
const stmt = this.db.prepare(`SELECT COUNT(*) as count FROM user_prompts WHERE captured = 0`);
|
|
67
73
|
const row = stmt.get();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-server.d.ts","sourceRoot":"","sources":["../../src/services/web-server.ts"],"names":[],"mappings":"AAOA,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAeD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,YAAY,CAA8B;gBAEtC,MAAM,EAAE,eAAe;IAI7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YASd,MAAM;
|
|
1
|
+
{"version":3,"file":"web-server.d.ts","sourceRoot":"","sources":["../../src/services/web-server.ts"],"names":[],"mappings":"AAOA,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAeD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,YAAY,CAA8B;gBAEtC,MAAM,EAAE,eAAe;IAI7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YASd,MAAM;IAyEd,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC3B,SAAS,IAAI,OAAO;IAIpB,aAAa,IAAI,OAAO;IAIxB,MAAM,IAAI,MAAM;IAIV,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;CAW/C;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAIhF"}
|
package/dist/web/app.js
CHANGED
|
@@ -170,6 +170,7 @@ function groupMemories(items) {
|
|
|
170
170
|
function renderCombinedCard(pair) {
|
|
171
171
|
const { memory, prompt } = pair;
|
|
172
172
|
const isSelected = state.selectedMemories.has(memory.id);
|
|
173
|
+
const isPinned = memory.isPinned || false;
|
|
173
174
|
const similarityHtml =
|
|
174
175
|
memory.similarity !== undefined
|
|
175
176
|
? `<span class="similarity-score">${Math.round(memory.similarity * 100)}%</span>`
|
|
@@ -180,8 +181,20 @@ function renderCombinedCard(pair) {
|
|
|
180
181
|
? `<div class="tags-list">${memory.tags.map((t) => `<span class="tag-badge">${escapeHtml(t)}</span>`).join("")}</div>`
|
|
181
182
|
: "";
|
|
182
183
|
|
|
184
|
+
const pinButton = isPinned
|
|
185
|
+
? `<button class="btn-pin pinned" onclick="unpinMemory('${memory.id}')" title="Unpin"><i data-lucide="pin" class="icon icon-filled"></i></button>`
|
|
186
|
+
: `<button class="btn-pin" onclick="pinMemory('${memory.id}')" title="Pin"><i data-lucide="pin" class="icon"></i></button>`;
|
|
187
|
+
|
|
188
|
+
const createdDate = formatDate(memory.createdAt);
|
|
189
|
+
const updatedDate =
|
|
190
|
+
memory.updatedAt && memory.updatedAt !== memory.createdAt ? formatDate(memory.updatedAt) : null;
|
|
191
|
+
|
|
192
|
+
const dateInfo = updatedDate
|
|
193
|
+
? `<span>Created: ${createdDate}</span><span>Updated: ${updatedDate}</span>`
|
|
194
|
+
: `<span>Created: ${createdDate}</span>`;
|
|
195
|
+
|
|
183
196
|
return `
|
|
184
|
-
<div class="combined-card ${isSelected ? "selected" : ""}" data-id="${memory.id}">
|
|
197
|
+
<div class="combined-card ${isSelected ? "selected" : ""} ${isPinned ? "pinned" : ""}" data-id="${memory.id}">
|
|
185
198
|
<div class="combined-prompt-section">
|
|
186
199
|
<div class="combined-header">
|
|
187
200
|
<span class="badge badge-prompt">USER PROMPT</span>
|
|
@@ -201,9 +214,12 @@ function renderCombinedCard(pair) {
|
|
|
201
214
|
<span class="badge badge-memory">MEMORY</span>
|
|
202
215
|
${memory.memoryType ? `<span class="badge badge-type">${memory.memoryType}</span>` : ""}
|
|
203
216
|
${similarityHtml}
|
|
217
|
+
${isPinned ? '<span class="badge badge-pinned">PINNED</span>' : ""}
|
|
204
218
|
<span class="memory-display-name">${escapeHtml(memory.displayName || memory.id)}</span>
|
|
205
219
|
</div>
|
|
206
220
|
<div class="memory-actions">
|
|
221
|
+
${pinButton}
|
|
222
|
+
<button class="btn-edit" onclick="editMemory('${memory.id}')"><i data-lucide="edit-3" class="icon"></i></button>
|
|
207
223
|
<button class="btn-delete" onclick="deleteMemoryWithLink('${memory.id}', true)">
|
|
208
224
|
<i data-lucide="trash-2" class="icon"></i> Delete Pair
|
|
209
225
|
</button>
|
|
@@ -211,6 +227,10 @@ function renderCombinedCard(pair) {
|
|
|
211
227
|
</div>
|
|
212
228
|
${tagsHtml}
|
|
213
229
|
<div class="memory-content markdown-content">${renderMarkdown(memory.content)}</div>
|
|
230
|
+
<div class="memory-footer">
|
|
231
|
+
${dateInfo}
|
|
232
|
+
<span>ID: ${memory.id}</span>
|
|
233
|
+
</div>
|
|
214
234
|
</div>
|
|
215
235
|
</div>
|
|
216
236
|
`;
|
|
@@ -387,7 +407,7 @@ async function addMemory(e) {
|
|
|
387
407
|
|
|
388
408
|
const content = document.getElementById("add-content").value.trim();
|
|
389
409
|
const containerTag = document.getElementById("add-tag").value;
|
|
390
|
-
const type = document.getElementById("add-type").value
|
|
410
|
+
const type = document.getElementById("add-type").value;
|
|
391
411
|
const tagsStr = document.getElementById("add-tags").value.trim();
|
|
392
412
|
const tags = tagsStr
|
|
393
413
|
? tagsStr
|
package/dist/web/index.html
CHANGED
|
@@ -152,7 +152,18 @@
|
|
|
152
152
|
|
|
153
153
|
<div class="form-group">
|
|
154
154
|
<label>Type:</label>
|
|
155
|
-
<
|
|
155
|
+
<select id="add-type">
|
|
156
|
+
<option value="">other</option>
|
|
157
|
+
<option value="feature">feature</option>
|
|
158
|
+
<option value="bug-fix">bug-fix</option>
|
|
159
|
+
<option value="refactor">refactor</option>
|
|
160
|
+
<option value="architecture">architecture</option>
|
|
161
|
+
<option value="rule">rule</option>
|
|
162
|
+
<option value="documentation">documentation</option>
|
|
163
|
+
<option value="discussion">discussion</option>
|
|
164
|
+
<option value="analysis">analysis</option>
|
|
165
|
+
<option value="configuration">configuration</option>
|
|
166
|
+
</select>
|
|
156
167
|
</div>
|
|
157
168
|
|
|
158
169
|
<div class="form-group">
|