@knowzai/local-store 0.1.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/LICENSE +21 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/hash.d.ts +3 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +0 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/open.d.ts +7 -0
- package/dist/open.d.ts.map +1 -0
- package/dist/open.js +24 -0
- package/dist/open.js.map +1 -0
- package/dist/pg-provision.d.ts +64 -0
- package/dist/pg-provision.d.ts.map +1 -0
- package/dist/pg-provision.js +270 -0
- package/dist/pg-provision.js.map +1 -0
- package/dist/postgres-store.d.ts +40 -0
- package/dist/postgres-store.d.ts.map +1 -0
- package/dist/postgres-store.js +186 -0
- package/dist/postgres-store.js.map +1 -0
- package/dist/sqlite-store.d.ts +54 -0
- package/dist/sqlite-store.d.ts.map +1 -0
- package/dist/sqlite-store.js +416 -0
- package/dist/sqlite-store.js.map +1 -0
- package/dist/sync-engine.d.ts +118 -0
- package/dist/sync-engine.d.ts.map +1 -0
- package/dist/sync-engine.js +342 -0
- package/dist/sync-engine.js.map +1 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { supportsOutbox } from './types.js';
|
|
2
|
+
import { contentHash } from './hash.js';
|
|
3
|
+
function emptyReport() {
|
|
4
|
+
return { pulled: 0, pushed: 0, conflicts: [], skipped: 0 };
|
|
5
|
+
}
|
|
6
|
+
export class SyncEngine {
|
|
7
|
+
store;
|
|
8
|
+
client;
|
|
9
|
+
encryptor;
|
|
10
|
+
constructor(store, client, opts = {}) {
|
|
11
|
+
this.store = store;
|
|
12
|
+
this.client = client;
|
|
13
|
+
this.encryptor = opts.encryptor;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Iterate EVERY page of `knowledge.list` for a vault, invoking `onSummary`
|
|
17
|
+
* per remote item. Stops when the cumulative count reaches `total` OR a page
|
|
18
|
+
* returns fewer than `pageSize` items (defensive against a 0/undefined total),
|
|
19
|
+
* so the full remote set is always covered without an infinite loop.
|
|
20
|
+
*/
|
|
21
|
+
async forEachRemote(opts, onSummary) {
|
|
22
|
+
const pageSize = opts.pageSize;
|
|
23
|
+
let page = 1;
|
|
24
|
+
let fetched = 0;
|
|
25
|
+
for (;;) {
|
|
26
|
+
const result = await this.client.knowledge.list({ vaultId: opts.vaultId, page, pageSize });
|
|
27
|
+
const items = result.items ?? [];
|
|
28
|
+
for (const summary of items)
|
|
29
|
+
await onSummary(summary);
|
|
30
|
+
fetched += items.length;
|
|
31
|
+
const total = result.total;
|
|
32
|
+
const reachedTotal = typeof total === 'number' && total >= 0 && fetched >= total;
|
|
33
|
+
if (items.length < pageSize || reachedTotal)
|
|
34
|
+
break;
|
|
35
|
+
page++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Cloud → local. Upserts remote items; flags conflicts; skips unchanged. */
|
|
39
|
+
async pull(opts = {}) {
|
|
40
|
+
const report = emptyReport();
|
|
41
|
+
await this.forEachRemote({ vaultId: opts.vaultId, pageSize: opts.pageSize ?? 100 }, async (summary) => {
|
|
42
|
+
const detail = await this.client.knowledge.get(summary.id);
|
|
43
|
+
// Model-3: decrypt ciphertext fetched from the cloud into local plaintext.
|
|
44
|
+
let content = detail.content;
|
|
45
|
+
if (content && this.encryptor?.isEncrypted(content))
|
|
46
|
+
content = this.encryptor.decrypt(content);
|
|
47
|
+
const hash = contentHash(content, detail.title, detail.tags ?? []);
|
|
48
|
+
const local = await this.store.getItem(detail.id);
|
|
49
|
+
if (local && local.contentHash === hash && local.syncState === 'synced') {
|
|
50
|
+
report.skipped++;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (local &&
|
|
54
|
+
local.syncState === 'dirty' &&
|
|
55
|
+
local.remoteVersion != null &&
|
|
56
|
+
detail.version > local.remoteVersion) {
|
|
57
|
+
await this.store.upsertItem({ ...local, syncState: 'conflict' });
|
|
58
|
+
report.conflicts.push(detail.id);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
await this.store.upsertItem({
|
|
62
|
+
id: detail.id,
|
|
63
|
+
vaultId: detail.vaultId,
|
|
64
|
+
title: detail.title,
|
|
65
|
+
type: detail.type,
|
|
66
|
+
contentHash: hash,
|
|
67
|
+
content,
|
|
68
|
+
tags: detail.tags ?? [],
|
|
69
|
+
updatedAt: detail.updatedAt,
|
|
70
|
+
syncState: 'synced',
|
|
71
|
+
remoteVersion: detail.version,
|
|
72
|
+
});
|
|
73
|
+
report.pulled++;
|
|
74
|
+
});
|
|
75
|
+
return report;
|
|
76
|
+
}
|
|
77
|
+
/** Local dirty/new → cloud. Clears dirty + records remoteVersion. */
|
|
78
|
+
async push(opts = {}) {
|
|
79
|
+
const report = emptyReport();
|
|
80
|
+
const pending = [
|
|
81
|
+
...(await this.store.listItems({ syncState: 'new', vaultId: opts.vaultId })),
|
|
82
|
+
...(await this.store.listItems({ syncState: 'dirty', vaultId: opts.vaultId })),
|
|
83
|
+
];
|
|
84
|
+
for (const it of pending) {
|
|
85
|
+
// Model-3: encrypt before the content leaves the machine.
|
|
86
|
+
const wire = it.content != null && this.encryptor ? this.encryptor.encrypt(it.content) : it.content;
|
|
87
|
+
if (it.remoteVersion == null) {
|
|
88
|
+
const created = await this.client.knowledge.create({
|
|
89
|
+
vaultId: it.vaultId,
|
|
90
|
+
title: it.title,
|
|
91
|
+
content: wire,
|
|
92
|
+
type: it.type,
|
|
93
|
+
tags: it.tags,
|
|
94
|
+
});
|
|
95
|
+
if (created.id !== it.id)
|
|
96
|
+
await this.store.reassignItemId(it.id, created.id);
|
|
97
|
+
await this.store.upsertItem({
|
|
98
|
+
...it,
|
|
99
|
+
id: created.id,
|
|
100
|
+
syncState: 'synced',
|
|
101
|
+
remoteVersion: created.version,
|
|
102
|
+
// hash the LOCAL plaintext (server echoes ciphertext under CMEK).
|
|
103
|
+
contentHash: contentHash(it.content, it.title, it.tags),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// F26 — precondition guard. The API can't yet supply a monotonic
|
|
108
|
+
// version on create/update (create() returns 0, update() falls back to
|
|
109
|
+
// 0), so version-based conflict detection on pull/recover is effectively
|
|
110
|
+
// dead. Re-fetch the remote before overwriting: if it advanced past the
|
|
111
|
+
// version this local copy last saw, mark a conflict instead of silently
|
|
112
|
+
// clobbering the concurrent remote edit.
|
|
113
|
+
if (it.remoteVersion != null) {
|
|
114
|
+
const current = await this.client.knowledge.get(it.id);
|
|
115
|
+
if (current.version > it.remoteVersion) {
|
|
116
|
+
await this.store.upsertItem({ ...it, syncState: 'conflict' });
|
|
117
|
+
report.conflicts.push(it.id);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const updated = await this.client.knowledge.update(it.id, {
|
|
122
|
+
title: it.title,
|
|
123
|
+
content: wire,
|
|
124
|
+
tags: it.tags,
|
|
125
|
+
vaultId: it.vaultId,
|
|
126
|
+
});
|
|
127
|
+
await this.store.upsertItem({
|
|
128
|
+
...it,
|
|
129
|
+
syncState: 'synced',
|
|
130
|
+
remoteVersion: updated.version,
|
|
131
|
+
contentHash: contentHash(it.content, it.title, it.tags),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
report.pushed++;
|
|
135
|
+
}
|
|
136
|
+
return report;
|
|
137
|
+
}
|
|
138
|
+
async run(opts = {}) {
|
|
139
|
+
const a = await this.pull(opts);
|
|
140
|
+
const b = await this.push(opts);
|
|
141
|
+
return {
|
|
142
|
+
pulled: a.pulled,
|
|
143
|
+
pushed: b.pushed,
|
|
144
|
+
conflicts: [...a.conflicts, ...b.conflicts],
|
|
145
|
+
skipped: a.skipped + b.skipped,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
async status() {
|
|
149
|
+
const all = await this.store.listItems();
|
|
150
|
+
const by = (s) => all.filter((i) => i.syncState === s).length;
|
|
151
|
+
return {
|
|
152
|
+
total: all.length,
|
|
153
|
+
new: by('new'),
|
|
154
|
+
dirty: by('dirty'),
|
|
155
|
+
synced: by('synced'),
|
|
156
|
+
conflict: by('conflict'),
|
|
157
|
+
deleted: by('deleted'),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/** Items currently in conflict (local + remote both changed). */
|
|
161
|
+
async listConflicts() {
|
|
162
|
+
return this.store.listItems({ syncState: 'conflict' });
|
|
163
|
+
}
|
|
164
|
+
/** Resolve one conflicted item by keeping the remote copy or the local copy. */
|
|
165
|
+
async resolveConflict(id, strategy) {
|
|
166
|
+
const local = await this.store.getItem(id);
|
|
167
|
+
if (!local)
|
|
168
|
+
return 'not-found';
|
|
169
|
+
if (strategy === 'remote') {
|
|
170
|
+
const detail = await this.client.knowledge.get(id);
|
|
171
|
+
let content = detail.content;
|
|
172
|
+
if (content && this.encryptor?.isEncrypted(content))
|
|
173
|
+
content = this.encryptor.decrypt(content);
|
|
174
|
+
await this.store.upsertItem({
|
|
175
|
+
id: detail.id,
|
|
176
|
+
vaultId: detail.vaultId,
|
|
177
|
+
title: detail.title,
|
|
178
|
+
type: detail.type,
|
|
179
|
+
contentHash: contentHash(content, detail.title, detail.tags ?? []),
|
|
180
|
+
content,
|
|
181
|
+
tags: detail.tags ?? [],
|
|
182
|
+
updatedAt: detail.updatedAt,
|
|
183
|
+
syncState: 'synced',
|
|
184
|
+
remoteVersion: detail.version,
|
|
185
|
+
});
|
|
186
|
+
return 'updated-from-remote';
|
|
187
|
+
}
|
|
188
|
+
// keep local → mark dirty so the next push overwrites the remote (last-writer)
|
|
189
|
+
await this.store.upsertItem({ ...local, syncState: 'dirty' });
|
|
190
|
+
return 'kept-local';
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Recovery: re-pull authoritative cloud state to repair the local store
|
|
194
|
+
* (overwrites non-dirty items, preserves dirty-vs-advanced as conflicts) and
|
|
195
|
+
* report any local `synced` items no longer present remotely (orphans).
|
|
196
|
+
*/
|
|
197
|
+
async recover(opts = {}) {
|
|
198
|
+
const report = { repaired: 0, conflicts: [], orphanedLocal: [] };
|
|
199
|
+
// F3 — repair + collect remoteIds across ALL pages BEFORE the orphan sweep,
|
|
200
|
+
// else a local synced item that only lives on page 2+ is wrongly reported
|
|
201
|
+
// as orphaned.
|
|
202
|
+
const remoteIds = new Set();
|
|
203
|
+
await this.forEachRemote({ vaultId: opts.vaultId, pageSize: opts.pageSize ?? 200 }, async (summary) => {
|
|
204
|
+
remoteIds.add(summary.id);
|
|
205
|
+
const detail = await this.client.knowledge.get(summary.id);
|
|
206
|
+
let content = detail.content;
|
|
207
|
+
if (content && this.encryptor?.isEncrypted(content))
|
|
208
|
+
content = this.encryptor.decrypt(content);
|
|
209
|
+
const local = await this.store.getItem(detail.id);
|
|
210
|
+
if (local?.syncState === 'dirty' && local.remoteVersion != null && detail.version > local.remoteVersion) {
|
|
211
|
+
await this.store.upsertItem({ ...local, syncState: 'conflict' });
|
|
212
|
+
report.conflicts.push(detail.id);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
await this.store.upsertItem({
|
|
216
|
+
id: detail.id,
|
|
217
|
+
vaultId: detail.vaultId,
|
|
218
|
+
title: detail.title,
|
|
219
|
+
type: detail.type,
|
|
220
|
+
contentHash: contentHash(content, detail.title, detail.tags ?? []),
|
|
221
|
+
content,
|
|
222
|
+
tags: detail.tags ?? [],
|
|
223
|
+
updatedAt: detail.updatedAt,
|
|
224
|
+
syncState: 'synced',
|
|
225
|
+
remoteVersion: detail.version,
|
|
226
|
+
});
|
|
227
|
+
report.repaired++;
|
|
228
|
+
});
|
|
229
|
+
// orphan sweep only after the FULL remote set is known
|
|
230
|
+
for (const it of await this.store.listItems({ syncState: 'synced' })) {
|
|
231
|
+
if (!remoteIds.has(it.id))
|
|
232
|
+
report.orphanedLocal.push(it.id);
|
|
233
|
+
}
|
|
234
|
+
return report;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Drain the capture outbox to the cloud (FEAT_AgentMemoryLocalOutbox).
|
|
238
|
+
*
|
|
239
|
+
* Each claimed entry's sealed content lives in `items`; we create it via the
|
|
240
|
+
* existing `knowledge.create` path (the batch endpoint is not yet shipped),
|
|
241
|
+
* stamping the idempotency key into item metadata for best-effort server-side
|
|
242
|
+
* dedup. Idempotency is enforced LOCALLY: a `done` entry is never reclaimed,
|
|
243
|
+
* and re-enqueue is a no-op on the idempotency-key PK. The only double-create
|
|
244
|
+
* window is "create succeeded but its ack was lost before we marked done" —
|
|
245
|
+
* acceptable until the content-hash-dedup batch endpoint lands.
|
|
246
|
+
*
|
|
247
|
+
* Resilience: claims `pending` (whose backoff elapsed) plus `inflight` whose
|
|
248
|
+
* lease expired (crash recovery). Transient failures back off exponentially;
|
|
249
|
+
* after `maxAttempts` an entry dead-letters (visible via `outboxStats`). This
|
|
250
|
+
* is what makes the orphaned-pending-captures leak structurally impossible —
|
|
251
|
+
* a capture is a committed row drained by an idempotent, retrying consumer.
|
|
252
|
+
*/
|
|
253
|
+
async flushOutbox(opts = {}) {
|
|
254
|
+
if (!supportsOutbox(this.store)) {
|
|
255
|
+
throw new Error('flushOutbox requires a store that supports the capture outbox (use the default SQLite store).');
|
|
256
|
+
}
|
|
257
|
+
const store = this.store;
|
|
258
|
+
const nowFn = opts.now ?? (() => Date.now());
|
|
259
|
+
const limit = opts.limit ?? 100;
|
|
260
|
+
const leaseMs = opts.leaseMs ?? 60_000;
|
|
261
|
+
const maxAttempts = opts.maxAttempts ?? 5;
|
|
262
|
+
const baseBackoff = opts.baseBackoffMs ?? 1_000;
|
|
263
|
+
const maxBackoff = opts.maxBackoffMs ?? 5 * 60_000;
|
|
264
|
+
const report = { claimed: 0, sent: 0, failed: 0, dead: 0, conflicts: [] };
|
|
265
|
+
const nowMs = nowFn();
|
|
266
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
267
|
+
const leaseIso = new Date(nowMs + leaseMs).toISOString();
|
|
268
|
+
const claimed = await store.claimOutbox({ now: nowIso, leaseUntil: leaseIso, limit });
|
|
269
|
+
report.claimed = claimed.length;
|
|
270
|
+
for (const entry of claimed) {
|
|
271
|
+
const item = await this.store.getItem(entry.itemId);
|
|
272
|
+
if (!item) {
|
|
273
|
+
await store.updateOutbox(entry.idempotencyKey, {
|
|
274
|
+
state: 'dead',
|
|
275
|
+
attempts: entry.attempts + 1,
|
|
276
|
+
lastAttemptAt: nowIso,
|
|
277
|
+
lastError: 'orphaned: sealed item not found',
|
|
278
|
+
});
|
|
279
|
+
report.dead++;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const wire = item.content != null && this.encryptor ? this.encryptor.encrypt(item.content) : item.content;
|
|
284
|
+
const created = await this.client.knowledge.create({
|
|
285
|
+
vaultId: item.vaultId,
|
|
286
|
+
title: item.title,
|
|
287
|
+
content: wire,
|
|
288
|
+
type: item.type,
|
|
289
|
+
tags: item.tags,
|
|
290
|
+
metadata: {
|
|
291
|
+
'knowz-agent-key': entry.idempotencyKey,
|
|
292
|
+
'knowz-agent-session': entry.sessionId,
|
|
293
|
+
...(opts.source ? { 'knowz-agent-source': opts.source } : {}),
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
if (created.id !== item.id)
|
|
297
|
+
await this.store.reassignItemId(item.id, created.id);
|
|
298
|
+
await this.store.upsertItem({
|
|
299
|
+
...item,
|
|
300
|
+
id: created.id,
|
|
301
|
+
syncState: 'synced',
|
|
302
|
+
remoteVersion: created.version,
|
|
303
|
+
contentHash: contentHash(item.content, item.title, item.tags),
|
|
304
|
+
});
|
|
305
|
+
await store.updateOutbox(entry.idempotencyKey, {
|
|
306
|
+
state: 'done',
|
|
307
|
+
attempts: entry.attempts + 1,
|
|
308
|
+
lastAttemptAt: nowIso,
|
|
309
|
+
lastError: undefined,
|
|
310
|
+
itemId: created.id,
|
|
311
|
+
});
|
|
312
|
+
report.sent++;
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
const attempts = entry.attempts + 1;
|
|
316
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
317
|
+
if (attempts >= maxAttempts) {
|
|
318
|
+
await store.updateOutbox(entry.idempotencyKey, {
|
|
319
|
+
state: 'dead',
|
|
320
|
+
attempts,
|
|
321
|
+
lastAttemptAt: nowIso,
|
|
322
|
+
lastError: msg,
|
|
323
|
+
});
|
|
324
|
+
report.dead++;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
const backoff = Math.min(maxBackoff, baseBackoff * 2 ** (attempts - 1));
|
|
328
|
+
await store.updateOutbox(entry.idempotencyKey, {
|
|
329
|
+
state: 'pending',
|
|
330
|
+
attempts,
|
|
331
|
+
lastAttemptAt: nowIso,
|
|
332
|
+
lastError: msg,
|
|
333
|
+
leaseUntil: new Date(nowMs + backoff).toISOString(),
|
|
334
|
+
});
|
|
335
|
+
report.failed++;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return report;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=sync-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../src/sync-engine.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export type SyncState = 'new' | 'synced' | 'dirty' | 'conflict' | 'deleted';
|
|
2
|
+
export interface LocalItem {
|
|
3
|
+
id: string;
|
|
4
|
+
vaultId?: string;
|
|
5
|
+
title: string;
|
|
6
|
+
type: string;
|
|
7
|
+
contentHash: string;
|
|
8
|
+
content?: string;
|
|
9
|
+
tags: string[];
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
syncState: SyncState;
|
|
12
|
+
remoteVersion?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface Chunk {
|
|
15
|
+
id: string;
|
|
16
|
+
itemId: string;
|
|
17
|
+
ordinal: number;
|
|
18
|
+
text: string;
|
|
19
|
+
startByte?: number;
|
|
20
|
+
endByte?: number;
|
|
21
|
+
lang?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface FileState {
|
|
24
|
+
path: string;
|
|
25
|
+
hash: string;
|
|
26
|
+
mtimeMs: number;
|
|
27
|
+
size: number;
|
|
28
|
+
itemId?: string;
|
|
29
|
+
lastIndexedAt?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface KnnHit {
|
|
32
|
+
chunk: Chunk;
|
|
33
|
+
distance: number;
|
|
34
|
+
}
|
|
35
|
+
export interface LocalStore {
|
|
36
|
+
readonly driver: 'sqlite' | 'postgres';
|
|
37
|
+
init(): Promise<void>;
|
|
38
|
+
upsertItem(item: LocalItem): Promise<void>;
|
|
39
|
+
getItem(id: string): Promise<LocalItem | undefined>;
|
|
40
|
+
reassignItemId(oldId: string, newId: string): Promise<void>;
|
|
41
|
+
listItems(filter?: {
|
|
42
|
+
vaultId?: string;
|
|
43
|
+
syncState?: SyncState;
|
|
44
|
+
}): Promise<LocalItem[]>;
|
|
45
|
+
deleteItem(id: string): Promise<void>;
|
|
46
|
+
putChunks(itemId: string, chunks: Chunk[], vectors?: Float32Array[]): Promise<void>;
|
|
47
|
+
knnSearch(queryVec: Float32Array, k: number, filter?: {
|
|
48
|
+
vaultId?: string;
|
|
49
|
+
}): Promise<KnnHit[]>;
|
|
50
|
+
setFileState(s: FileState): Promise<void>;
|
|
51
|
+
getFileState(path: string): Promise<FileState | undefined>;
|
|
52
|
+
listFileStates(): Promise<FileState[]>;
|
|
53
|
+
meta(): Promise<{
|
|
54
|
+
schemaVersion: number;
|
|
55
|
+
dim: number;
|
|
56
|
+
itemCount: number;
|
|
57
|
+
chunkCount: number;
|
|
58
|
+
}>;
|
|
59
|
+
close(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
export interface OpenStoreOptions {
|
|
62
|
+
driver?: 'sqlite' | 'postgres';
|
|
63
|
+
dbPath?: string;
|
|
64
|
+
dsn?: string;
|
|
65
|
+
dim?: number;
|
|
66
|
+
}
|
|
67
|
+
export declare const SCHEMA_VERSION = 2;
|
|
68
|
+
export declare const DEFAULT_DIM = 384;
|
|
69
|
+
/**
|
|
70
|
+
* Map an L2 distance between L2-normalized vectors to a 0..1 cosine-similarity
|
|
71
|
+
* "relevance". For unit vectors L2² = 2(1 − cos), so cos = 1 − d²/2. Identical
|
|
72
|
+
* vectors → 1; orthogonal (d=√2) → 0. Use this for any "% match" display.
|
|
73
|
+
*/
|
|
74
|
+
export declare function relevanceFromDistance(distance: number): number;
|
|
75
|
+
/** Lifecycle of a capture_outbox entry. `done`/`dead` are terminal. */
|
|
76
|
+
export type OutboxState = 'pending' | 'inflight' | 'done' | 'dead' | 'conflict';
|
|
77
|
+
/** A raw, already-redacted observation of one tool-use / agent event. */
|
|
78
|
+
export interface Observation {
|
|
79
|
+
id: string;
|
|
80
|
+
sessionId: string;
|
|
81
|
+
/** ISO timestamp. */
|
|
82
|
+
ts: string;
|
|
83
|
+
/** Event kind, e.g. 'tool', 'prompt', 'precompact'. */
|
|
84
|
+
kind: string;
|
|
85
|
+
toolName?: string;
|
|
86
|
+
/** One-line, redaction-safe summary. Never raw secret-bearing payload. */
|
|
87
|
+
summary: string;
|
|
88
|
+
/** True once the source payload passed through the redactor. */
|
|
89
|
+
redacted: boolean;
|
|
90
|
+
vaultId?: string;
|
|
91
|
+
/** Set once this observation has been rolled into a sealed `items` row. */
|
|
92
|
+
sealedIntoItemId?: string;
|
|
93
|
+
}
|
|
94
|
+
/** Durable delivery record for one sealed observation item bound for the cloud. */
|
|
95
|
+
export interface OutboxEntry {
|
|
96
|
+
/** sha256(sessionId:seq:contentHash) — stable PK, dedupes re-enqueue. */
|
|
97
|
+
idempotencyKey: string;
|
|
98
|
+
/** FK → items.id (the sealed observation content). */
|
|
99
|
+
itemId: string;
|
|
100
|
+
sessionId: string;
|
|
101
|
+
createdAt: string;
|
|
102
|
+
attempts: number;
|
|
103
|
+
lastAttemptAt?: string;
|
|
104
|
+
state: OutboxState;
|
|
105
|
+
lastError?: string;
|
|
106
|
+
/** ISO not-before: claim ignores entries leased into the future. */
|
|
107
|
+
leaseUntil?: string;
|
|
108
|
+
}
|
|
109
|
+
export interface OutboxStats {
|
|
110
|
+
pending: number;
|
|
111
|
+
inflight: number;
|
|
112
|
+
done: number;
|
|
113
|
+
dead: number;
|
|
114
|
+
conflict: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Capture/outbox surface, implemented by SqliteStore (the default local store).
|
|
118
|
+
* Kept separate from LocalStore so the Postgres driver is unaffected for now
|
|
119
|
+
* (agent-memory capture targets the default SQLite store; pg outbox is a
|
|
120
|
+
* follow-up). Use `supportsOutbox(store)` to narrow a LocalStore safely.
|
|
121
|
+
*/
|
|
122
|
+
export interface ObservationOutboxStore {
|
|
123
|
+
recordObservation(o: Observation): Promise<void>;
|
|
124
|
+
listObservations(filter?: {
|
|
125
|
+
sessionId?: string;
|
|
126
|
+
sealed?: boolean;
|
|
127
|
+
}): Promise<Observation[]>;
|
|
128
|
+
markObservationsSealed(ids: string[], itemId: string): Promise<void>;
|
|
129
|
+
/** Insert-or-ignore on idempotencyKey, so a re-enqueue is a no-op. */
|
|
130
|
+
enqueueOutbox(e: OutboxEntry): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Atomically claim up to `limit` deliverable entries — `pending` whose lease
|
|
133
|
+
* has elapsed plus `inflight` whose lease expired (crash recovery) — flipping
|
|
134
|
+
* each to `inflight` with a fresh lease. Returns the claimed entries.
|
|
135
|
+
*/
|
|
136
|
+
claimOutbox(opts: {
|
|
137
|
+
now: string;
|
|
138
|
+
leaseUntil: string;
|
|
139
|
+
limit: number;
|
|
140
|
+
}): Promise<OutboxEntry[]>;
|
|
141
|
+
updateOutbox(key: string, patch: Partial<Omit<OutboxEntry, 'idempotencyKey'>>): Promise<void>;
|
|
142
|
+
getOutbox(key: string): Promise<OutboxEntry | undefined>;
|
|
143
|
+
listOutbox(filter?: {
|
|
144
|
+
state?: OutboxState;
|
|
145
|
+
}): Promise<OutboxEntry[]>;
|
|
146
|
+
outboxStats(): Promise<OutboxStats>;
|
|
147
|
+
}
|
|
148
|
+
/** Runtime narrow: does this LocalStore also expose the capture/outbox surface? */
|
|
149
|
+
export declare function supportsOutbox(store: unknown): store is LocalStore & ObservationOutboxStore;
|
|
150
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const SCHEMA_VERSION = 2;
|
|
2
|
+
// Local store holds LOCALLY-computed embeddings only (the local hashing
|
|
3
|
+
// embedder, dim 384). Cloud (1536-dim) vectors are never stored here — synced
|
|
4
|
+
// content is re-embedded locally for KNN. Keep this aligned with the indexer's
|
|
5
|
+
// DEFAULT_EMBED_DIM so every command opens the store at the same dim.
|
|
6
|
+
export const DEFAULT_DIM = 384;
|
|
7
|
+
/**
|
|
8
|
+
* Map an L2 distance between L2-normalized vectors to a 0..1 cosine-similarity
|
|
9
|
+
* "relevance". For unit vectors L2² = 2(1 − cos), so cos = 1 − d²/2. Identical
|
|
10
|
+
* vectors → 1; orthogonal (d=√2) → 0. Use this for any "% match" display.
|
|
11
|
+
*/
|
|
12
|
+
export function relevanceFromDistance(distance) {
|
|
13
|
+
return Math.max(0, Math.min(1, 1 - (distance * distance) / 2));
|
|
14
|
+
}
|
|
15
|
+
/** Runtime narrow: does this LocalStore also expose the capture/outbox surface? */
|
|
16
|
+
export function supportsOutbox(store) {
|
|
17
|
+
return (!!store &&
|
|
18
|
+
typeof store.claimOutbox === 'function' &&
|
|
19
|
+
typeof store.enqueueOutbox === 'function');
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA+DA,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAChC,wEAAwE;AACxE,8EAA8E;AAC9E,+EAA+E;AAC/E,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAE/B;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC;AA+ED,mFAAmF;AACnF,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,CACL,CAAC,CAAC,KAAK;QACP,OAAQ,KAAgC,CAAC,WAAW,KAAK,UAAU;QACnE,OAAQ,KAAgC,CAAC,aAAa,KAAK,UAAU,CACtE,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@knowzai/local-store",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"better-sqlite3": "^11.7.0",
|
|
18
|
+
"sqlite-vec": "^0.1.6",
|
|
19
|
+
"@knowzai/types": "0.1.0",
|
|
20
|
+
"@knowzai/api-client": "0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"optionalDependencies": {
|
|
23
|
+
"pg": "^8.13.1"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": "Knowz",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/knowz-io/knowz-platform.git",
|
|
30
|
+
"directory": "cli/local-store"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=20"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -b"
|
|
40
|
+
}
|
|
41
|
+
}
|