@memtensor/memos-local-openclaw-plugin 1.0.5 → 1.0.6-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +24 -0
- package/dist/capture/index.js.map +1 -1
- package/dist/client/connector.d.ts.map +1 -1
- package/dist/client/connector.js +33 -5
- package/dist/client/connector.js.map +1 -1
- package/dist/client/hub.d.ts.map +1 -1
- package/dist/client/hub.js +4 -0
- package/dist/client/hub.js.map +1 -1
- package/dist/hub/server.d.ts +2 -0
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +116 -54
- package/dist/hub/server.js.map +1 -1
- package/dist/ingest/providers/index.d.ts +4 -0
- package/dist/ingest/providers/index.d.ts.map +1 -1
- package/dist/ingest/providers/index.js +32 -86
- package/dist/ingest/providers/index.js.map +1 -1
- package/dist/ingest/providers/openai.d.ts.map +1 -1
- package/dist/ingest/providers/openai.js +29 -13
- package/dist/ingest/providers/openai.js.map +1 -1
- package/dist/recall/engine.d.ts.map +1 -1
- package/dist/recall/engine.js +33 -32
- package/dist/recall/engine.js.map +1 -1
- package/dist/storage/sqlite.d.ts +43 -7
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +179 -58
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/tools/memory-get.d.ts.map +1 -1
- package/dist/tools/memory-get.js +4 -1
- package/dist/tools/memory-get.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +71 -21
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +24 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +398 -144
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +86 -34
- package/package.json +1 -1
- package/scripts/postinstall.cjs +21 -5
- package/src/capture/index.ts +36 -0
- package/src/client/connector.ts +32 -5
- package/src/client/hub.ts +4 -0
- package/src/hub/server.ts +110 -50
- package/src/ingest/providers/index.ts +37 -92
- package/src/ingest/providers/openai.ts +31 -13
- package/src/recall/engine.ts +32 -30
- package/src/storage/sqlite.ts +196 -63
- package/src/tools/memory-get.ts +4 -1
- package/src/types.ts +2 -0
- package/src/viewer/html.ts +71 -21
- package/src/viewer/server.ts +387 -139
- package/prebuilds/darwin-arm64/better_sqlite3.node +0 -0
- package/prebuilds/darwin-x64/better_sqlite3.node +0 -0
- package/prebuilds/linux-x64/better_sqlite3.node +0 -0
- package/prebuilds/win32-x64/better_sqlite3.node +0 -0
- package/telemetry.credentials.json +0 -5
package/src/recall/engine.ts
CHANGED
|
@@ -62,10 +62,20 @@ export class RecallEngine {
|
|
|
62
62
|
|
|
63
63
|
// Step 1b: Pattern search (LIKE-based) as fallback for short terms that
|
|
64
64
|
// trigram FTS cannot match (trigram requires >= 3 chars).
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
// For CJK text without spaces, extract bigrams (2-char sliding windows)
|
|
66
|
+
// so that queries like "唐波是谁" produce ["唐波", "波是", "是谁"].
|
|
67
|
+
const cleaned = query.replace(/[."""(){}[\]*:^~!@#$%&\\/<>,;'`??。,!、:""''()【】《》]/g, " ");
|
|
68
|
+
const spaceSplit = cleaned.split(/\s+/).filter((t) => t.length === 2);
|
|
69
|
+
const cjkBigrams: string[] = [];
|
|
70
|
+
const cjkRuns = cleaned.match(/[\u4e00-\u9fff\u3400-\u4dbf\uF900-\uFAFF]{2,}/g);
|
|
71
|
+
if (cjkRuns) {
|
|
72
|
+
for (const run of cjkRuns) {
|
|
73
|
+
for (let i = 0; i <= run.length - 2; i++) {
|
|
74
|
+
cjkBigrams.push(run.slice(i, i + 2));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const shortTerms = [...new Set([...spaceSplit, ...cjkBigrams])];
|
|
69
79
|
const patternHits = shortTerms.length > 0
|
|
70
80
|
? this.store.patternSearch(shortTerms, { limit: candidatePool })
|
|
71
81
|
: [];
|
|
@@ -74,49 +84,41 @@ export class RecallEngine {
|
|
|
74
84
|
score: 1 / (i + 1),
|
|
75
85
|
}));
|
|
76
86
|
|
|
77
|
-
// Step 1c: Hub memories
|
|
78
|
-
// hub_memories data and embeddings were generated by the same Embedder.
|
|
79
|
-
// Client mode must use remote API (hubSearchMemories) to avoid cross-model
|
|
80
|
-
// embedding mismatch.
|
|
87
|
+
// Step 1c: Hub memories — FTS + pattern + cached embeddings (same strategy as chunks/skills).
|
|
81
88
|
let hubMemFtsRanked: Array<{ id: string; score: number }> = [];
|
|
82
89
|
let hubMemVecRanked: Array<{ id: string; score: number }> = [];
|
|
83
90
|
let hubMemPatternRanked: Array<{ id: string; score: number }> = [];
|
|
84
91
|
if (query && this.ctx.config.sharing?.enabled && this.ctx.config.sharing.role === "hub") {
|
|
85
92
|
try {
|
|
86
93
|
const hubFtsHits = this.store.searchHubMemories(query, { maxResults: candidatePool });
|
|
87
|
-
hubMemFtsRanked = hubFtsHits.map(({ hit }, i) => ({
|
|
88
|
-
id: `hubmem:${hit.id}`, score: 1 / (i + 1),
|
|
89
|
-
}));
|
|
94
|
+
hubMemFtsRanked = hubFtsHits.map(({ hit }, i) => ({ id: `hubmem:${hit.id}`, score: 1 / (i + 1) }));
|
|
90
95
|
} catch { /* hub_memories table may not exist */ }
|
|
91
96
|
if (shortTerms.length > 0) {
|
|
92
97
|
try {
|
|
93
98
|
const hubPatternHits = this.store.hubMemoryPatternSearch(shortTerms, { limit: candidatePool });
|
|
94
|
-
hubMemPatternRanked = hubPatternHits.map((h, i) => ({
|
|
95
|
-
id: `hubmem:${h.memoryId}`, score: 1 / (i + 1),
|
|
96
|
-
}));
|
|
99
|
+
hubMemPatternRanked = hubPatternHits.map((h, i) => ({ id: `hubmem:${h.memoryId}`, score: 1 / (i + 1) }));
|
|
97
100
|
} catch { /* best-effort */ }
|
|
98
101
|
}
|
|
102
|
+
|
|
99
103
|
try {
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
const sim = nA > 0 && nB > 0 ? dot / (Math.sqrt(nA) * Math.sqrt(nB)) : 0;
|
|
111
|
-
if (sim > 0.3) {
|
|
112
|
-
scored.push({ id: `hubmem:${e.memoryId}`, score: sim });
|
|
113
|
-
}
|
|
104
|
+
const qv = await this.embedder.embedQuery(query).catch(() => null);
|
|
105
|
+
if (qv) {
|
|
106
|
+
const memEmbs = this.store.getVisibleHubMemoryEmbeddings("__hub__");
|
|
107
|
+
const scored: Array<{ id: string; score: number }> = [];
|
|
108
|
+
for (const e of memEmbs) {
|
|
109
|
+
let dot = 0, nA = 0, nB = 0;
|
|
110
|
+
const len = Math.min(qv.length, e.vector.length);
|
|
111
|
+
for (let i = 0; i < len; i++) {
|
|
112
|
+
dot += qv[i] * e.vector[i]; nA += qv[i] * qv[i]; nB += e.vector[i] * e.vector[i];
|
|
114
113
|
}
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
const sim = nA > 0 && nB > 0 ? dot / (Math.sqrt(nA) * Math.sqrt(nB)) : 0;
|
|
115
|
+
if (sim > 0.3) scored.push({ id: `hubmem:${e.memoryId}`, score: sim });
|
|
117
116
|
}
|
|
117
|
+
scored.sort((a, b) => b.score - a.score);
|
|
118
|
+
hubMemVecRanked = scored.slice(0, candidatePool);
|
|
118
119
|
}
|
|
119
120
|
} catch { /* best-effort */ }
|
|
121
|
+
|
|
120
122
|
const hubTotal = hubMemFtsRanked.length + hubMemVecRanked.length + hubMemPatternRanked.length;
|
|
121
123
|
if (hubTotal > 0) {
|
|
122
124
|
this.ctx.log.debug(`recall: hub_memories candidates: fts=${hubMemFtsRanked.length}, vec=${hubMemVecRanked.length}, pattern=${hubMemPatternRanked.length}`);
|
package/src/storage/sqlite.ts
CHANGED
|
@@ -116,6 +116,7 @@ export class SqliteStore {
|
|
|
116
116
|
this.migrateLocalSharedTasksOwner();
|
|
117
117
|
this.migrateHubUserIdentityFields();
|
|
118
118
|
this.migrateClientHubConnectionIdentityFields();
|
|
119
|
+
this.migrateTeamSharingInstanceId();
|
|
119
120
|
this.log.debug("Database schema initialized");
|
|
120
121
|
}
|
|
121
122
|
|
|
@@ -176,6 +177,40 @@ export class SqliteStore {
|
|
|
176
177
|
} catch { /* table may not exist yet */ }
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
private migrateTeamSharingInstanceId(): void {
|
|
181
|
+
try {
|
|
182
|
+
const tscCols = this.db.prepare("PRAGMA table_info(team_shared_chunks)").all() as Array<{ name: string }>;
|
|
183
|
+
if (tscCols.length > 0 && !tscCols.some(c => c.name === "hub_instance_id")) {
|
|
184
|
+
this.db.exec("ALTER TABLE team_shared_chunks ADD COLUMN hub_instance_id TEXT NOT NULL DEFAULT ''");
|
|
185
|
+
this.log.info("Migrated: added hub_instance_id to team_shared_chunks");
|
|
186
|
+
}
|
|
187
|
+
} catch { /* table may not exist yet */ }
|
|
188
|
+
try {
|
|
189
|
+
const lstCols = this.db.prepare("PRAGMA table_info(local_shared_tasks)").all() as Array<{ name: string }>;
|
|
190
|
+
if (lstCols.length > 0 && !lstCols.some(c => c.name === "hub_instance_id")) {
|
|
191
|
+
this.db.exec("ALTER TABLE local_shared_tasks ADD COLUMN hub_instance_id TEXT NOT NULL DEFAULT ''");
|
|
192
|
+
this.log.info("Migrated: added hub_instance_id to local_shared_tasks");
|
|
193
|
+
}
|
|
194
|
+
} catch { /* table may not exist yet */ }
|
|
195
|
+
try {
|
|
196
|
+
const connCols = this.db.prepare("PRAGMA table_info(client_hub_connection)").all() as Array<{ name: string }>;
|
|
197
|
+
if (connCols.length > 0 && !connCols.some(c => c.name === "hub_instance_id")) {
|
|
198
|
+
this.db.exec("ALTER TABLE client_hub_connection ADD COLUMN hub_instance_id TEXT NOT NULL DEFAULT ''");
|
|
199
|
+
this.log.info("Migrated: added hub_instance_id to client_hub_connection");
|
|
200
|
+
}
|
|
201
|
+
} catch { /* table may not exist yet */ }
|
|
202
|
+
this.db.exec(`
|
|
203
|
+
CREATE TABLE IF NOT EXISTS team_shared_skills (
|
|
204
|
+
skill_id TEXT PRIMARY KEY,
|
|
205
|
+
hub_skill_id TEXT NOT NULL DEFAULT '',
|
|
206
|
+
visibility TEXT NOT NULL DEFAULT 'public',
|
|
207
|
+
group_id TEXT,
|
|
208
|
+
hub_instance_id TEXT NOT NULL DEFAULT '',
|
|
209
|
+
shared_at INTEGER NOT NULL
|
|
210
|
+
)
|
|
211
|
+
`);
|
|
212
|
+
}
|
|
213
|
+
|
|
179
214
|
private migrateOwnerFields(): void {
|
|
180
215
|
const chunkCols = this.db.prepare("PRAGMA table_info(chunks)").all() as Array<{ name: string }>;
|
|
181
216
|
if (!chunkCols.some((c) => c.name === "owner")) {
|
|
@@ -778,12 +813,13 @@ export class SqliteStore {
|
|
|
778
813
|
);
|
|
779
814
|
|
|
780
815
|
CREATE TABLE IF NOT EXISTS local_shared_tasks (
|
|
781
|
-
task_id
|
|
782
|
-
hub_task_id
|
|
783
|
-
visibility
|
|
784
|
-
group_id
|
|
785
|
-
synced_chunks
|
|
786
|
-
|
|
816
|
+
task_id TEXT PRIMARY KEY,
|
|
817
|
+
hub_task_id TEXT NOT NULL,
|
|
818
|
+
visibility TEXT NOT NULL DEFAULT 'public',
|
|
819
|
+
group_id TEXT,
|
|
820
|
+
synced_chunks INTEGER NOT NULL DEFAULT 0,
|
|
821
|
+
hub_instance_id TEXT NOT NULL DEFAULT '',
|
|
822
|
+
shared_at INTEGER NOT NULL
|
|
787
823
|
);
|
|
788
824
|
|
|
789
825
|
CREATE TABLE IF NOT EXISTS local_shared_memories (
|
|
@@ -794,11 +830,21 @@ export class SqliteStore {
|
|
|
794
830
|
|
|
795
831
|
-- Client: team share UI metadata only (no hub_memories row — avoids local FTS/embed recall duplication)
|
|
796
832
|
CREATE TABLE IF NOT EXISTS team_shared_chunks (
|
|
797
|
-
chunk_id
|
|
798
|
-
hub_memory_id
|
|
799
|
-
visibility
|
|
800
|
-
group_id
|
|
801
|
-
|
|
833
|
+
chunk_id TEXT PRIMARY KEY REFERENCES chunks(id) ON DELETE CASCADE,
|
|
834
|
+
hub_memory_id TEXT NOT NULL DEFAULT '',
|
|
835
|
+
visibility TEXT NOT NULL DEFAULT 'public',
|
|
836
|
+
group_id TEXT,
|
|
837
|
+
hub_instance_id TEXT NOT NULL DEFAULT '',
|
|
838
|
+
shared_at INTEGER NOT NULL
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
CREATE TABLE IF NOT EXISTS team_shared_skills (
|
|
842
|
+
skill_id TEXT PRIMARY KEY,
|
|
843
|
+
hub_skill_id TEXT NOT NULL DEFAULT '',
|
|
844
|
+
visibility TEXT NOT NULL DEFAULT 'public',
|
|
845
|
+
group_id TEXT,
|
|
846
|
+
hub_instance_id TEXT NOT NULL DEFAULT '',
|
|
847
|
+
shared_at INTEGER NOT NULL
|
|
802
848
|
);
|
|
803
849
|
|
|
804
850
|
CREATE TABLE IF NOT EXISTS hub_users (
|
|
@@ -960,10 +1006,10 @@ export class SqliteStore {
|
|
|
960
1006
|
CREATE INDEX IF NOT EXISTS idx_hub_memories_group ON hub_memories(group_id);
|
|
961
1007
|
|
|
962
1008
|
CREATE TABLE IF NOT EXISTS hub_memory_embeddings (
|
|
963
|
-
memory_id
|
|
964
|
-
vector
|
|
965
|
-
dimensions
|
|
966
|
-
updated_at
|
|
1009
|
+
memory_id TEXT PRIMARY KEY REFERENCES hub_memories(id) ON DELETE CASCADE,
|
|
1010
|
+
vector BLOB NOT NULL,
|
|
1011
|
+
dimensions INTEGER NOT NULL,
|
|
1012
|
+
updated_at INTEGER NOT NULL
|
|
967
1013
|
);
|
|
968
1014
|
|
|
969
1015
|
CREATE VIRTUAL TABLE IF NOT EXISTS hub_memories_fts USING fts5(
|
|
@@ -1211,6 +1257,13 @@ export class SqliteStore {
|
|
|
1211
1257
|
} catch { return []; }
|
|
1212
1258
|
}
|
|
1213
1259
|
|
|
1260
|
+
listHubMemories(opts: { limit?: number } = {}): Array<{ id: string; summary?: string; content?: string }> {
|
|
1261
|
+
const limit = opts.limit ?? 200;
|
|
1262
|
+
try {
|
|
1263
|
+
return this.db.prepare("SELECT id, summary, content FROM hub_memories ORDER BY created_at DESC LIMIT ?").all(limit) as Array<{ id: string; summary?: string; content?: string }>;
|
|
1264
|
+
} catch { return []; }
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1214
1267
|
// ─── Vector Search ───
|
|
1215
1268
|
|
|
1216
1269
|
getAllEmbeddings(ownerFilter?: string[]): Array<{ chunkId: string; vector: number[] }> {
|
|
@@ -1379,6 +1432,7 @@ export class SqliteStore {
|
|
|
1379
1432
|
"skills",
|
|
1380
1433
|
"local_shared_memories",
|
|
1381
1434
|
"team_shared_chunks",
|
|
1435
|
+
"team_shared_skills",
|
|
1382
1436
|
"local_shared_tasks",
|
|
1383
1437
|
"embeddings",
|
|
1384
1438
|
"chunks",
|
|
@@ -1803,8 +1857,8 @@ export class SqliteStore {
|
|
|
1803
1857
|
|
|
1804
1858
|
setClientHubConnection(conn: ClientHubConnection): void {
|
|
1805
1859
|
this.db.prepare(`
|
|
1806
|
-
INSERT INTO client_hub_connection (id, hub_url, user_id, username, user_token, role, connected_at, identity_key, last_known_status)
|
|
1807
|
-
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1860
|
+
INSERT INTO client_hub_connection (id, hub_url, user_id, username, user_token, role, connected_at, identity_key, last_known_status, hub_instance_id)
|
|
1861
|
+
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1808
1862
|
ON CONFLICT(id) DO UPDATE SET
|
|
1809
1863
|
hub_url = excluded.hub_url,
|
|
1810
1864
|
user_id = excluded.user_id,
|
|
@@ -1813,8 +1867,9 @@ export class SqliteStore {
|
|
|
1813
1867
|
role = excluded.role,
|
|
1814
1868
|
connected_at = excluded.connected_at,
|
|
1815
1869
|
identity_key = excluded.identity_key,
|
|
1816
|
-
last_known_status = excluded.last_known_status
|
|
1817
|
-
|
|
1870
|
+
last_known_status = excluded.last_known_status,
|
|
1871
|
+
hub_instance_id = excluded.hub_instance_id
|
|
1872
|
+
`).run(conn.hubUrl, conn.userId, conn.username, conn.userToken, conn.role, conn.connectedAt, conn.identityKey ?? "", conn.lastKnownStatus ?? "", conn.hubInstanceId ?? "");
|
|
1818
1873
|
}
|
|
1819
1874
|
|
|
1820
1875
|
getClientHubConnection(): ClientHubConnection | null {
|
|
@@ -1828,32 +1883,33 @@ export class SqliteStore {
|
|
|
1828
1883
|
|
|
1829
1884
|
// ─── Local Shared Tasks (client-side tracking) ───
|
|
1830
1885
|
|
|
1831
|
-
markTaskShared(taskId: string, hubTaskId: string, syncedChunks: number, visibility: string, groupId?: string | null): void {
|
|
1886
|
+
markTaskShared(taskId: string, hubTaskId: string, syncedChunks: number, visibility: string, groupId?: string | null, hubInstanceId?: string): void {
|
|
1832
1887
|
this.db.prepare(`
|
|
1833
|
-
INSERT INTO local_shared_tasks (task_id, hub_task_id, visibility, group_id, synced_chunks, shared_at)
|
|
1834
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
1888
|
+
INSERT INTO local_shared_tasks (task_id, hub_task_id, visibility, group_id, synced_chunks, hub_instance_id, shared_at)
|
|
1889
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1835
1890
|
ON CONFLICT(task_id) DO UPDATE SET
|
|
1836
1891
|
hub_task_id = excluded.hub_task_id,
|
|
1837
1892
|
visibility = excluded.visibility,
|
|
1838
1893
|
group_id = excluded.group_id,
|
|
1839
1894
|
synced_chunks = excluded.synced_chunks,
|
|
1895
|
+
hub_instance_id = excluded.hub_instance_id,
|
|
1840
1896
|
shared_at = excluded.shared_at
|
|
1841
|
-
`).run(taskId, hubTaskId, visibility, groupId ?? null, syncedChunks, Date.now());
|
|
1897
|
+
`).run(taskId, hubTaskId, visibility, groupId ?? null, syncedChunks, hubInstanceId ?? "", Date.now());
|
|
1842
1898
|
}
|
|
1843
1899
|
|
|
1844
1900
|
unmarkTaskShared(taskId: string): void {
|
|
1845
1901
|
this.db.prepare('DELETE FROM local_shared_tasks WHERE task_id = ?').run(taskId);
|
|
1846
1902
|
}
|
|
1847
1903
|
|
|
1848
|
-
getLocalSharedTask(taskId: string): { taskId: string; hubTaskId: string; visibility: string; groupId: string | null; syncedChunks: number; sharedAt: number } | null {
|
|
1904
|
+
getLocalSharedTask(taskId: string): { taskId: string; hubTaskId: string; visibility: string; groupId: string | null; syncedChunks: number; sharedAt: number; hubInstanceId: string } | null {
|
|
1849
1905
|
const row = this.db.prepare('SELECT * FROM local_shared_tasks WHERE task_id = ?').get(taskId) as any;
|
|
1850
1906
|
if (!row) return null;
|
|
1851
|
-
return { taskId: row.task_id, hubTaskId: row.hub_task_id, visibility: row.visibility, groupId: row.group_id, syncedChunks: row.synced_chunks, sharedAt: row.shared_at };
|
|
1907
|
+
return { taskId: row.task_id, hubTaskId: row.hub_task_id, visibility: row.visibility, groupId: row.group_id, syncedChunks: row.synced_chunks, sharedAt: row.shared_at, hubInstanceId: row.hub_instance_id || "" };
|
|
1852
1908
|
}
|
|
1853
1909
|
|
|
1854
|
-
listLocalSharedTasks(): Array<{ taskId: string; hubTaskId: string; visibility: string; groupId: string | null; syncedChunks: number }> {
|
|
1855
|
-
const rows = this.db.prepare('SELECT task_id, hub_task_id, visibility, group_id, synced_chunks FROM local_shared_tasks').all() as any[];
|
|
1856
|
-
return rows.map(r => ({ taskId: r.task_id, hubTaskId: r.hub_task_id, visibility: r.visibility, groupId: r.group_id, syncedChunks: r.synced_chunks }));
|
|
1910
|
+
listLocalSharedTasks(): Array<{ taskId: string; hubTaskId: string; visibility: string; groupId: string | null; syncedChunks: number; hubInstanceId: string }> {
|
|
1911
|
+
const rows = this.db.prepare('SELECT task_id, hub_task_id, visibility, group_id, synced_chunks, hub_instance_id FROM local_shared_tasks').all() as any[];
|
|
1912
|
+
return rows.map(r => ({ taskId: r.task_id, hubTaskId: r.hub_task_id, visibility: r.visibility, groupId: r.group_id, syncedChunks: r.synced_chunks, hubInstanceId: r.hub_instance_id || "" }));
|
|
1857
1913
|
}
|
|
1858
1914
|
|
|
1859
1915
|
// ─── Local Shared Memories (client-side tracking) ───
|
|
@@ -1958,11 +2014,23 @@ export class SqliteStore {
|
|
|
1958
2014
|
});
|
|
1959
2015
|
}
|
|
1960
2016
|
|
|
2017
|
+
deleteHubMemoriesByUser(userId: string): void {
|
|
2018
|
+
this.db.prepare('DELETE FROM hub_memories WHERE source_user_id = ?').run(userId);
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
deleteHubTasksByUser(userId: string): void {
|
|
2022
|
+
this.db.prepare('DELETE FROM hub_tasks WHERE source_user_id = ?').run(userId);
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
deleteHubSkillsByUser(userId: string): void {
|
|
2026
|
+
this.db.prepare('DELETE FROM hub_skills WHERE source_user_id = ?').run(userId);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
1961
2029
|
deleteHubUser(userId: string, cleanResources = false): boolean {
|
|
1962
2030
|
if (cleanResources) {
|
|
1963
|
-
this.
|
|
1964
|
-
this.
|
|
1965
|
-
this.
|
|
2031
|
+
this.deleteHubTasksByUser(userId);
|
|
2032
|
+
this.deleteHubSkillsByUser(userId);
|
|
2033
|
+
this.deleteHubMemoriesByUser(userId);
|
|
1966
2034
|
const result = this.db.prepare('DELETE FROM hub_users WHERE id = ?').run(userId);
|
|
1967
2035
|
return result.changes > 0;
|
|
1968
2036
|
}
|
|
@@ -2138,6 +2206,36 @@ export class SqliteStore {
|
|
|
2138
2206
|
}));
|
|
2139
2207
|
}
|
|
2140
2208
|
|
|
2209
|
+
upsertHubMemoryEmbedding(memoryId: string, vector: Float32Array): void {
|
|
2210
|
+
const buf = Buffer.from(vector.buffer, vector.byteOffset, vector.byteLength);
|
|
2211
|
+
this.db.prepare(`
|
|
2212
|
+
INSERT INTO hub_memory_embeddings (memory_id, vector, dimensions, updated_at)
|
|
2213
|
+
VALUES (?, ?, ?, ?)
|
|
2214
|
+
ON CONFLICT(memory_id) DO UPDATE SET vector = excluded.vector, dimensions = excluded.dimensions, updated_at = excluded.updated_at
|
|
2215
|
+
`).run(memoryId, buf, vector.length, Date.now());
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
getHubMemoryEmbedding(memoryId: string): Float32Array | null {
|
|
2219
|
+
const row = this.db.prepare('SELECT vector, dimensions FROM hub_memory_embeddings WHERE memory_id = ?').get(memoryId) as { vector: Buffer; dimensions: number } | undefined;
|
|
2220
|
+
if (!row) return null;
|
|
2221
|
+
return new Float32Array(row.vector.buffer, row.vector.byteOffset, row.dimensions);
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
getVisibleHubMemoryEmbeddings(userId: string): Array<{ memoryId: string; vector: Float32Array }> {
|
|
2225
|
+
const rows = this.db.prepare(`
|
|
2226
|
+
SELECT hme.memory_id, hme.vector, hme.dimensions
|
|
2227
|
+
FROM hub_memory_embeddings hme
|
|
2228
|
+
JOIN hub_memories hm ON hm.id = hme.memory_id
|
|
2229
|
+
WHERE hm.visibility = 'public'
|
|
2230
|
+
OR hm.source_user_id = ?
|
|
2231
|
+
OR EXISTS (SELECT 1 FROM hub_group_members gm WHERE gm.group_id = hm.group_id AND gm.user_id = ?)
|
|
2232
|
+
`).all(userId, userId) as Array<{ memory_id: string; vector: Buffer; dimensions: number }>;
|
|
2233
|
+
return rows.map(r => ({
|
|
2234
|
+
memoryId: r.memory_id,
|
|
2235
|
+
vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
|
|
2236
|
+
}));
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2141
2239
|
searchHubChunks(query: string, options?: { userId?: string; maxResults?: number }): Array<{ hit: HubSearchRow; rank: number }> {
|
|
2142
2240
|
const limit = options?.maxResults ?? 10;
|
|
2143
2241
|
const userId = options?.userId ?? "";
|
|
@@ -2369,25 +2467,26 @@ export class SqliteStore {
|
|
|
2369
2467
|
|
|
2370
2468
|
upsertTeamSharedChunk(
|
|
2371
2469
|
chunkId: string,
|
|
2372
|
-
row: { hubMemoryId?: string; visibility?: string; groupId?: string | null },
|
|
2470
|
+
row: { hubMemoryId?: string; visibility?: string; groupId?: string | null; hubInstanceId?: string },
|
|
2373
2471
|
): void {
|
|
2374
2472
|
const now = Date.now();
|
|
2375
2473
|
const vis = row.visibility === "group" ? "group" : "public";
|
|
2376
2474
|
const gid = vis === "group" ? (row.groupId ?? null) : null;
|
|
2377
2475
|
this.db.prepare(`
|
|
2378
|
-
INSERT INTO team_shared_chunks (chunk_id, hub_memory_id, visibility, group_id, shared_at)
|
|
2379
|
-
VALUES (?, ?, ?, ?, ?)
|
|
2476
|
+
INSERT INTO team_shared_chunks (chunk_id, hub_memory_id, visibility, group_id, hub_instance_id, shared_at)
|
|
2477
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
2380
2478
|
ON CONFLICT(chunk_id) DO UPDATE SET
|
|
2381
2479
|
hub_memory_id = excluded.hub_memory_id,
|
|
2382
2480
|
visibility = excluded.visibility,
|
|
2383
2481
|
group_id = excluded.group_id,
|
|
2482
|
+
hub_instance_id = excluded.hub_instance_id,
|
|
2384
2483
|
shared_at = excluded.shared_at
|
|
2385
|
-
`).run(chunkId, row.hubMemoryId ?? "", vis, gid, now);
|
|
2484
|
+
`).run(chunkId, row.hubMemoryId ?? "", vis, gid, row.hubInstanceId ?? "", now);
|
|
2386
2485
|
}
|
|
2387
2486
|
|
|
2388
|
-
getTeamSharedChunk(chunkId: string): { chunkId: string; hubMemoryId: string; visibility: string; groupId: string | null; sharedAt: number } | null {
|
|
2389
|
-
const r = this.db.prepare("SELECT chunk_id, hub_memory_id, visibility, group_id, shared_at FROM team_shared_chunks WHERE chunk_id = ?").get(chunkId) as {
|
|
2390
|
-
chunk_id: string; hub_memory_id: string; visibility: string; group_id: string | null; shared_at: number;
|
|
2487
|
+
getTeamSharedChunk(chunkId: string): { chunkId: string; hubMemoryId: string; visibility: string; groupId: string | null; hubInstanceId: string; sharedAt: number } | null {
|
|
2488
|
+
const r = this.db.prepare("SELECT chunk_id, hub_memory_id, visibility, group_id, hub_instance_id, shared_at FROM team_shared_chunks WHERE chunk_id = ?").get(chunkId) as {
|
|
2489
|
+
chunk_id: string; hub_memory_id: string; visibility: string; group_id: string | null; hub_instance_id: string; shared_at: number;
|
|
2391
2490
|
} | undefined;
|
|
2392
2491
|
if (!r) return null;
|
|
2393
2492
|
return {
|
|
@@ -2395,6 +2494,7 @@ export class SqliteStore {
|
|
|
2395
2494
|
hubMemoryId: r.hub_memory_id,
|
|
2396
2495
|
visibility: r.visibility,
|
|
2397
2496
|
groupId: r.group_id,
|
|
2497
|
+
hubInstanceId: r.hub_instance_id || "",
|
|
2398
2498
|
sharedAt: r.shared_at,
|
|
2399
2499
|
};
|
|
2400
2500
|
}
|
|
@@ -2404,6 +2504,58 @@ export class SqliteStore {
|
|
|
2404
2504
|
return info.changes > 0;
|
|
2405
2505
|
}
|
|
2406
2506
|
|
|
2507
|
+
// ─── Team Shared Skills (Client role — UI metadata only) ───
|
|
2508
|
+
|
|
2509
|
+
upsertTeamSharedSkill(skillId: string, row: { hubSkillId?: string; visibility?: string; groupId?: string | null; hubInstanceId?: string }): void {
|
|
2510
|
+
const now = Date.now();
|
|
2511
|
+
const vis = row.visibility === "group" ? "group" : "public";
|
|
2512
|
+
const gid = vis === "group" ? (row.groupId ?? null) : null;
|
|
2513
|
+
this.db.prepare(`
|
|
2514
|
+
INSERT INTO team_shared_skills (skill_id, hub_skill_id, visibility, group_id, hub_instance_id, shared_at)
|
|
2515
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
2516
|
+
ON CONFLICT(skill_id) DO UPDATE SET
|
|
2517
|
+
hub_skill_id = excluded.hub_skill_id,
|
|
2518
|
+
visibility = excluded.visibility,
|
|
2519
|
+
group_id = excluded.group_id,
|
|
2520
|
+
hub_instance_id = excluded.hub_instance_id,
|
|
2521
|
+
shared_at = excluded.shared_at
|
|
2522
|
+
`).run(skillId, row.hubSkillId ?? "", vis, gid, row.hubInstanceId ?? "", now);
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
getTeamSharedSkill(skillId: string): { skillId: string; hubSkillId: string; visibility: string; groupId: string | null; hubInstanceId: string; sharedAt: number } | null {
|
|
2526
|
+
const r = this.db.prepare("SELECT * FROM team_shared_skills WHERE skill_id = ?").get(skillId) as any;
|
|
2527
|
+
if (!r) return null;
|
|
2528
|
+
return { skillId: r.skill_id, hubSkillId: r.hub_skill_id, visibility: r.visibility, groupId: r.group_id, hubInstanceId: r.hub_instance_id || "", sharedAt: r.shared_at };
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
deleteTeamSharedSkill(skillId: string): boolean {
|
|
2532
|
+
return this.db.prepare("DELETE FROM team_shared_skills WHERE skill_id = ?").run(skillId).changes > 0;
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
// ─── Team sharing cleanup (role switch / leave) ───
|
|
2536
|
+
|
|
2537
|
+
clearTeamSharedChunks(): void {
|
|
2538
|
+
this.db.prepare("DELETE FROM team_shared_chunks").run();
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
clearTeamSharedSkills(): void {
|
|
2542
|
+
this.db.prepare("DELETE FROM team_shared_skills").run();
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
downgradeTeamSharedTasksToLocal(): void {
|
|
2546
|
+
this.db.prepare("UPDATE local_shared_tasks SET hub_task_id = '', hub_instance_id = '', visibility = 'public', group_id = NULL, synced_chunks = 0").run();
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
downgradeTeamSharedTaskToLocal(taskId: string): void {
|
|
2550
|
+
this.db.prepare("UPDATE local_shared_tasks SET hub_task_id = '', hub_instance_id = '', visibility = 'public', group_id = NULL, synced_chunks = 0 WHERE task_id = ?").run(taskId);
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
clearAllTeamSharingState(): void {
|
|
2554
|
+
this.clearTeamSharedChunks();
|
|
2555
|
+
this.clearTeamSharedSkills();
|
|
2556
|
+
this.downgradeTeamSharedTasksToLocal();
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2407
2559
|
// ─── Hub Notifications ───
|
|
2408
2560
|
|
|
2409
2561
|
insertHubNotification(n: { id: string; userId: string; type: string; resource: string; title: string; message?: string }): void {
|
|
@@ -2445,20 +2597,8 @@ export class SqliteStore {
|
|
|
2445
2597
|
this.db.prepare('DELETE FROM hub_notifications WHERE user_id = ?').run(userId);
|
|
2446
2598
|
}
|
|
2447
2599
|
|
|
2448
|
-
upsertHubMemoryEmbedding
|
|
2449
|
-
|
|
2450
|
-
this.db.prepare(`
|
|
2451
|
-
INSERT INTO hub_memory_embeddings (memory_id, vector, dimensions, updated_at)
|
|
2452
|
-
VALUES (?, ?, ?, ?)
|
|
2453
|
-
ON CONFLICT(memory_id) DO UPDATE SET vector = excluded.vector, dimensions = excluded.dimensions, updated_at = excluded.updated_at
|
|
2454
|
-
`).run(memoryId, buf, vector.length, Date.now());
|
|
2455
|
-
}
|
|
2456
|
-
|
|
2457
|
-
getHubMemoryEmbedding(memoryId: string): Float32Array | null {
|
|
2458
|
-
const row = this.db.prepare('SELECT vector, dimensions FROM hub_memory_embeddings WHERE memory_id = ?').get(memoryId) as { vector: Buffer; dimensions: number } | undefined;
|
|
2459
|
-
if (!row) return null;
|
|
2460
|
-
return new Float32Array(row.vector.buffer, row.vector.byteOffset, row.dimensions);
|
|
2461
|
-
}
|
|
2600
|
+
// upsertHubMemoryEmbedding / getHubMemoryEmbedding removed:
|
|
2601
|
+
// hub memory vectors are now computed on-the-fly at search time.
|
|
2462
2602
|
|
|
2463
2603
|
searchHubMemories(query: string, options?: { userId?: string; maxResults?: number }): Array<{ hit: HubMemorySearchRow; rank: number }> {
|
|
2464
2604
|
const limit = options?.maxResults ?? 10;
|
|
@@ -2478,17 +2618,7 @@ export class SqliteStore {
|
|
|
2478
2618
|
return rows.map((row, idx) => ({ hit: row, rank: idx + 1 }));
|
|
2479
2619
|
}
|
|
2480
2620
|
|
|
2481
|
-
getVisibleHubMemoryEmbeddings
|
|
2482
|
-
const rows = this.db.prepare(`
|
|
2483
|
-
SELECT hme.memory_id, hme.vector, hme.dimensions
|
|
2484
|
-
FROM hub_memory_embeddings hme
|
|
2485
|
-
JOIN hub_memories hm ON hm.id = hme.memory_id
|
|
2486
|
-
`).all() as Array<{ memory_id: string; vector: Buffer; dimensions: number }>;
|
|
2487
|
-
return rows.map(r => ({
|
|
2488
|
-
memoryId: r.memory_id,
|
|
2489
|
-
vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
|
|
2490
|
-
}));
|
|
2491
|
-
}
|
|
2621
|
+
// getVisibleHubMemoryEmbeddings removed: vectors computed on-the-fly at search time.
|
|
2492
2622
|
|
|
2493
2623
|
getVisibleHubSearchHitByMemoryId(memoryId: string, userId: string): HubMemorySearchRow | null {
|
|
2494
2624
|
const row = this.db.prepare(`
|
|
@@ -2740,6 +2870,7 @@ interface ClientHubConnection {
|
|
|
2740
2870
|
connectedAt: number;
|
|
2741
2871
|
identityKey?: string;
|
|
2742
2872
|
lastKnownStatus?: string;
|
|
2873
|
+
hubInstanceId?: string;
|
|
2743
2874
|
}
|
|
2744
2875
|
|
|
2745
2876
|
interface ClientHubConnectionRow {
|
|
@@ -2751,6 +2882,7 @@ interface ClientHubConnectionRow {
|
|
|
2751
2882
|
connected_at: number;
|
|
2752
2883
|
identity_key?: string;
|
|
2753
2884
|
last_known_status?: string;
|
|
2885
|
+
hub_instance_id?: string;
|
|
2754
2886
|
}
|
|
2755
2887
|
|
|
2756
2888
|
function rowToClientHubConnection(row: ClientHubConnectionRow): ClientHubConnection {
|
|
@@ -2763,6 +2895,7 @@ function rowToClientHubConnection(row: ClientHubConnectionRow): ClientHubConnect
|
|
|
2763
2895
|
connectedAt: row.connected_at,
|
|
2764
2896
|
identityKey: row.identity_key || "",
|
|
2765
2897
|
lastKnownStatus: row.last_known_status || "",
|
|
2898
|
+
hubInstanceId: row.hub_instance_id || "",
|
|
2766
2899
|
};
|
|
2767
2900
|
}
|
|
2768
2901
|
|
package/src/tools/memory-get.ts
CHANGED
|
@@ -47,7 +47,10 @@ export function createMemoryGetTool(store: SqliteStore): ToolDefinition {
|
|
|
47
47
|
return { error: `Chunk not found: ${ref.chunkId}` };
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
let content = chunk.content;
|
|
51
|
+
if (content.length > maxChars) {
|
|
52
|
+
content = content.slice(0, maxChars) + "…";
|
|
53
|
+
}
|
|
51
54
|
|
|
52
55
|
const result: GetResult = {
|
|
53
56
|
content,
|