opencode-mem 1.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 +588 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +258 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +618 -0
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +15 -0
- package/dist/services/api-handlers.d.ts +102 -0
- package/dist/services/api-handlers.d.ts.map +1 -0
- package/dist/services/api-handlers.js +494 -0
- package/dist/services/auto-capture.d.ts +32 -0
- package/dist/services/auto-capture.d.ts.map +1 -0
- package/dist/services/auto-capture.js +451 -0
- package/dist/services/cleanup-service.d.ts +20 -0
- package/dist/services/cleanup-service.d.ts.map +1 -0
- package/dist/services/cleanup-service.js +88 -0
- package/dist/services/client.d.ts +104 -0
- package/dist/services/client.d.ts.map +1 -0
- package/dist/services/client.js +251 -0
- package/dist/services/compaction.d.ts +92 -0
- package/dist/services/compaction.d.ts.map +1 -0
- package/dist/services/compaction.js +421 -0
- package/dist/services/context.d.ts +17 -0
- package/dist/services/context.d.ts.map +1 -0
- package/dist/services/context.js +41 -0
- package/dist/services/deduplication-service.d.ts +30 -0
- package/dist/services/deduplication-service.d.ts.map +1 -0
- package/dist/services/deduplication-service.js +131 -0
- package/dist/services/embedding.d.ts +10 -0
- package/dist/services/embedding.d.ts.map +1 -0
- package/dist/services/embedding.js +77 -0
- package/dist/services/jsonc.d.ts +7 -0
- package/dist/services/jsonc.d.ts.map +1 -0
- package/dist/services/jsonc.js +76 -0
- package/dist/services/logger.d.ts +2 -0
- package/dist/services/logger.d.ts.map +1 -0
- package/dist/services/logger.js +16 -0
- package/dist/services/migration-service.d.ts +42 -0
- package/dist/services/migration-service.d.ts.map +1 -0
- package/dist/services/migration-service.js +258 -0
- package/dist/services/privacy.d.ts +4 -0
- package/dist/services/privacy.d.ts.map +1 -0
- package/dist/services/privacy.js +10 -0
- package/dist/services/sqlite/connection-manager.d.ts +10 -0
- package/dist/services/sqlite/connection-manager.d.ts.map +1 -0
- package/dist/services/sqlite/connection-manager.js +45 -0
- package/dist/services/sqlite/shard-manager.d.ts +20 -0
- package/dist/services/sqlite/shard-manager.d.ts.map +1 -0
- package/dist/services/sqlite/shard-manager.js +221 -0
- package/dist/services/sqlite/types.d.ts +39 -0
- package/dist/services/sqlite/types.d.ts.map +1 -0
- package/dist/services/sqlite/types.js +1 -0
- package/dist/services/sqlite/vector-search.d.ts +18 -0
- package/dist/services/sqlite/vector-search.d.ts.map +1 -0
- package/dist/services/sqlite/vector-search.js +129 -0
- package/dist/services/sqlite-client.d.ts +116 -0
- package/dist/services/sqlite-client.d.ts.map +1 -0
- package/dist/services/sqlite-client.js +284 -0
- package/dist/services/tags.d.ts +20 -0
- package/dist/services/tags.d.ts.map +1 -0
- package/dist/services/tags.js +76 -0
- package/dist/services/web-server-lock.d.ts +12 -0
- package/dist/services/web-server-lock.d.ts.map +1 -0
- package/dist/services/web-server-lock.js +157 -0
- package/dist/services/web-server-worker.d.ts +2 -0
- package/dist/services/web-server-worker.d.ts.map +1 -0
- package/dist/services/web-server-worker.js +221 -0
- package/dist/services/web-server.d.ts +22 -0
- package/dist/services/web-server.d.ts.map +1 -0
- package/dist/services/web-server.js +134 -0
- package/dist/types/index.d.ts +48 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/web/app.d.ts +2 -0
- package/dist/web/app.d.ts.map +1 -0
- package/dist/web/app.js +691 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/favicon.svg +14 -0
- package/dist/web/index.html +202 -0
- package/dist/web/styles.css +851 -0
- package/package.json +52 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { MemoryType } from "../types/index.js";
|
|
2
|
+
interface ApiResponse<T = any> {
|
|
3
|
+
success: boolean;
|
|
4
|
+
data?: T;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
interface Memory {
|
|
8
|
+
id: string;
|
|
9
|
+
content: string;
|
|
10
|
+
type?: string;
|
|
11
|
+
scope: string;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
updatedAt?: string;
|
|
14
|
+
metadata?: Record<string, unknown>;
|
|
15
|
+
displayName?: string;
|
|
16
|
+
userName?: string;
|
|
17
|
+
userEmail?: string;
|
|
18
|
+
projectPath?: string;
|
|
19
|
+
projectName?: string;
|
|
20
|
+
gitRepoUrl?: string;
|
|
21
|
+
isPinned?: boolean;
|
|
22
|
+
}
|
|
23
|
+
interface TagInfo {
|
|
24
|
+
tag: string;
|
|
25
|
+
displayName?: string;
|
|
26
|
+
userName?: string;
|
|
27
|
+
userEmail?: string;
|
|
28
|
+
projectPath?: string;
|
|
29
|
+
projectName?: string;
|
|
30
|
+
gitRepoUrl?: string;
|
|
31
|
+
}
|
|
32
|
+
interface PaginatedResponse<T> {
|
|
33
|
+
items: T[];
|
|
34
|
+
total: number;
|
|
35
|
+
page: number;
|
|
36
|
+
pageSize: number;
|
|
37
|
+
totalPages: number;
|
|
38
|
+
}
|
|
39
|
+
export declare function handleListTags(): Promise<ApiResponse<{
|
|
40
|
+
user: TagInfo[];
|
|
41
|
+
project: TagInfo[];
|
|
42
|
+
}>>;
|
|
43
|
+
export declare function handleListMemories(tag?: string, page?: number, pageSize?: number): Promise<ApiResponse<PaginatedResponse<Memory>>>;
|
|
44
|
+
export declare function handleAddMemory(data: {
|
|
45
|
+
content: string;
|
|
46
|
+
containerTag: string;
|
|
47
|
+
type?: MemoryType;
|
|
48
|
+
displayName?: string;
|
|
49
|
+
userName?: string;
|
|
50
|
+
userEmail?: string;
|
|
51
|
+
projectPath?: string;
|
|
52
|
+
projectName?: string;
|
|
53
|
+
gitRepoUrl?: string;
|
|
54
|
+
}): Promise<ApiResponse<{
|
|
55
|
+
id: string;
|
|
56
|
+
}>>;
|
|
57
|
+
export declare function handleDeleteMemory(id: string): Promise<ApiResponse<void>>;
|
|
58
|
+
export declare function handleBulkDelete(ids: string[]): Promise<ApiResponse<{
|
|
59
|
+
deleted: number;
|
|
60
|
+
}>>;
|
|
61
|
+
export declare function handleUpdateMemory(id: string, data: {
|
|
62
|
+
content?: string;
|
|
63
|
+
type?: MemoryType;
|
|
64
|
+
}): Promise<ApiResponse<void>>;
|
|
65
|
+
export declare function handleSearch(query: string, tag?: string, page?: number, pageSize?: number): Promise<ApiResponse<PaginatedResponse<Memory & {
|
|
66
|
+
similarity: number;
|
|
67
|
+
}>>>;
|
|
68
|
+
export declare function handleStats(): Promise<ApiResponse<{
|
|
69
|
+
total: number;
|
|
70
|
+
byScope: {
|
|
71
|
+
user: number;
|
|
72
|
+
project: number;
|
|
73
|
+
};
|
|
74
|
+
byType: Record<string, number>;
|
|
75
|
+
}>>;
|
|
76
|
+
export declare function handlePinMemory(id: string): Promise<ApiResponse<void>>;
|
|
77
|
+
export declare function handleUnpinMemory(id: string): Promise<ApiResponse<void>>;
|
|
78
|
+
export declare function handleRunCleanup(): Promise<ApiResponse<{
|
|
79
|
+
deletedCount: number;
|
|
80
|
+
userCount: number;
|
|
81
|
+
projectCount: number;
|
|
82
|
+
}>>;
|
|
83
|
+
export declare function handleRunDeduplication(): Promise<ApiResponse<{
|
|
84
|
+
exactDuplicatesDeleted: number;
|
|
85
|
+
nearDuplicateGroups: any[];
|
|
86
|
+
}>>;
|
|
87
|
+
export declare function handleDetectMigration(): Promise<ApiResponse<{
|
|
88
|
+
needsMigration: boolean;
|
|
89
|
+
configDimensions: number;
|
|
90
|
+
configModel: string;
|
|
91
|
+
shardMismatches: any[];
|
|
92
|
+
}>>;
|
|
93
|
+
export declare function handleRunMigration(strategy: "fresh-start" | "re-embed"): Promise<ApiResponse<{
|
|
94
|
+
success: boolean;
|
|
95
|
+
strategy: string;
|
|
96
|
+
deletedShards: number;
|
|
97
|
+
reEmbeddedMemories: number;
|
|
98
|
+
duration: number;
|
|
99
|
+
error?: string;
|
|
100
|
+
}>>;
|
|
101
|
+
export {};
|
|
102
|
+
//# sourceMappingURL=api-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-handlers.d.ts","sourceRoot":"","sources":["../../src/services/api-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import { embeddingService } from "./embedding.js";
|
|
2
|
+
import { shardManager } from "./sqlite/shard-manager.js";
|
|
3
|
+
import { vectorSearch } from "./sqlite/vector-search.js";
|
|
4
|
+
import { connectionManager } from "./sqlite/connection-manager.js";
|
|
5
|
+
import { log } from "./logger.js";
|
|
6
|
+
function safeToISOString(timestamp) {
|
|
7
|
+
try {
|
|
8
|
+
if (timestamp === null || timestamp === undefined) {
|
|
9
|
+
return new Date().toISOString();
|
|
10
|
+
}
|
|
11
|
+
const numValue = typeof timestamp === "bigint" ? Number(timestamp) : Number(timestamp);
|
|
12
|
+
if (isNaN(numValue) || numValue < 0) {
|
|
13
|
+
return new Date().toISOString();
|
|
14
|
+
}
|
|
15
|
+
return new Date(numValue).toISOString();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return new Date().toISOString();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function safeJSONParse(jsonString) {
|
|
22
|
+
if (!jsonString || typeof jsonString !== "string") {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(jsonString);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function extractScopeFromTag(tag) {
|
|
33
|
+
const parts = tag.split("_");
|
|
34
|
+
if (parts.length >= 3) {
|
|
35
|
+
const scope = parts[1];
|
|
36
|
+
const hash = parts.slice(2).join("_");
|
|
37
|
+
return { scope, hash };
|
|
38
|
+
}
|
|
39
|
+
return { scope: "user", hash: tag };
|
|
40
|
+
}
|
|
41
|
+
export async function handleListTags() {
|
|
42
|
+
try {
|
|
43
|
+
await embeddingService.warmup();
|
|
44
|
+
const userShards = shardManager.getAllShards("user", "");
|
|
45
|
+
const projectShards = shardManager.getAllShards("project", "");
|
|
46
|
+
const allShards = [...userShards, ...projectShards];
|
|
47
|
+
const tagsMap = new Map();
|
|
48
|
+
for (const shard of allShards) {
|
|
49
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
50
|
+
const tags = vectorSearch.getDistinctTags(db);
|
|
51
|
+
for (const t of tags) {
|
|
52
|
+
if (t.container_tag && !tagsMap.has(t.container_tag)) {
|
|
53
|
+
tagsMap.set(t.container_tag, {
|
|
54
|
+
tag: t.container_tag,
|
|
55
|
+
displayName: t.display_name,
|
|
56
|
+
userName: t.user_name,
|
|
57
|
+
userEmail: t.user_email,
|
|
58
|
+
projectPath: t.project_path,
|
|
59
|
+
projectName: t.project_name,
|
|
60
|
+
gitRepoUrl: t.git_repo_url,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const userTags = [];
|
|
66
|
+
const projectTags = [];
|
|
67
|
+
for (const tagInfo of tagsMap.values()) {
|
|
68
|
+
if (tagInfo.tag.includes("_user_")) {
|
|
69
|
+
userTags.push(tagInfo);
|
|
70
|
+
}
|
|
71
|
+
else if (tagInfo.tag.includes("_project_")) {
|
|
72
|
+
projectTags.push(tagInfo);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
data: { user: userTags, project: projectTags },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
log("handleListTags: error", { error: String(error) });
|
|
82
|
+
return { success: false, error: String(error) };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export async function handleListMemories(tag, page = 1, pageSize = 20) {
|
|
86
|
+
try {
|
|
87
|
+
await embeddingService.warmup();
|
|
88
|
+
let allResults = [];
|
|
89
|
+
if (tag) {
|
|
90
|
+
const { scope, hash } = extractScopeFromTag(tag);
|
|
91
|
+
const shards = shardManager.getAllShards(scope, hash);
|
|
92
|
+
for (const shard of shards) {
|
|
93
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
94
|
+
const memories = vectorSearch.listMemories(db, tag, 10000);
|
|
95
|
+
allResults.push(...memories);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const userShards = shardManager.getAllShards("user", "");
|
|
100
|
+
const projectShards = shardManager.getAllShards("project", "");
|
|
101
|
+
const allShards = [...userShards, ...projectShards];
|
|
102
|
+
for (const shard of allShards) {
|
|
103
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
104
|
+
const memories = vectorSearch.getAllMemories(db);
|
|
105
|
+
allResults.push(...memories);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const sortedResults = allResults.sort((a, b) => Number(b.created_at) - Number(a.created_at));
|
|
109
|
+
const total = sortedResults.length;
|
|
110
|
+
const totalPages = Math.ceil(total / pageSize);
|
|
111
|
+
const offset = (page - 1) * pageSize;
|
|
112
|
+
const paginatedResults = sortedResults.slice(offset, offset + pageSize);
|
|
113
|
+
const memories = paginatedResults.map((r) => ({
|
|
114
|
+
id: r.id,
|
|
115
|
+
content: r.content,
|
|
116
|
+
type: r.type,
|
|
117
|
+
scope: r.container_tag?.includes("_user_") ? "user" : "project",
|
|
118
|
+
createdAt: safeToISOString(r.created_at),
|
|
119
|
+
updatedAt: r.updated_at ? safeToISOString(r.updated_at) : undefined,
|
|
120
|
+
metadata: safeJSONParse(r.metadata),
|
|
121
|
+
displayName: r.display_name,
|
|
122
|
+
userName: r.user_name,
|
|
123
|
+
userEmail: r.user_email,
|
|
124
|
+
projectPath: r.project_path,
|
|
125
|
+
projectName: r.project_name,
|
|
126
|
+
gitRepoUrl: r.git_repo_url,
|
|
127
|
+
isPinned: r.is_pinned === 1,
|
|
128
|
+
}));
|
|
129
|
+
return {
|
|
130
|
+
success: true,
|
|
131
|
+
data: {
|
|
132
|
+
items: memories,
|
|
133
|
+
total,
|
|
134
|
+
page,
|
|
135
|
+
pageSize,
|
|
136
|
+
totalPages,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
log("handleListMemories: error", { error: String(error) });
|
|
142
|
+
return { success: false, error: String(error) };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export async function handleAddMemory(data) {
|
|
146
|
+
try {
|
|
147
|
+
if (!data.content || !data.containerTag) {
|
|
148
|
+
return { success: false, error: "content and containerTag are required" };
|
|
149
|
+
}
|
|
150
|
+
await embeddingService.warmup();
|
|
151
|
+
const vector = await embeddingService.embedWithTimeout(data.content);
|
|
152
|
+
const { scope, hash } = extractScopeFromTag(data.containerTag);
|
|
153
|
+
const shard = shardManager.getWriteShard(scope, hash);
|
|
154
|
+
const id = `mem_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
155
|
+
const now = Date.now();
|
|
156
|
+
const record = {
|
|
157
|
+
id,
|
|
158
|
+
content: data.content,
|
|
159
|
+
vector,
|
|
160
|
+
containerTag: data.containerTag,
|
|
161
|
+
type: data.type,
|
|
162
|
+
createdAt: now,
|
|
163
|
+
updatedAt: now,
|
|
164
|
+
displayName: data.displayName,
|
|
165
|
+
userName: data.userName,
|
|
166
|
+
userEmail: data.userEmail,
|
|
167
|
+
projectPath: data.projectPath,
|
|
168
|
+
projectName: data.projectName,
|
|
169
|
+
gitRepoUrl: data.gitRepoUrl,
|
|
170
|
+
metadata: JSON.stringify({
|
|
171
|
+
source: "api",
|
|
172
|
+
}),
|
|
173
|
+
};
|
|
174
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
175
|
+
vectorSearch.insertVector(db, record);
|
|
176
|
+
shardManager.incrementVectorCount(shard.id);
|
|
177
|
+
return { success: true, data: { id } };
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
log("handleAddMemory: error", { error: String(error) });
|
|
181
|
+
return { success: false, error: String(error) };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
export async function handleDeleteMemory(id) {
|
|
185
|
+
try {
|
|
186
|
+
if (!id) {
|
|
187
|
+
return { success: false, error: "id is required" };
|
|
188
|
+
}
|
|
189
|
+
const userShards = shardManager.getAllShards("user", "");
|
|
190
|
+
const projectShards = shardManager.getAllShards("project", "");
|
|
191
|
+
const allShards = [...userShards, ...projectShards];
|
|
192
|
+
for (const shard of allShards) {
|
|
193
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
194
|
+
const memory = vectorSearch.getMemoryById(db, id);
|
|
195
|
+
if (memory) {
|
|
196
|
+
vectorSearch.deleteVector(db, id);
|
|
197
|
+
shardManager.decrementVectorCount(shard.id);
|
|
198
|
+
return { success: true };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { success: false, error: "Memory not found" };
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
log("handleDeleteMemory: error", { error: String(error) });
|
|
205
|
+
return { success: false, error: String(error) };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export async function handleBulkDelete(ids) {
|
|
209
|
+
try {
|
|
210
|
+
if (!ids || ids.length === 0) {
|
|
211
|
+
return { success: false, error: "ids array is required" };
|
|
212
|
+
}
|
|
213
|
+
let deleted = 0;
|
|
214
|
+
for (const id of ids) {
|
|
215
|
+
const result = await handleDeleteMemory(id);
|
|
216
|
+
if (result.success) {
|
|
217
|
+
deleted++;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return { success: true, data: { deleted } };
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
log("handleBulkDelete: error", { error: String(error) });
|
|
224
|
+
return { success: false, error: String(error) };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
export async function handleUpdateMemory(id, data) {
|
|
228
|
+
try {
|
|
229
|
+
if (!id) {
|
|
230
|
+
return { success: false, error: "id is required" };
|
|
231
|
+
}
|
|
232
|
+
await embeddingService.warmup();
|
|
233
|
+
const userShards = shardManager.getAllShards("user", "");
|
|
234
|
+
const projectShards = shardManager.getAllShards("project", "");
|
|
235
|
+
const allShards = [...userShards, ...projectShards];
|
|
236
|
+
let foundShard = null;
|
|
237
|
+
let existingMemory = null;
|
|
238
|
+
for (const shard of allShards) {
|
|
239
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
240
|
+
const memory = vectorSearch.getMemoryById(db, id);
|
|
241
|
+
if (memory) {
|
|
242
|
+
foundShard = shard;
|
|
243
|
+
existingMemory = memory;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!foundShard || !existingMemory) {
|
|
248
|
+
return { success: false, error: "Memory not found" };
|
|
249
|
+
}
|
|
250
|
+
const db = connectionManager.getConnection(foundShard.dbPath);
|
|
251
|
+
vectorSearch.deleteVector(db, id);
|
|
252
|
+
shardManager.decrementVectorCount(foundShard.id);
|
|
253
|
+
const newContent = data.content || existingMemory.content;
|
|
254
|
+
const vector = await embeddingService.embedWithTimeout(newContent);
|
|
255
|
+
const updatedRecord = {
|
|
256
|
+
id,
|
|
257
|
+
content: newContent,
|
|
258
|
+
vector,
|
|
259
|
+
containerTag: existingMemory.container_tag,
|
|
260
|
+
type: data.type || existingMemory.type,
|
|
261
|
+
createdAt: existingMemory.created_at,
|
|
262
|
+
updatedAt: Date.now(),
|
|
263
|
+
metadata: existingMemory.metadata,
|
|
264
|
+
displayName: existingMemory.display_name,
|
|
265
|
+
userName: existingMemory.user_name,
|
|
266
|
+
userEmail: existingMemory.user_email,
|
|
267
|
+
projectPath: existingMemory.project_path,
|
|
268
|
+
projectName: existingMemory.project_name,
|
|
269
|
+
gitRepoUrl: existingMemory.git_repo_url,
|
|
270
|
+
};
|
|
271
|
+
vectorSearch.insertVector(db, updatedRecord);
|
|
272
|
+
shardManager.incrementVectorCount(foundShard.id);
|
|
273
|
+
return { success: true };
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
log("handleUpdateMemory: error", { error: String(error) });
|
|
277
|
+
return { success: false, error: String(error) };
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
export async function handleSearch(query, tag, page = 1, pageSize = 20) {
|
|
281
|
+
try {
|
|
282
|
+
if (!query) {
|
|
283
|
+
return { success: false, error: "query is required" };
|
|
284
|
+
}
|
|
285
|
+
await embeddingService.warmup();
|
|
286
|
+
const queryVector = await embeddingService.embedWithTimeout(query);
|
|
287
|
+
let allResults = [];
|
|
288
|
+
if (tag) {
|
|
289
|
+
const { scope, hash } = extractScopeFromTag(tag);
|
|
290
|
+
const shards = shardManager.getAllShards(scope, hash);
|
|
291
|
+
for (const shard of shards) {
|
|
292
|
+
try {
|
|
293
|
+
const results = vectorSearch.searchInShard(shard, queryVector, tag, pageSize * 2);
|
|
294
|
+
allResults.push(...results);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
log("Shard search error", { shardId: shard.id, error: String(error) });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
const userShards = shardManager.getAllShards("user", "");
|
|
303
|
+
const projectShards = shardManager.getAllShards("project", "");
|
|
304
|
+
const allShards = [...userShards, ...projectShards];
|
|
305
|
+
const uniqueTags = new Set();
|
|
306
|
+
for (const shard of allShards) {
|
|
307
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
308
|
+
const tags = vectorSearch.getDistinctTags(db);
|
|
309
|
+
for (const t of tags) {
|
|
310
|
+
if (t.container_tag) {
|
|
311
|
+
uniqueTags.add(t.container_tag);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
for (const containerTag of uniqueTags) {
|
|
316
|
+
const { scope, hash } = extractScopeFromTag(containerTag);
|
|
317
|
+
const shards = shardManager.getAllShards(scope, hash);
|
|
318
|
+
for (const shard of shards) {
|
|
319
|
+
try {
|
|
320
|
+
const results = vectorSearch.searchInShard(shard, queryVector, containerTag, pageSize);
|
|
321
|
+
allResults.push(...results);
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
log("Shard search error", { shardId: shard.id, error: String(error) });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const sortedResults = allResults.sort((a, b) => b.similarity - a.similarity);
|
|
330
|
+
const total = sortedResults.length;
|
|
331
|
+
const totalPages = Math.ceil(total / pageSize);
|
|
332
|
+
const offset = (page - 1) * pageSize;
|
|
333
|
+
const paginatedResults = sortedResults.slice(offset, offset + pageSize);
|
|
334
|
+
const memories = paginatedResults.map((r) => ({
|
|
335
|
+
id: r.id,
|
|
336
|
+
content: r.memory,
|
|
337
|
+
type: r.metadata?.type,
|
|
338
|
+
scope: r.containerTag?.includes("_user_") ? "user" : "project",
|
|
339
|
+
createdAt: safeToISOString(r.metadata?.createdAt),
|
|
340
|
+
updatedAt: r.metadata?.updatedAt ? safeToISOString(r.metadata.updatedAt) : undefined,
|
|
341
|
+
similarity: Math.round(r.similarity * 100),
|
|
342
|
+
metadata: r.metadata,
|
|
343
|
+
displayName: r.displayName,
|
|
344
|
+
userName: r.userName,
|
|
345
|
+
userEmail: r.userEmail,
|
|
346
|
+
projectPath: r.projectPath,
|
|
347
|
+
projectName: r.projectName,
|
|
348
|
+
gitRepoUrl: r.gitRepoUrl,
|
|
349
|
+
isPinned: r.isPinned === 1,
|
|
350
|
+
}));
|
|
351
|
+
return {
|
|
352
|
+
success: true,
|
|
353
|
+
data: {
|
|
354
|
+
items: memories,
|
|
355
|
+
total,
|
|
356
|
+
page,
|
|
357
|
+
pageSize,
|
|
358
|
+
totalPages,
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
log("handleSearch: error", { error: String(error) });
|
|
364
|
+
return { success: false, error: String(error) };
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
export async function handleStats() {
|
|
368
|
+
try {
|
|
369
|
+
await embeddingService.warmup();
|
|
370
|
+
const userShards = shardManager.getAllShards("user", "");
|
|
371
|
+
const projectShards = shardManager.getAllShards("project", "");
|
|
372
|
+
const allShards = [...userShards, ...projectShards];
|
|
373
|
+
let userCount = 0;
|
|
374
|
+
let projectCount = 0;
|
|
375
|
+
const typeCount = {};
|
|
376
|
+
for (const shard of allShards) {
|
|
377
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
378
|
+
const memories = vectorSearch.getAllMemories(db);
|
|
379
|
+
for (const r of memories) {
|
|
380
|
+
if (r.container_tag?.includes("_user_")) {
|
|
381
|
+
userCount++;
|
|
382
|
+
}
|
|
383
|
+
else if (r.container_tag?.includes("_project_")) {
|
|
384
|
+
projectCount++;
|
|
385
|
+
}
|
|
386
|
+
if (r.type) {
|
|
387
|
+
typeCount[r.type] = (typeCount[r.type] || 0) + 1;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
success: true,
|
|
393
|
+
data: {
|
|
394
|
+
total: userCount + projectCount,
|
|
395
|
+
byScope: { user: userCount, project: projectCount },
|
|
396
|
+
byType: typeCount,
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
log("handleStats: error", { error: String(error) });
|
|
402
|
+
return { success: false, error: String(error) };
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
export async function handlePinMemory(id) {
|
|
406
|
+
try {
|
|
407
|
+
if (!id) {
|
|
408
|
+
return { success: false, error: "id is required" };
|
|
409
|
+
}
|
|
410
|
+
const userShards = shardManager.getAllShards("user", "");
|
|
411
|
+
const projectShards = shardManager.getAllShards("project", "");
|
|
412
|
+
const allShards = [...userShards, ...projectShards];
|
|
413
|
+
for (const shard of allShards) {
|
|
414
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
415
|
+
const memory = vectorSearch.getMemoryById(db, id);
|
|
416
|
+
if (memory) {
|
|
417
|
+
vectorSearch.pinMemory(db, id);
|
|
418
|
+
return { success: true };
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return { success: false, error: "Memory not found" };
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
log("handlePinMemory: error", { error: String(error) });
|
|
425
|
+
return { success: false, error: String(error) };
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
export async function handleUnpinMemory(id) {
|
|
429
|
+
try {
|
|
430
|
+
if (!id) {
|
|
431
|
+
return { success: false, error: "id is required" };
|
|
432
|
+
}
|
|
433
|
+
const userShards = shardManager.getAllShards("user", "");
|
|
434
|
+
const projectShards = shardManager.getAllShards("project", "");
|
|
435
|
+
const allShards = [...userShards, ...projectShards];
|
|
436
|
+
for (const shard of allShards) {
|
|
437
|
+
const db = connectionManager.getConnection(shard.dbPath);
|
|
438
|
+
const memory = vectorSearch.getMemoryById(db, id);
|
|
439
|
+
if (memory) {
|
|
440
|
+
vectorSearch.unpinMemory(db, id);
|
|
441
|
+
return { success: true };
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return { success: false, error: "Memory not found" };
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
log("handleUnpinMemory: error", { error: String(error) });
|
|
448
|
+
return { success: false, error: String(error) };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
export async function handleRunCleanup() {
|
|
452
|
+
try {
|
|
453
|
+
const { cleanupService } = await import("./cleanup-service.js");
|
|
454
|
+
const result = await cleanupService.runCleanup();
|
|
455
|
+
return { success: true, data: result };
|
|
456
|
+
}
|
|
457
|
+
catch (error) {
|
|
458
|
+
log("handleRunCleanup: error", { error: String(error) });
|
|
459
|
+
return { success: false, error: String(error) };
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
export async function handleRunDeduplication() {
|
|
463
|
+
try {
|
|
464
|
+
const { deduplicationService } = await import("./deduplication-service.js");
|
|
465
|
+
const result = await deduplicationService.detectAndRemoveDuplicates();
|
|
466
|
+
return { success: true, data: result };
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
log("handleRunDeduplication: error", { error: String(error) });
|
|
470
|
+
return { success: false, error: String(error) };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
export async function handleDetectMigration() {
|
|
474
|
+
try {
|
|
475
|
+
const { migrationService } = await import("./migration-service.js");
|
|
476
|
+
const result = await migrationService.detectDimensionMismatch();
|
|
477
|
+
return { success: true, data: result };
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
log("handleDetectMigration: error", { error: String(error) });
|
|
481
|
+
return { success: false, error: String(error) };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
export async function handleRunMigration(strategy) {
|
|
485
|
+
try {
|
|
486
|
+
const { migrationService } = await import("./migration-service.js");
|
|
487
|
+
const result = await migrationService.migrateToNewModel(strategy);
|
|
488
|
+
return { success: result.success, data: result };
|
|
489
|
+
}
|
|
490
|
+
catch (error) {
|
|
491
|
+
log("handleRunMigration: error", { error: String(error) });
|
|
492
|
+
return { success: false, error: String(error) };
|
|
493
|
+
}
|
|
494
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
interface CaptureBuffer {
|
|
3
|
+
sessionID: string;
|
|
4
|
+
lastCaptureTokens: number;
|
|
5
|
+
lastCaptureTime: number;
|
|
6
|
+
lastCapturedMessageIndex: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class AutoCaptureService {
|
|
9
|
+
private buffers;
|
|
10
|
+
private capturing;
|
|
11
|
+
private tokenThreshold;
|
|
12
|
+
private minTokens;
|
|
13
|
+
private enabled;
|
|
14
|
+
private maxMemories;
|
|
15
|
+
constructor();
|
|
16
|
+
isEnabled(): boolean;
|
|
17
|
+
getDisabledReason(): string | null;
|
|
18
|
+
toggle(): boolean;
|
|
19
|
+
getOrCreateBuffer(sessionID: string): CaptureBuffer;
|
|
20
|
+
checkTokenThreshold(sessionID: string, totalTokens: number): boolean;
|
|
21
|
+
getSystemPrompt(hasContext: boolean): string;
|
|
22
|
+
markCapturing(sessionID: string): void;
|
|
23
|
+
clearBuffer(sessionID: string): void;
|
|
24
|
+
getStats(sessionID: string): {
|
|
25
|
+
lastCaptureTokens: number;
|
|
26
|
+
timeSinceCapture: number;
|
|
27
|
+
} | null;
|
|
28
|
+
cleanup(sessionID: string): void;
|
|
29
|
+
}
|
|
30
|
+
export declare function performAutoCapture(ctx: PluginInput, service: AutoCaptureService, sessionID: string, directory: string): Promise<void>;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=auto-capture.d.ts.map
|
|
@@ -0,0 +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;AAOvD,UAAU,aAAa;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,wBAAwB,EAAE,MAAM,CAAC;CAClC;AA4BD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,WAAW,CAAS;;IAwB5B,SAAS,IAAI,OAAO;IAIpB,iBAAiB,IAAI,MAAM,GAAG,IAAI;IAQlC,MAAM,IAAI,OAAO;IAKjB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa;IAYnD,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO;IAkBpE,eAAe,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM;IA2C5C,aAAa,CAAC,SAAS,EAAE,MAAM;IAI/B,WAAW,CAAC,SAAS,EAAE,MAAM;IAa7B,QAAQ,CAAC,SAAS,EAAE,MAAM;;;;IAU1B,OAAO,CAAC,SAAS,EAAE,MAAM;CAI1B;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAoNf"}
|