opencode-mem 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -477
- package/dist/config.d.ts +5 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +46 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -88
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +1 -8
- package/dist/services/ai/ai-provider-factory.d.ts +8 -0
- package/dist/services/ai/ai-provider-factory.d.ts.map +1 -0
- package/dist/services/ai/ai-provider-factory.js +25 -0
- package/dist/services/ai/providers/anthropic-messages.d.ts +13 -0
- package/dist/services/ai/providers/anthropic-messages.d.ts.map +1 -0
- package/dist/services/ai/providers/anthropic-messages.js +176 -0
- package/dist/services/ai/providers/base-provider.d.ts +21 -0
- package/dist/services/ai/providers/base-provider.d.ts.map +1 -0
- package/dist/services/ai/providers/base-provider.js +6 -0
- package/dist/services/ai/providers/openai-chat-completion.d.ts +12 -0
- package/dist/services/ai/providers/openai-chat-completion.d.ts.map +1 -0
- package/dist/services/ai/providers/openai-chat-completion.js +181 -0
- package/dist/services/ai/providers/openai-responses.d.ts +14 -0
- package/dist/services/ai/providers/openai-responses.d.ts.map +1 -0
- package/dist/services/ai/providers/openai-responses.js +191 -0
- package/dist/services/ai/session/ai-session-manager.d.ts +21 -0
- package/dist/services/ai/session/ai-session-manager.d.ts.map +1 -0
- package/dist/services/ai/session/ai-session-manager.js +165 -0
- package/dist/services/ai/session/session-types.d.ts +43 -0
- package/dist/services/ai/session/session-types.d.ts.map +1 -0
- package/dist/services/ai/session/session-types.js +1 -0
- package/dist/services/ai/tools/tool-schema.d.ts +41 -0
- package/dist/services/ai/tools/tool-schema.d.ts.map +1 -0
- package/dist/services/ai/tools/tool-schema.js +24 -0
- package/dist/services/api-handlers.d.ts +11 -3
- package/dist/services/api-handlers.d.ts.map +1 -1
- package/dist/services/api-handlers.js +143 -30
- package/dist/services/auto-capture.d.ts +1 -30
- package/dist/services/auto-capture.d.ts.map +1 -1
- package/dist/services/auto-capture.js +199 -396
- package/dist/services/cleanup-service.d.ts +3 -0
- package/dist/services/cleanup-service.d.ts.map +1 -1
- package/dist/services/cleanup-service.js +31 -4
- package/dist/services/client.d.ts +1 -0
- package/dist/services/client.d.ts.map +1 -1
- package/dist/services/client.js +3 -11
- package/dist/services/sqlite/connection-manager.d.ts.map +1 -1
- package/dist/services/sqlite/connection-manager.js +8 -4
- package/dist/services/user-memory-learning.d.ts +3 -0
- package/dist/services/user-memory-learning.d.ts.map +1 -0
- package/dist/services/user-memory-learning.js +157 -0
- package/dist/services/user-prompt/user-prompt-manager.d.ts +38 -0
- package/dist/services/user-prompt/user-prompt-manager.d.ts.map +1 -0
- package/dist/services/user-prompt/user-prompt-manager.js +164 -0
- package/dist/services/web-server-worker.js +27 -6
- package/dist/services/web-server.d.ts.map +1 -1
- package/dist/services/web-server.js +0 -5
- package/dist/types/index.d.ts +5 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/web/app.js +210 -120
- package/dist/web/index.html +14 -10
- package/dist/web/styles.css +326 -1
- package/package.json +4 -1
- package/dist/services/compaction.d.ts +0 -92
- package/dist/services/compaction.d.ts.map +0 -1
- package/dist/services/compaction.js +0 -421
- package/dist/services/sqlite-client.d.ts +0 -116
- package/dist/services/sqlite-client.d.ts.map +0 -1
- package/dist/services/sqlite-client.js +0 -284
- package/dist/services/web-server-lock.d.ts +0 -12
- package/dist/services/web-server-lock.d.ts.map +0 -1
- package/dist/services/web-server-lock.js +0 -157
- package/dist/web/favicon.svg +0 -14
|
@@ -3,6 +3,7 @@ import { vectorSearch } from "./sqlite/vector-search.js";
|
|
|
3
3
|
import { connectionManager } from "./sqlite/connection-manager.js";
|
|
4
4
|
import { CONFIG } from "../config.js";
|
|
5
5
|
import { log } from "./logger.js";
|
|
6
|
+
import { userPromptManager } from "./user-prompt/user-prompt-manager.js";
|
|
6
7
|
export class CleanupService {
|
|
7
8
|
lastCleanupTime = 0;
|
|
8
9
|
isRunning = false;
|
|
@@ -30,21 +31,40 @@ export class CleanupService {
|
|
|
30
31
|
const userShards = shardManager.getAllShards("user", "");
|
|
31
32
|
const projectShards = shardManager.getAllShards("project", "");
|
|
32
33
|
const allShards = [...userShards, ...projectShards];
|
|
34
|
+
const pinnedMemoryIds = new Set();
|
|
35
|
+
for (const shard of allShards) {
|
|
36
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
37
|
+
const pinned = db.prepare(`SELECT id FROM memories WHERE is_pinned = 1`).all();
|
|
38
|
+
pinned.forEach((row) => pinnedMemoryIds.add(row.id));
|
|
39
|
+
}
|
|
40
|
+
const promptCleanupResult = userPromptManager.deleteOldPrompts(cutoffTime);
|
|
41
|
+
const linkedMemoryIds = new Set(promptCleanupResult.linkedMemoryIds);
|
|
42
|
+
const protectedMemoryIds = new Set([...pinnedMemoryIds, ...linkedMemoryIds]);
|
|
33
43
|
let totalDeleted = 0;
|
|
34
44
|
let userDeleted = 0;
|
|
35
45
|
let projectDeleted = 0;
|
|
46
|
+
let linkedMemoriesDeleted = 0;
|
|
47
|
+
let pinnedSkipped = 0;
|
|
36
48
|
for (const shard of allShards) {
|
|
37
49
|
const db = connectionManager.getConnection(shard.dbPath);
|
|
38
50
|
const oldMemories = db
|
|
39
51
|
.prepare(`
|
|
40
|
-
SELECT id, container_tag FROM memories
|
|
41
|
-
WHERE updated_at < ?
|
|
52
|
+
SELECT id, container_tag, is_pinned FROM memories
|
|
53
|
+
WHERE updated_at < ?
|
|
42
54
|
`)
|
|
43
55
|
.all(cutoffTime);
|
|
44
|
-
if (oldMemories.length === 0)
|
|
45
|
-
continue;
|
|
46
56
|
for (const memory of oldMemories) {
|
|
47
57
|
try {
|
|
58
|
+
if (memory.is_pinned === 1) {
|
|
59
|
+
pinnedSkipped++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (protectedMemoryIds.has(memory.id)) {
|
|
63
|
+
if (linkedMemoryIds.has(memory.id)) {
|
|
64
|
+
log("Cleanup: skipped linked memory", { memoryId: memory.id });
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
48
68
|
vectorSearch.deleteVector(db, memory.id);
|
|
49
69
|
shardManager.decrementVectorCount(shard.id);
|
|
50
70
|
totalDeleted++;
|
|
@@ -60,16 +80,23 @@ export class CleanupService {
|
|
|
60
80
|
}
|
|
61
81
|
}
|
|
62
82
|
}
|
|
83
|
+
const promptsDeleted = promptCleanupResult.deleted - linkedMemoryIds.size;
|
|
63
84
|
log("Cleanup: completed", {
|
|
64
85
|
totalDeleted,
|
|
65
86
|
userDeleted,
|
|
66
87
|
projectDeleted,
|
|
88
|
+
promptsDeleted,
|
|
89
|
+
linkedMemoriesProtected: linkedMemoryIds.size,
|
|
90
|
+
pinnedMemoriesSkipped: pinnedSkipped,
|
|
67
91
|
cutoffTime: new Date(cutoffTime).toISOString(),
|
|
68
92
|
});
|
|
69
93
|
return {
|
|
70
94
|
deletedCount: totalDeleted,
|
|
71
95
|
userCount: userDeleted,
|
|
72
96
|
projectCount: projectDeleted,
|
|
97
|
+
promptsDeleted,
|
|
98
|
+
linkedMemoriesDeleted,
|
|
99
|
+
pinnedMemoriesSkipped: pinnedSkipped,
|
|
73
100
|
};
|
|
74
101
|
}
|
|
75
102
|
finally {
|
|
@@ -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;AACpD,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AA2CpE,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,aAAa,CAAkB;;YAIzB,UAAU;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/services/client.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AA2CpE,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,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,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;;;;;;;;;IAyC/C,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,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;;;;;;;;;IAqDG,YAAY,CAAC,QAAQ,EAAE,MAAM;;;;;;;IA2B7B,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,SAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDpD;AAED,eAAO,MAAM,YAAY,mBAA0B,CAAC"}
|
package/dist/services/client.js
CHANGED
|
@@ -51,7 +51,6 @@ export class LocalMemoryClient {
|
|
|
51
51
|
this.initPromise = (async () => {
|
|
52
52
|
try {
|
|
53
53
|
this.isInitialized = true;
|
|
54
|
-
log("SQLite memory client initialized");
|
|
55
54
|
}
|
|
56
55
|
catch (error) {
|
|
57
56
|
this.initPromise = null;
|
|
@@ -75,8 +74,10 @@ export class LocalMemoryClient {
|
|
|
75
74
|
ready: this.isInitialized && embeddingService.isWarmedUp,
|
|
76
75
|
};
|
|
77
76
|
}
|
|
77
|
+
close() {
|
|
78
|
+
connectionManager.closeAll();
|
|
79
|
+
}
|
|
78
80
|
async searchMemories(query, containerTag) {
|
|
79
|
-
log("searchMemories: start", { containerTag });
|
|
80
81
|
try {
|
|
81
82
|
await this.initialize();
|
|
82
83
|
const queryVector = await embeddingService.embedWithTimeout(query);
|
|
@@ -87,7 +88,6 @@ export class LocalMemoryClient {
|
|
|
87
88
|
return { success: true, results: [], total: 0, timing: 0 };
|
|
88
89
|
}
|
|
89
90
|
const results = await vectorSearch.searchAcrossShards(shards, queryVector, containerTag, CONFIG.maxMemories, CONFIG.similarityThreshold);
|
|
90
|
-
log("searchMemories: success", { count: results.length });
|
|
91
91
|
return { success: true, results, total: results.length, timing: 0 };
|
|
92
92
|
}
|
|
93
93
|
catch (error) {
|
|
@@ -97,7 +97,6 @@ export class LocalMemoryClient {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
async getProfile(containerTag, query) {
|
|
100
|
-
log("getProfile: start", { containerTag });
|
|
101
100
|
try {
|
|
102
101
|
await this.initialize();
|
|
103
102
|
const { scope, hash } = extractScopeFromContainerTag(containerTag);
|
|
@@ -124,7 +123,6 @@ export class LocalMemoryClient {
|
|
|
124
123
|
static: staticFacts.slice(0, CONFIG.maxProfileItems),
|
|
125
124
|
dynamic: dynamicFacts.slice(0, CONFIG.maxProfileItems),
|
|
126
125
|
};
|
|
127
|
-
log("getProfile: success", { hasProfile: true });
|
|
128
126
|
return { success: true, profile };
|
|
129
127
|
}
|
|
130
128
|
catch (error) {
|
|
@@ -134,7 +132,6 @@ export class LocalMemoryClient {
|
|
|
134
132
|
}
|
|
135
133
|
}
|
|
136
134
|
async addMemory(content, containerTag, metadata) {
|
|
137
|
-
log("addMemory: start", { containerTag, contentLength: content.length });
|
|
138
135
|
try {
|
|
139
136
|
await this.initialize();
|
|
140
137
|
const vector = await embeddingService.embedWithTimeout(content);
|
|
@@ -162,7 +159,6 @@ export class LocalMemoryClient {
|
|
|
162
159
|
const db = connectionManager.getConnection(shard.dbPath);
|
|
163
160
|
vectorSearch.insertVector(db, record);
|
|
164
161
|
shardManager.incrementVectorCount(shard.id);
|
|
165
|
-
log("addMemory: success", { id, shardId: shard.id });
|
|
166
162
|
return { success: true, id };
|
|
167
163
|
}
|
|
168
164
|
catch (error) {
|
|
@@ -172,7 +168,6 @@ export class LocalMemoryClient {
|
|
|
172
168
|
}
|
|
173
169
|
}
|
|
174
170
|
async deleteMemory(memoryId) {
|
|
175
|
-
log("deleteMemory: start", { memoryId });
|
|
176
171
|
try {
|
|
177
172
|
await this.initialize();
|
|
178
173
|
const { scope, hash } = extractScopeFromContainerTag(memoryId);
|
|
@@ -183,7 +178,6 @@ export class LocalMemoryClient {
|
|
|
183
178
|
if (memory) {
|
|
184
179
|
vectorSearch.deleteVector(db, memoryId);
|
|
185
180
|
shardManager.decrementVectorCount(shard.id);
|
|
186
|
-
log("deleteMemory: success", { memoryId, shardId: shard.id });
|
|
187
181
|
return { success: true };
|
|
188
182
|
}
|
|
189
183
|
}
|
|
@@ -197,7 +191,6 @@ export class LocalMemoryClient {
|
|
|
197
191
|
}
|
|
198
192
|
}
|
|
199
193
|
async listMemories(containerTag, limit = 20) {
|
|
200
|
-
log("listMemories: start", { containerTag, limit });
|
|
201
194
|
try {
|
|
202
195
|
await this.initialize();
|
|
203
196
|
const { scope, hash } = extractScopeFromContainerTag(containerTag);
|
|
@@ -229,7 +222,6 @@ export class LocalMemoryClient {
|
|
|
229
222
|
projectName: r.project_name,
|
|
230
223
|
gitRepoUrl: r.git_repo_url,
|
|
231
224
|
}));
|
|
232
|
-
log("listMemories: success", { count: memories.length });
|
|
233
225
|
return {
|
|
234
226
|
success: true,
|
|
235
227
|
memories,
|
|
@@ -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;AAKtC,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAoC;IAEvD,OAAO,CAAC,YAAY;IAUpB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;
|
|
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;AAKtC,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAoC;IAEvD,OAAO,CAAC,YAAY;IAUpB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAiBvC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IASrC,QAAQ,IAAI,IAAI;CAWjB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
|
|
@@ -23,21 +23,25 @@ export class ConnectionManager {
|
|
|
23
23
|
const db = new Database(dbPath);
|
|
24
24
|
this.initDatabase(db);
|
|
25
25
|
this.connections.set(dbPath, db);
|
|
26
|
-
log("SQLite connection opened", { path: dbPath });
|
|
27
26
|
return db;
|
|
28
27
|
}
|
|
29
28
|
closeConnection(dbPath) {
|
|
30
29
|
const db = this.connections.get(dbPath);
|
|
31
30
|
if (db) {
|
|
31
|
+
db.run("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
32
32
|
db.close();
|
|
33
33
|
this.connections.delete(dbPath);
|
|
34
|
-
log("SQLite connection closed", { path: dbPath });
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
closeAll() {
|
|
38
37
|
for (const [path, db] of this.connections) {
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
try {
|
|
39
|
+
db.run("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
40
|
+
db.close();
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
log("Error closing database", { path, error: String(error) });
|
|
44
|
+
}
|
|
41
45
|
}
|
|
42
46
|
this.connections.clear();
|
|
43
47
|
}
|
|
@@ -0,0 +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;AAQvD,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAyEf"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { memoryClient } from "./client.js";
|
|
2
|
+
import { getTags } from "./tags.js";
|
|
3
|
+
import { log } from "./logger.js";
|
|
4
|
+
import { CONFIG } from "../config.js";
|
|
5
|
+
import { userPromptManager } from "./user-prompt/user-prompt-manager.js";
|
|
6
|
+
export async function performUserMemoryLearning(ctx, directory) {
|
|
7
|
+
try {
|
|
8
|
+
const count = userPromptManager.countUnanalyzedForUserLearning();
|
|
9
|
+
const threshold = CONFIG.userMemoryAnalysisInterval;
|
|
10
|
+
if (count < threshold) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const prompts = userPromptManager.getPromptsForUserLearning(threshold);
|
|
14
|
+
if (prompts.length === 0) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const context = buildUserAnalysisContext(prompts);
|
|
18
|
+
const memories = await analyzeUserPatterns(ctx, context);
|
|
19
|
+
if (!memories || memories.length === 0) {
|
|
20
|
+
log("User memory learning: no patterns identified", { promptCount: prompts.length });
|
|
21
|
+
userPromptManager.markMultipleAsUserLearningCaptured(prompts.map((p) => p.id));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const tags = getTags(directory);
|
|
25
|
+
let savedCount = 0;
|
|
26
|
+
for (const memory of memories) {
|
|
27
|
+
const result = await memoryClient.addMemory(memory.summary, tags.user.tag, {
|
|
28
|
+
type: memory.type,
|
|
29
|
+
source: "user-learning",
|
|
30
|
+
promptCount: prompts.length,
|
|
31
|
+
analysisTimestamp: Date.now(),
|
|
32
|
+
reasoning: memory.reasoning,
|
|
33
|
+
displayName: tags.user.displayName,
|
|
34
|
+
userName: tags.user.userName,
|
|
35
|
+
userEmail: tags.user.userEmail,
|
|
36
|
+
});
|
|
37
|
+
if (result.success) {
|
|
38
|
+
savedCount++;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
userPromptManager.markMultipleAsUserLearningCaptured(prompts.map((p) => p.id));
|
|
42
|
+
if (savedCount > 0) {
|
|
43
|
+
await ctx.client?.tui
|
|
44
|
+
.showToast({
|
|
45
|
+
body: {
|
|
46
|
+
title: "User Memory Learning",
|
|
47
|
+
message: `Learned ${savedCount} pattern${savedCount > 1 ? "s" : ""} from ${prompts.length} prompts`,
|
|
48
|
+
variant: "success",
|
|
49
|
+
duration: 3000,
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
.catch(() => { });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
log("User memory learning error", { error: String(error) });
|
|
57
|
+
await ctx.client?.tui
|
|
58
|
+
.showToast({
|
|
59
|
+
body: {
|
|
60
|
+
title: "User Memory Learning Failed",
|
|
61
|
+
message: String(error),
|
|
62
|
+
variant: "error",
|
|
63
|
+
duration: 5000,
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
.catch(() => { });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function buildUserAnalysisContext(prompts) {
|
|
70
|
+
return `# User Prompt History Analysis
|
|
71
|
+
|
|
72
|
+
Analyze the following ${prompts.length} user prompts to identify patterns, preferences, and workflows.
|
|
73
|
+
|
|
74
|
+
## Prompts
|
|
75
|
+
|
|
76
|
+
${prompts.map((p, i) => `${i + 1}. ${p.content}`).join("\n\n")}
|
|
77
|
+
|
|
78
|
+
## Analysis Instructions
|
|
79
|
+
|
|
80
|
+
Identify user patterns and preferences from these prompts. Look for:
|
|
81
|
+
|
|
82
|
+
1. **Preferences**: How the user likes things done
|
|
83
|
+
- Code style preferences (e.g., "prefers code without comments")
|
|
84
|
+
- Communication preferences (e.g., "likes concise responses")
|
|
85
|
+
- Tool preferences (e.g., "prefers TypeScript over JavaScript")
|
|
86
|
+
|
|
87
|
+
2. **Patterns**: Recurring topics or requests
|
|
88
|
+
- Technical topics (e.g., "often asks about database optimization")
|
|
89
|
+
- Problem domains (e.g., "frequently works on authentication")
|
|
90
|
+
- Skill level indicators (e.g., "asks detailed questions about async patterns")
|
|
91
|
+
|
|
92
|
+
3. **Workflows**n sequences or habits
|
|
93
|
+
- Development flow (e.g., "usually asks for tests after implementation")
|
|
94
|
+
- Review habits (e.g., "always requests code review before committing")
|
|
95
|
+
- Learning style (e.g., "prefers examples over explanations")
|
|
96
|
+
|
|
97
|
+
Generate 1-5 key insights as memories. Only include patterns that are clearly evident from multiple prompts.`;
|
|
98
|
+
}
|
|
99
|
+
async function analyzeUserPatterns(ctx, context) {
|
|
100
|
+
if (!CONFIG.memoryModel || !CONFIG.memoryApiUrl || !CONFIG.memoryApiKey) {
|
|
101
|
+
throw new Error("External API not configured for user memory learning");
|
|
102
|
+
}
|
|
103
|
+
const { AIProviderFactory } = await import("./ai/ai-provider-factory.js");
|
|
104
|
+
const providerConfig = {
|
|
105
|
+
model: CONFIG.memoryModel,
|
|
106
|
+
apiUrl: CONFIG.memoryApiUrl,
|
|
107
|
+
apiKey: CONFIG.memoryApiKey,
|
|
108
|
+
maxIterations: CONFIG.autoCaptureMaxIterations,
|
|
109
|
+
iterationTimeout: CONFIG.autoCaptureIterationTimeout,
|
|
110
|
+
};
|
|
111
|
+
const provider = AIProviderFactory.createProvider(CONFIG.memoryProvider, providerConfig);
|
|
112
|
+
const systemPrompt = `You are a user behavior analyst for a coding assistant.
|
|
113
|
+
|
|
114
|
+
Your task is to analyze user prompts fy patterns, preferences, and workflows.
|
|
115
|
+
|
|
116
|
+
Use the save_user_memories tool to save identified patterns. Only save patterns that are clearly evident from the prompts.`;
|
|
117
|
+
const toolSchema = {
|
|
118
|
+
type: "function",
|
|
119
|
+
function: {
|
|
120
|
+
name: "save_user_memories",
|
|
121
|
+
description: "Save identified user patterns and preferences",
|
|
122
|
+
parameters: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
memories: {
|
|
126
|
+
type: "array",
|
|
127
|
+
description: "Array of identified patterns (1-5 memories)",
|
|
128
|
+
items: {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {
|
|
131
|
+
summary: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Clear description of the pattern or preference",
|
|
134
|
+
},
|
|
135
|
+
type: {
|
|
136
|
+
type: "string",
|
|
137
|
+
description: "Type of insight (e.g., preference, pattern, workflow, skill-level, communication-style)",
|
|
138
|
+
},
|
|
139
|
+
reasoning: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "Why this pattern is significant (optial)",
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
required: ["summary", "type"],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
required: ["memories"],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
const result = await provider.executeToolCall(systemPrompt, context, toolSchema, "user-learning");
|
|
153
|
+
if (!result.success || !result.data) {
|
|
154
|
+
throw new Error(result.error || "Failed to analyze user patterns");
|
|
155
|
+
}
|
|
156
|
+
return result.data.memories || [];
|
|
157
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface UserPrompt {
|
|
2
|
+
id: string;
|
|
3
|
+
sessionId: string;
|
|
4
|
+
messageId: string;
|
|
5
|
+
projectPath: string | null;
|
|
6
|
+
content: string;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
captured: boolean;
|
|
9
|
+
userLearningCaptured: boolean;
|
|
10
|
+
linkedMemoryId: string | null;
|
|
11
|
+
}
|
|
12
|
+
export declare class UserPromptManager {
|
|
13
|
+
private db;
|
|
14
|
+
private readonly dbPath;
|
|
15
|
+
constructor();
|
|
16
|
+
private initDatabase;
|
|
17
|
+
savePrompt(sessionId: string, messageId: string, projectPath: string, content: string): string;
|
|
18
|
+
getLastUncapturedPrompt(sessionId: string): UserPrompt | null;
|
|
19
|
+
deletePrompt(promptId: string): void;
|
|
20
|
+
markAsCaptured(promptId: string): void;
|
|
21
|
+
countUncapturedPrompts(): number;
|
|
22
|
+
getUncapturedPrompts(limit: number): UserPrompt[];
|
|
23
|
+
markMultipleAsCaptured(promptIds: string[]): void;
|
|
24
|
+
countUnanalyzedForUserLearning(): number;
|
|
25
|
+
getPromptsForUserLearning(limit: number): UserPrompt[];
|
|
26
|
+
markAsUserLearningCaptured(promptId: string): void;
|
|
27
|
+
markMultipleAsUserLearningCaptured(promptIds: string[]): void;
|
|
28
|
+
deleteOldPrompts(cutoffTime: number): {
|
|
29
|
+
deleted: number;
|
|
30
|
+
linkedMemoryIds: string[];
|
|
31
|
+
};
|
|
32
|
+
linkMemoryToPrompt(promptId: string, memoryId: string): void;
|
|
33
|
+
getPromptById(promptId: string): UserPrompt | null;
|
|
34
|
+
getCapturedPrompts(projectPath?: string): UserPrompt[];
|
|
35
|
+
private rowToPrompt;
|
|
36
|
+
}
|
|
37
|
+
export declare const userPromptManager: UserPromptManager;
|
|
38
|
+
//# sourceMappingURL=user-prompt-manager.d.ts.map
|
|
@@ -0,0 +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;IA+BpB,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,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,OAAO,CAAC,WAAW;CAapB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { connectionManager } from "../sqlite/connection-manager.js";
|
|
4
|
+
import { CONFIG } from "../../config.js";
|
|
5
|
+
const USER_PROMPTS_DB_NAME = "user-prompts.db";
|
|
6
|
+
export class UserPromptManager {
|
|
7
|
+
db;
|
|
8
|
+
dbPath;
|
|
9
|
+
constructor() {
|
|
10
|
+
this.dbPath = join(CONFIG.storagePath, USER_PROMPTS_DB_NAME);
|
|
11
|
+
this.db = connectionManager.getConnection(this.dbPath);
|
|
12
|
+
this.initDatabase();
|
|
13
|
+
}
|
|
14
|
+
initDatabase() {
|
|
15
|
+
this.db.run(`
|
|
16
|
+
CREATE TABLE IF NOT EXISTS user_prompts (
|
|
17
|
+
id TEXT PRIMARY KEY,
|
|
18
|
+
session_id TEXT NOT NULL,
|
|
19
|
+
message_id TEXT NOT NULL,
|
|
20
|
+
project_path TEXT,
|
|
21
|
+
content TEXT NOT NULL,
|
|
22
|
+
created_at INTEGER NOT NULL,
|
|
23
|
+
captured BOOLEAN DEFAULT 0,
|
|
24
|
+
user_learning_captured BOOLEAN DEFAULT 0,
|
|
25
|
+
linked_memory_id TEXT
|
|
26
|
+
)
|
|
27
|
+
`);
|
|
28
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_user_prompts_session ON user_prompts(session_id)");
|
|
29
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_user_prompts_captured ON user_prompts(captured)");
|
|
30
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_user_prompts_created ON user_prompts(created_at DESC)");
|
|
31
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_user_prompts_project ON user_prompts(project_path)");
|
|
32
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_user_prompts_linked ON user_prompts(linked_memory_id)");
|
|
33
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_user_prompts_user_learning ON user_prompts(user_learning_captured)");
|
|
34
|
+
}
|
|
35
|
+
savePrompt(sessionId, messageId, projectPath, content) {
|
|
36
|
+
const id = `prompt_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
const stmt = this.db.prepare(`
|
|
39
|
+
INSERT INTO user_prompts (id, session_id, message_id, project_path, content, created_at, captured)
|
|
40
|
+
VALUES (?, ?, ?, ?, ?, ?, 0)
|
|
41
|
+
`);
|
|
42
|
+
stmt.run(id, sessionId, messageId, projectPath, content, now);
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
getLastUncapturedPrompt(sessionId) {
|
|
46
|
+
const stmt = this.db.prepare(`
|
|
47
|
+
SELECT * FROM user_prompts
|
|
48
|
+
WHERE session_id = ? AND captured = 0
|
|
49
|
+
ORDER BY created_at DESC
|
|
50
|
+
LIMIT 1
|
|
51
|
+
`);
|
|
52
|
+
const row = stmt.get(sessionId);
|
|
53
|
+
if (!row)
|
|
54
|
+
return null;
|
|
55
|
+
return this.rowToPrompt(row);
|
|
56
|
+
}
|
|
57
|
+
deletePrompt(promptId) {
|
|
58
|
+
const stmt = this.db.prepare(`DELETE FROM user_prompts WHERE id = ?`);
|
|
59
|
+
stmt.run(promptId);
|
|
60
|
+
}
|
|
61
|
+
markAsCaptured(promptId) {
|
|
62
|
+
const stmt = this.db.prepare(`UPDATE user_prompts SET captured = 1 WHERE id = ?`);
|
|
63
|
+
stmt.run(promptId);
|
|
64
|
+
}
|
|
65
|
+
countUncapturedPrompts() {
|
|
66
|
+
const stmt = this.db.prepare(`SELECT COUNT(*) as count FROM user_prompts WHERE captured = 0`);
|
|
67
|
+
const row = stmt.get();
|
|
68
|
+
return row?.count || 0;
|
|
69
|
+
}
|
|
70
|
+
getUncapturedPrompts(limit) {
|
|
71
|
+
const stmt = this.db.prepare(`
|
|
72
|
+
SELECT * FROM user_prompts
|
|
73
|
+
WHERE captured = 0
|
|
74
|
+
ORDER BY created_at ASC
|
|
75
|
+
LIMIT ?
|
|
76
|
+
`);
|
|
77
|
+
const rows = stmt.all(limit);
|
|
78
|
+
return rows.map((row) => this.rowToPrompt(row));
|
|
79
|
+
}
|
|
80
|
+
markMultipleAsCaptured(promptIds) {
|
|
81
|
+
if (promptIds.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
const placeholders = promptIds.map(() => "?").join(",");
|
|
84
|
+
const stmt = this.db.prepare(`UPDATE user_prompts SET captured = 1 WHERE id IN (${placeholders})`);
|
|
85
|
+
stmt.run(...promptIds);
|
|
86
|
+
}
|
|
87
|
+
countUnanalyzedForUserLearning() {
|
|
88
|
+
const stmt = this.db.prepare(`SELECT COUNT(*) as count FROM user_prompts WHERE user_learning_captured = 0`);
|
|
89
|
+
const row = stmt.get();
|
|
90
|
+
return row?.count || 0;
|
|
91
|
+
}
|
|
92
|
+
getPromptsForUserLearning(limit) {
|
|
93
|
+
const stmt = this.db.prepare(`
|
|
94
|
+
SELECT * FROM user_prompts
|
|
95
|
+
WHERE user_learning_captured = 0
|
|
96
|
+
ORDER BY created_at ASC
|
|
97
|
+
LIMIT ?
|
|
98
|
+
`);
|
|
99
|
+
const rows = stmt.all(limit);
|
|
100
|
+
return rows.map((row) => this.rowToPrompt(row));
|
|
101
|
+
}
|
|
102
|
+
markAsUserLearningCaptured(promptId) {
|
|
103
|
+
const stmt = this.db.prepare(`UPDATE user_prompts SET user_learning_captured = 1 WHERE id = ?`);
|
|
104
|
+
stmt.run(promptId);
|
|
105
|
+
}
|
|
106
|
+
markMultipleAsUserLearningCaptured(promptIds) {
|
|
107
|
+
if (promptIds.length === 0)
|
|
108
|
+
return;
|
|
109
|
+
const placeholders = promptIds.map(() => "?").join(",");
|
|
110
|
+
const stmt = this.db.prepare(`UPDATE user_prompts SET user_learning_captured = 1 WHERE id IN (${placeholders})`);
|
|
111
|
+
stmt.run(...promptIds);
|
|
112
|
+
}
|
|
113
|
+
deleteOldPrompts(cutoffTime) {
|
|
114
|
+
const getLinkedStmt = this.db.prepare(`
|
|
115
|
+
SELECT linked_memory_id FROM user_prompts
|
|
116
|
+
WHERE created_at < ? AND linked_memory_id IS NOT NULL
|
|
117
|
+
`);
|
|
118
|
+
const linkedRows = getLinkedStmt.all(cutoffTime);
|
|
119
|
+
const linkedMemoryIds = linkedRows.map((row) => row.linked_memory_id).filter((id) => id);
|
|
120
|
+
const deleteStmt = this.db.prepare(`DELETE FROM user_prompts WHERE created_at < ?`);
|
|
121
|
+
const result = deleteStmt.run(cutoffTime);
|
|
122
|
+
return {
|
|
123
|
+
deleted: result.changes,
|
|
124
|
+
linkedMemoryIds,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
linkMemoryToPrompt(promptId, memoryId) {
|
|
128
|
+
const stmt = this.db.prepare(`UPDATE user_prompts SET linked_memory_id = ? WHERE id = ?`);
|
|
129
|
+
stmt.run(memoryId, promptId);
|
|
130
|
+
}
|
|
131
|
+
getPromptById(promptId) {
|
|
132
|
+
const stmt = this.db.prepare(`SELECT * FROM user_prompts WHERE id = ?`);
|
|
133
|
+
const row = stmt.get(promptId);
|
|
134
|
+
if (!row)
|
|
135
|
+
return null;
|
|
136
|
+
return this.rowToPrompt(row);
|
|
137
|
+
}
|
|
138
|
+
getCapturedPrompts(projectPath) {
|
|
139
|
+
let query = `SELECT * FROM user_prompts WHERE captured = 1`;
|
|
140
|
+
const params = [];
|
|
141
|
+
if (projectPath) {
|
|
142
|
+
query += ` AND project_path = ?`;
|
|
143
|
+
params.push(projectPath);
|
|
144
|
+
}
|
|
145
|
+
query += ` ORDER BY created_at DESC`;
|
|
146
|
+
const stmt = this.db.prepare(query);
|
|
147
|
+
const rows = stmt.all(...params);
|
|
148
|
+
return rows.map((row) => this.rowToPrompt(row));
|
|
149
|
+
}
|
|
150
|
+
rowToPrompt(row) {
|
|
151
|
+
return {
|
|
152
|
+
id: row.id,
|
|
153
|
+
sessionId: row.session_id,
|
|
154
|
+
messageId: row.message_id,
|
|
155
|
+
projectPath: row.project_path,
|
|
156
|
+
content: row.content,
|
|
157
|
+
createdAt: row.created_at,
|
|
158
|
+
captured: row.captured === 1,
|
|
159
|
+
userLearningCaptured: row.user_learning_captured === 1,
|
|
160
|
+
linkedMemoryId: row.linked_memory_id,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export const userPromptManager = new UserPromptManager();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join, dirname } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { handleListTags, handleListMemories, handleAddMemory, handleDeleteMemory, handleBulkDelete, handleUpdateMemory, handleSearch, handleStats, handlePinMemory, handleUnpinMemory, handleRunCleanup, handleRunDeduplication, handleDetectMigration, handleRunMigration, } from "./api-handlers.js";
|
|
4
|
+
import { handleListTags, handleListMemories, handleAddMemory, handleDeleteMemory, handleBulkDelete, handleUpdateMemory, handleSearch, handleStats, handlePinMemory, handleUnpinMemory, handleRunCleanup, handleRunDeduplication, handleDetectMigration, handleRunMigration, handleDeletePrompt, handleBulkDeletePrompts, } from "./api-handlers.js";
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = dirname(__filename);
|
|
7
7
|
let server = null;
|
|
@@ -30,7 +30,9 @@ async function handleRequest(req) {
|
|
|
30
30
|
const tag = url.searchParams.get("tag") || undefined;
|
|
31
31
|
const page = parseInt(url.searchParams.get("page") || "1");
|
|
32
32
|
const pageSize = parseInt(url.searchParams.get("pageSize") || "20");
|
|
33
|
-
const
|
|
33
|
+
const scope = url.searchParams.get("scope");
|
|
34
|
+
const includePrompts = url.searchParams.get("includePrompts") !== "false";
|
|
35
|
+
const result = await handleListMemories(tag, page, pageSize, scope, includePrompts);
|
|
34
36
|
return jsonResponse(result);
|
|
35
37
|
}
|
|
36
38
|
if (path === "/api/memories" && method === "POST") {
|
|
@@ -39,11 +41,13 @@ async function handleRequest(req) {
|
|
|
39
41
|
return jsonResponse(result);
|
|
40
42
|
}
|
|
41
43
|
if (path.startsWith("/api/memories/") && method === "DELETE") {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
+
const parts = path.split("/");
|
|
45
|
+
const id = parts[3];
|
|
46
|
+
if (!id || id === "bulk-delete") {
|
|
44
47
|
return jsonResponse({ success: false, error: "Invalid ID" });
|
|
45
48
|
}
|
|
46
|
-
const
|
|
49
|
+
const cascade = url.searchParams.get("cascade") === "true";
|
|
50
|
+
const result = await handleDeleteMemory(id, cascade);
|
|
47
51
|
return jsonResponse(result);
|
|
48
52
|
}
|
|
49
53
|
if (path.startsWith("/api/memories/") && method === "PUT") {
|
|
@@ -57,7 +61,8 @@ async function handleRequest(req) {
|
|
|
57
61
|
}
|
|
58
62
|
if (path === "/api/memories/bulk-delete" && method === "POST") {
|
|
59
63
|
const body = (await req.json());
|
|
60
|
-
const
|
|
64
|
+
const cascade = body.cascade !== false;
|
|
65
|
+
const result = await handleBulkDelete(body.ids || [], cascade);
|
|
61
66
|
return jsonResponse(result);
|
|
62
67
|
}
|
|
63
68
|
if (path === "/api/search" && method === "GET") {
|
|
@@ -112,6 +117,22 @@ async function handleRequest(req) {
|
|
|
112
117
|
const result = await handleRunMigration(strategy);
|
|
113
118
|
return jsonResponse(result);
|
|
114
119
|
}
|
|
120
|
+
if (path.startsWith("/api/prompts/") && method === "DELETE") {
|
|
121
|
+
const parts = path.split("/");
|
|
122
|
+
const id = parts[3];
|
|
123
|
+
if (!id || id === "bulk-delete") {
|
|
124
|
+
return jsonResponse({ success: false, error: "Invalid ID" });
|
|
125
|
+
}
|
|
126
|
+
const cascade = url.searchParams.get("cascade") === "true";
|
|
127
|
+
const result = await handleDeletePrompt(id, cascade);
|
|
128
|
+
return jsonResponse(result);
|
|
129
|
+
}
|
|
130
|
+
if (path === "/api/prompts/bulk-delete" && method === "POST") {
|
|
131
|
+
const body = (await req.json());
|
|
132
|
+
const cascade = body.cascade !== false;
|
|
133
|
+
const result = await handleBulkDeletePrompts(body.ids || [], cascade);
|
|
134
|
+
return jsonResponse(result);
|
|
135
|
+
}
|
|
115
136
|
return new Response("Not Found", { status: 404 });
|
|
116
137
|
}
|
|
117
138
|
catch (error) {
|
|
@@ -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;IA4Dd,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"}
|