eklavya 1.18.2 → 1.19.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.
Files changed (98) hide show
  1. package/dist/assets/dashboard.html +559 -14
  2. package/dist/assets/tutor/references/focus-and-level.md +29 -0
  3. package/dist/cli.js +648 -20
  4. package/dist/cli.js.map +1 -1
  5. package/dist/config-path.js +156 -0
  6. package/dist/config-path.js.map +1 -0
  7. package/dist/config.js +231 -2
  8. package/dist/config.js.map +1 -1
  9. package/dist/dashboard.js +355 -0
  10. package/dist/dashboard.js.map +1 -1
  11. package/dist/eval/retrieval-score.js +77 -0
  12. package/dist/eval/retrieval-score.js.map +1 -0
  13. package/dist/hooks/capture-tool.js +107 -0
  14. package/dist/hooks/capture-tool.js.map +1 -0
  15. package/dist/hooks/checkpoint-quiz.js +5 -6
  16. package/dist/hooks/checkpoint-quiz.js.map +1 -1
  17. package/dist/hooks/lib.js +7 -33
  18. package/dist/hooks/lib.js.map +1 -1
  19. package/dist/hooks/memory-lib.js +181 -0
  20. package/dist/hooks/memory-lib.js.map +1 -0
  21. package/dist/hooks/prompt-submit-nudge.js +56 -20
  22. package/dist/hooks/prompt-submit-nudge.js.map +1 -1
  23. package/dist/hooks/session-start.js +73 -74
  24. package/dist/hooks/session-start.js.map +1 -1
  25. package/dist/hooks/stop-quiz-check.js +44 -7
  26. package/dist/hooks/stop-quiz-check.js.map +1 -1
  27. package/dist/install.js +37 -0
  28. package/dist/install.js.map +1 -1
  29. package/dist/memory/capture.js +123 -0
  30. package/dist/memory/capture.js.map +1 -0
  31. package/dist/memory/code.js +186 -0
  32. package/dist/memory/code.js.map +1 -0
  33. package/dist/memory/collections.js +87 -0
  34. package/dist/memory/collections.js.map +1 -0
  35. package/dist/memory/embed.js +81 -0
  36. package/dist/memory/embed.js.map +1 -0
  37. package/dist/memory/hosts.js +71 -0
  38. package/dist/memory/hosts.js.map +1 -0
  39. package/dist/memory/identity.js +74 -0
  40. package/dist/memory/identity.js.map +1 -0
  41. package/dist/memory/import.js +916 -0
  42. package/dist/memory/import.js.map +1 -0
  43. package/dist/memory/learning.js +148 -0
  44. package/dist/memory/learning.js.map +1 -0
  45. package/dist/memory/notify.js +220 -0
  46. package/dist/memory/notify.js.map +1 -0
  47. package/dist/memory/privacy.js +117 -0
  48. package/dist/memory/privacy.js.map +1 -0
  49. package/dist/memory/provider.js +155 -0
  50. package/dist/memory/provider.js.map +1 -0
  51. package/dist/memory/recall.js +327 -0
  52. package/dist/memory/recall.js.map +1 -0
  53. package/dist/memory/replay.js +174 -0
  54. package/dist/memory/replay.js.map +1 -0
  55. package/dist/memory/search.js +143 -0
  56. package/dist/memory/search.js.map +1 -0
  57. package/dist/memory/spool.js +93 -0
  58. package/dist/memory/spool.js.map +1 -0
  59. package/dist/memory/store.js +399 -0
  60. package/dist/memory/store.js.map +1 -0
  61. package/dist/memory/summarize.js +178 -0
  62. package/dist/memory/summarize.js.map +1 -0
  63. package/dist/memory/sync.js +595 -0
  64. package/dist/memory/sync.js.map +1 -0
  65. package/dist/memory/tokens.js +55 -0
  66. package/dist/memory/tokens.js.map +1 -0
  67. package/dist/memory/worker.js +207 -0
  68. package/dist/memory/worker.js.map +1 -0
  69. package/dist/migrations/009_memory.sql +226 -0
  70. package/dist/migrations/010_import.sql +26 -0
  71. package/dist/migrations/011_sync.sql +76 -0
  72. package/dist/migrations/012_job_backoff.sql +13 -0
  73. package/dist/migrations/013_batch_provenance.sql +18 -0
  74. package/dist/migrations/014_batch_events_index.sql +18 -0
  75. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  76. package/dist/plugin/agents/tutor.md +3 -1
  77. package/dist/plugin/hooks/CLAUDE.md +30 -6
  78. package/dist/plugin/hooks/hooks.json +10 -0
  79. package/dist/plugin/skills/CLAUDE.md +17 -7
  80. package/dist/plugin/skills/memory/SKILL.md +88 -0
  81. package/dist/plugin/skills/setup/SKILL.md +16 -2
  82. package/dist/plugin/skills/tutor/references/focus-and-level.md +29 -0
  83. package/dist/time.js +41 -0
  84. package/dist/time.js.map +1 -0
  85. package/dist/tools/code_tools.js +73 -0
  86. package/dist/tools/code_tools.js.map +1 -0
  87. package/dist/tools/collection_tools.js +92 -0
  88. package/dist/tools/collection_tools.js.map +1 -0
  89. package/dist/tools/config_tools.js +104 -1
  90. package/dist/tools/config_tools.js.map +1 -1
  91. package/dist/tools/index.js +15 -0
  92. package/dist/tools/index.js.map +1 -1
  93. package/dist/tools/memory_read_tools.js +277 -0
  94. package/dist/tools/memory_read_tools.js.map +1 -0
  95. package/dist/tools/memory_write_tools.js +115 -0
  96. package/dist/tools/memory_write_tools.js.map +1 -0
  97. package/dist/user-skill/eklavya/SKILL.md +64 -8
  98. package/package.json +2 -1
@@ -0,0 +1,595 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { nowIso } from '../time.js';
5
+ import { deleteEntry, indexVector } from './store.js';
6
+ /**
7
+ * Multi-device sync (ADR-09): a shared **directory**, not a server.
8
+ *
9
+ * The whole protocol is files on disk. Each device appends numbered revision
10
+ * records under its own folder and reads what the others wrote; Dropbox, iCloud
11
+ * Drive, Syncthing, a mounted share or a git repository all work because none of
12
+ * them is a dependency -- the target is a path. The hosted, authenticated team
13
+ * server is deliberately not built, and ADR-09 is where that is recorded.
14
+ *
15
+ * <target>/devices/<device_id>/000000000001.json
16
+ *
17
+ * One file per record, named by revision, zero-padded so lexical order is
18
+ * numeric order. That shape buys three properties nothing else here has to work
19
+ * for. A pull is incremental, because the reader skips filenames at or below the
20
+ * high-water mark without opening them. A push resumes, because a record's path
21
+ * is a function of (device, revision) and a file already there is already sent.
22
+ * And a reader never sees half a record, because every write is temp-then-rename
23
+ * -- with a payload hash as the second line of defence for the cases rename does
24
+ * not cover, such as a cloud client materialising a partial file under the real
25
+ * name.
26
+ *
27
+ * **What crosses, and what must not.** Memory entries, their tags and their
28
+ * tombstones. Not attempts, not mastery, not gates, not receipts, not raw
29
+ * `evidence_events` (PRD SEC-02). A team that points every member at one shared
30
+ * folder gets shared project memory; it does not thereby get everyone's quiz
31
+ * history, and "the schema happens not to mention it" is not an enforcement.
32
+ * `ENTRY_COLUMNS` is, and it is the only list any payload is built from or
33
+ * applied through.
34
+ */
35
+ /** The wire format. Bump when the record shape changes. */
36
+ export const SYNC_FORMAT_VERSION = 1;
37
+ /**
38
+ * The only columns that ever leave this machine.
39
+ *
40
+ * An allowlist rather than `SELECT *`, so a later migration adding a column to
41
+ * `memory_entries` does not silently start publishing it. `session_id`,
42
+ * `batch_id` and `superseded_by` are left out for a different reason: they are
43
+ * local row ids and mean nothing on the other device.
44
+ */
45
+ const ENTRY_COLUMNS = [
46
+ 'project',
47
+ 'kind',
48
+ 'type',
49
+ 'title',
50
+ 'narrative',
51
+ 'facts',
52
+ 'files',
53
+ 'generator',
54
+ 'confidence',
55
+ 'occurred_at',
56
+ 'created_at',
57
+ 'import_source',
58
+ ];
59
+ const DEVICE_ID_KEY = 'sync_device_id';
60
+ /**
61
+ * This install's identity, generated once.
62
+ *
63
+ * Kept in `meta` rather than in the config file, and the difference matters.
64
+ * Config travels: `.eklavya.json` is committed, and `~/.eklavya/config.json` is
65
+ * what a dotfile manager copies to the second machine. Two devices sharing an id
66
+ * would interleave one revision stream and each would read the other's writes as
67
+ * its own already-applied history -- a silent, unrecoverable merge. `meta` lives
68
+ * in `knowledge.db`, which is per-install, and which the importer is already
69
+ * forbidden to copy foreign device identity into (PRD MIG-01).
70
+ *
71
+ * `sync.device_id` in the config overrides it when someone means to pin it.
72
+ */
73
+ /**
74
+ * A device id becomes a directory name under the sync target, so it has to be
75
+ * one path segment and nothing else. Without this, a pinned
76
+ * `device_id: "../../../../tmp/evil"` writes outside the target — `path.join`
77
+ * resolves it happily and `mkdirSync(..., {recursive: true})` creates whatever
78
+ * it names.
79
+ */
80
+ const DEVICE_ID = /^[A-Za-z0-9_-]{1,64}$/;
81
+ export function deviceId(db, config) {
82
+ const pinned = config.sync.device_id?.trim();
83
+ if (pinned) {
84
+ if (!DEVICE_ID.test(pinned)) {
85
+ throw new Error(`sync.device_id must be 1-64 characters of letters, digits, "-" or "_" (got ${JSON.stringify(pinned)}). It names a directory under the sync target.`);
86
+ }
87
+ return pinned;
88
+ }
89
+ const row = db.prepare('SELECT value FROM meta WHERE key = ?').get(DEVICE_ID_KEY);
90
+ if (row?.value)
91
+ return row.value;
92
+ const minted = crypto.randomUUID().replace(/-/g, '').slice(0, 16);
93
+ db.prepare('INSERT OR IGNORE INTO meta (key, value) VALUES (?, ?)').run(DEVICE_ID_KEY, minted);
94
+ return (db.prepare('SELECT value FROM meta WHERE key = ?').get(DEVICE_ID_KEY).value);
95
+ }
96
+ /** Resolves the target, honouring an explicit `--target` over the config. */
97
+ function resolveTarget(config, override) {
98
+ const chosen = override?.trim() || config.sync.target?.trim();
99
+ return chosen ? path.resolve(chosen) : null;
100
+ }
101
+ function guard(db, config, override) {
102
+ if (!config.sync.enabled)
103
+ return { ok: false, reason: 'disabled' };
104
+ const target = resolveTarget(config, override);
105
+ if (!target)
106
+ return { ok: false, reason: 'no-target' };
107
+ return { ok: true, device: deviceId(db, config), target };
108
+ }
109
+ // ---------------------------------------------------------------------------
110
+ // Hashing and the version vector
111
+ // ---------------------------------------------------------------------------
112
+ /** Content identity of a version. Equal hashes mean nothing has to be sent. */
113
+ function payloadHash(op, entry, tags) {
114
+ const h = crypto.createHash('sha256');
115
+ // Canonical by construction: ENTRY_COLUMNS is ordered and tags are sorted, so
116
+ // two devices holding the same content always agree on the hash.
117
+ h.update(JSON.stringify([op, entry ? ENTRY_COLUMNS.map((c) => entry[c] ?? null) : null, [...tags].sort()]));
118
+ return h.digest('hex').slice(0, 32);
119
+ }
120
+ function ref(device, revision) {
121
+ return `${device}:${revision}`;
122
+ }
123
+ function localRecord(db, entryUid) {
124
+ return db.prepare('SELECT * FROM sync_records WHERE entry_uid = ?').get(entryUid);
125
+ }
126
+ function highWater(db, device) {
127
+ const row = db.prepare('SELECT last_revision FROM sync_state WHERE device_id = ?').get(device);
128
+ return row?.last_revision ?? 0;
129
+ }
130
+ function setHighWater(db, device, revision) {
131
+ db.prepare(`INSERT INTO sync_state (device_id, last_revision, last_sync_at) VALUES (?, ?, ?)
132
+ ON CONFLICT(device_id) DO UPDATE SET
133
+ last_revision = MAX(last_revision, excluded.last_revision),
134
+ last_sync_at = excluded.last_sync_at`).run(device, revision, nowIso());
135
+ }
136
+ function nextRevision(db, device) {
137
+ const next = highWater(db, device) + 1;
138
+ setHighWater(db, device, next);
139
+ return next;
140
+ }
141
+ function writeRecord(db, rec) {
142
+ db.prepare(`INSERT INTO sync_records (entry_uid, device_id, revision, base, op, hash, updated_at)
143
+ VALUES (?, ?, ?, ?, ?, ?, ?)
144
+ ON CONFLICT(entry_uid) DO UPDATE SET
145
+ device_id = excluded.device_id, revision = excluded.revision, base = excluded.base,
146
+ op = excluded.op, hash = excluded.hash, updated_at = excluded.updated_at`).run(rec.entry_uid, rec.device_id, rec.revision, rec.base, rec.op, rec.hash, nowIso());
147
+ }
148
+ function tombstone(entryUid) {
149
+ return { entry_uid: entryUid, op: 'delete', entry: null, tags: [], hash: payloadHash('delete', null, []) };
150
+ }
151
+ function tagsOf(db, entryUid) {
152
+ return db
153
+ .prepare('SELECT tag FROM memory_entry_tags t JOIN memory_entries e ON e.id = t.entry_id WHERE e.entry_uid = ? ORDER BY tag')
154
+ .all(entryUid).map((t) => t.tag);
155
+ }
156
+ function stage(db, row) {
157
+ if (row.deleted_at !== null)
158
+ return tombstone(row.entry_uid);
159
+ const entry = Object.fromEntries(ENTRY_COLUMNS.map((c) => [c, row[c] ?? null]));
160
+ const tags = tagsOf(db, row.entry_uid);
161
+ return { entry_uid: row.entry_uid, op: 'upsert', entry, tags, hash: payloadHash('upsert', entry, tags) };
162
+ }
163
+ /**
164
+ * What this device holds for one entry right now, whether or not it has been
165
+ * staged. Null when the entry is unknown here in every sense.
166
+ */
167
+ function localContent(db, entryUid) {
168
+ const row = db
169
+ .prepare(`SELECT entry_uid, deleted_at, ${ENTRY_COLUMNS.join(', ')} FROM memory_entries WHERE entry_uid = ?`)
170
+ .get(entryUid);
171
+ if (row)
172
+ return stage(db, row);
173
+ // No row, but a record that outlived it: a hard delete. Still a tombstone.
174
+ return localRecord(db, entryUid) ? tombstone(entryUid) : null;
175
+ }
176
+ /**
177
+ * What a push would send: every entry whose content no longer matches its
178
+ * record, plus tombstones.
179
+ *
180
+ * ponytail: a full scan of `memory_entries` per push rather than a write-time
181
+ * hook, because a hook means every call site in `memory/store.ts` remembering to
182
+ * call it and a corpus this size is a millisecond. Add the hook if a push ever
183
+ * shows up in a profile.
184
+ */
185
+ function scanLocal(db) {
186
+ const rows = db
187
+ .prepare(`SELECT entry_uid, deleted_at, ${ENTRY_COLUMNS.join(', ')} FROM memory_entries`)
188
+ .all();
189
+ const out = rows
190
+ .map((row) => stage(db, row))
191
+ .filter((s) => localRecord(db, s.entry_uid)?.hash !== s.hash);
192
+ // A hard delete leaves no row at all, so the tombstone has to come from the
193
+ // record that outlived it. Without this the other device keeps its copy for
194
+ // ever and a later edit there resurrects it here.
195
+ const orphans = db
196
+ .prepare(`SELECT entry_uid FROM sync_records
197
+ WHERE op = 'upsert'
198
+ AND entry_uid NOT IN (SELECT entry_uid FROM memory_entries)`)
199
+ .all();
200
+ for (const o of orphans)
201
+ out.push(tombstone(o.entry_uid));
202
+ return out;
203
+ }
204
+ // ---------------------------------------------------------------------------
205
+ // The directory
206
+ // ---------------------------------------------------------------------------
207
+ function deviceDir(target, device) {
208
+ // Belt and braces with the check in `deviceId`: every path built here is one
209
+ // segment under `<target>/devices`, and a peer's directory name arrives from
210
+ // `readdirSync` rather than from a record, but a single assertion at the one
211
+ // place paths are built is cheaper than trusting both.
212
+ if (!DEVICE_ID.test(device))
213
+ throw new Error(`unsafe device id: ${JSON.stringify(device)}`);
214
+ return path.join(target, 'devices', device);
215
+ }
216
+ function recordPath(target, device, revision) {
217
+ return path.join(deviceDir(target, device), `${String(revision).padStart(12, '0')}.json`);
218
+ }
219
+ /** Temp name then rename: a reader must never see a half-written record. */
220
+ function writeAtomic(file, text) {
221
+ fs.mkdirSync(path.dirname(file), { recursive: true });
222
+ const tmp = `${file}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
223
+ fs.writeFileSync(tmp, text, 'utf8');
224
+ fs.renameSync(tmp, file);
225
+ }
226
+ // ---------------------------------------------------------------------------
227
+ // Push
228
+ // ---------------------------------------------------------------------------
229
+ /**
230
+ * Stages local changes as new revisions and writes any local record the target
231
+ * has not got.
232
+ *
233
+ * Both halves are idempotent. Staging skips an entry whose hash already matches
234
+ * its record, and writing skips a path that already exists -- so a push killed
235
+ * halfway resumes by writing only the remainder, and a push with nothing to do
236
+ * writes nothing at all.
237
+ */
238
+ export function push(db, config, opts = {}) {
239
+ const g = guard(db, config, opts.target);
240
+ if (!g.ok)
241
+ return { ok: false, reason: g.reason, staged: 0, written: 0, already: 0 };
242
+ const staged = scanLocal(db);
243
+ db.transaction(() => {
244
+ for (const change of staged) {
245
+ const previous = localRecord(db, change.entry_uid);
246
+ writeRecord(db, {
247
+ entry_uid: change.entry_uid,
248
+ device_id: g.device,
249
+ revision: nextRevision(db, g.device),
250
+ base: previous ? ref(previous.device_id, previous.revision) : null,
251
+ op: change.op,
252
+ hash: change.hash,
253
+ });
254
+ }
255
+ })();
256
+ const payloads = new Map(staged.map((s) => [s.entry_uid, s]));
257
+ const mine = db
258
+ .prepare('SELECT * FROM sync_records WHERE device_id = ? ORDER BY revision')
259
+ .all(g.device);
260
+ let written = 0;
261
+ let already = 0;
262
+ for (const rec of mine) {
263
+ const file = recordPath(g.target, g.device, rec.revision);
264
+ if (fs.existsSync(file)) {
265
+ already += 1;
266
+ continue;
267
+ }
268
+ const body = payloads.get(rec.entry_uid);
269
+ // A record whose payload this run did not rebuild predates the interruption
270
+ // that lost its file; it is rebuilt from the database below, and dropped if
271
+ // the entry is gone -- a missing revision file is a gap the peer's reader
272
+ // stops at, which is correct: it will be filled on the next push.
273
+ const rebuilt = body ?? rebuildPayload(db, rec);
274
+ if (!rebuilt)
275
+ continue;
276
+ const record = {
277
+ v: SYNC_FORMAT_VERSION,
278
+ device_id: g.device,
279
+ revision: rec.revision,
280
+ base: rec.base,
281
+ entry_uid: rec.entry_uid,
282
+ op: rec.op,
283
+ hash: rec.hash,
284
+ written_at: nowIso(),
285
+ entry: rebuilt.entry,
286
+ tags: rebuilt.tags,
287
+ };
288
+ writeAtomic(file, `${JSON.stringify(record)}\n`);
289
+ written += 1;
290
+ }
291
+ return { ok: true, device_id: g.device, target: g.target, staged: staged.length, written, already };
292
+ }
293
+ function rebuildPayload(db, rec) {
294
+ if (rec.op === 'delete')
295
+ return { entry: null, tags: [] };
296
+ const row = db
297
+ .prepare(`SELECT ${ENTRY_COLUMNS.join(', ')} FROM memory_entries WHERE entry_uid = ?`)
298
+ .get(rec.entry_uid);
299
+ if (!row)
300
+ return null;
301
+ const tags = db
302
+ .prepare('SELECT tag FROM memory_entry_tags t JOIN memory_entries e ON e.id = t.entry_id WHERE e.entry_uid = ? ORDER BY tag')
303
+ .all(rec.entry_uid).map((t) => t.tag);
304
+ const entry = Object.fromEntries(ENTRY_COLUMNS.map((c) => [c, row[c] ?? null]));
305
+ if (payloadHash('upsert', entry, tags) !== rec.hash)
306
+ return null;
307
+ return { entry, tags };
308
+ }
309
+ // ---------------------------------------------------------------------------
310
+ // Pull
311
+ // ---------------------------------------------------------------------------
312
+ /**
313
+ * Reads a record file, or returns null if it is not a whole, self-consistent
314
+ * one.
315
+ *
316
+ * The hash check is the part that matters. A truncated file usually fails
317
+ * `JSON.parse`, but a file a cloud client flushed on a record boundary can be
318
+ * valid JSON and still be the wrong record -- so the payload is re-hashed and
319
+ * compared with the hash the writer put in it.
320
+ */
321
+ function readRecord(file, device, revision) {
322
+ let parsed;
323
+ try {
324
+ parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
325
+ }
326
+ catch {
327
+ return null;
328
+ }
329
+ if (!parsed || typeof parsed !== 'object')
330
+ return null;
331
+ const rec = parsed;
332
+ if (rec.v !== SYNC_FORMAT_VERSION)
333
+ return null;
334
+ if (rec.device_id !== device || rec.revision !== revision)
335
+ return null;
336
+ if (typeof rec.entry_uid !== 'string' || !rec.entry_uid)
337
+ return null;
338
+ if (rec.op !== 'upsert' && rec.op !== 'delete')
339
+ return null;
340
+ const tags = Array.isArray(rec.tags) ? rec.tags.filter((t) => typeof t === 'string') : [];
341
+ const entry = rec.op === 'delete' ? null : sanitizeEntry(rec.entry);
342
+ if (rec.op === 'upsert' && !entry)
343
+ return null;
344
+ if (payloadHash(rec.op, entry, tags) !== rec.hash)
345
+ return null;
346
+ return { ...rec, entry, tags, base: rec.base ?? null };
347
+ }
348
+ /**
349
+ * The inbound half of the allowlist. Anything a peer put in the payload that is
350
+ * not a synced column is dropped here rather than reaching a prepared statement
351
+ * -- a future version that starts shipping a field this one does not know about
352
+ * must not have it applied by accident.
353
+ */
354
+ function sanitizeEntry(value) {
355
+ if (!value || typeof value !== 'object')
356
+ return null;
357
+ const v = value;
358
+ if (typeof v.title !== 'string' || typeof v.project !== 'string')
359
+ return null;
360
+ return Object.fromEntries(ENTRY_COLUMNS.map((c) => [c, v[c] ?? null]));
361
+ }
362
+ function peerDevices(target, local) {
363
+ const root = path.join(target, 'devices');
364
+ try {
365
+ return fs
366
+ .readdirSync(root, { withFileTypes: true })
367
+ .filter((d) => d.isDirectory() && d.name !== local)
368
+ .map((d) => d.name)
369
+ .sort();
370
+ }
371
+ catch {
372
+ return [];
373
+ }
374
+ }
375
+ function pendingRecordFiles(target, device, after) {
376
+ let names;
377
+ try {
378
+ names = fs.readdirSync(deviceDir(target, device));
379
+ }
380
+ catch {
381
+ return [];
382
+ }
383
+ return names
384
+ .filter((n) => /^\d{12}\.json$/.test(n))
385
+ .map((n) => ({ revision: Number(n.slice(0, 12)), file: path.join(deviceDir(target, device), n) }))
386
+ .filter((r) => r.revision > after)
387
+ .sort((a, b) => a.revision - b.revision);
388
+ }
389
+ /**
390
+ * Applies what the peers wrote, one record at a time, in revision order.
391
+ *
392
+ * Each record is applied in its own transaction together with the high-water
393
+ * mark it advances, so an interrupted pull leaves a consistent database and
394
+ * resumes exactly where it stopped. An unreadable record stops that device's
395
+ * stream rather than being skipped past: skipping would advance the mark over a
396
+ * file that is merely still being written, and the record would never be read
397
+ * again.
398
+ */
399
+ export function pull(db, config, opts = {}) {
400
+ const g = guard(db, config, opts.target);
401
+ if (!g.ok) {
402
+ return { ok: false, reason: g.reason, applied: 0, tombstones: 0, skipped: 0, conflicts: 0, stalled: [] };
403
+ }
404
+ let applied = 0;
405
+ let tombstones = 0;
406
+ let skipped = 0;
407
+ let conflicts = 0;
408
+ const stalled = [];
409
+ for (const device of peerDevices(g.target, g.device)) {
410
+ for (const { revision, file } of pendingRecordFiles(g.target, device, highWater(db, device))) {
411
+ const rec = readRecord(file, device, revision);
412
+ if (!rec) {
413
+ stalled.push(device);
414
+ break;
415
+ }
416
+ const outcome = db.transaction(() => {
417
+ const result = applyRecord(db, rec);
418
+ setHighWater(db, device, revision);
419
+ return result;
420
+ })();
421
+ if (outcome === 'applied')
422
+ applied += 1;
423
+ else if (outcome === 'tombstone') {
424
+ applied += 1;
425
+ tombstones += 1;
426
+ }
427
+ else if (outcome === 'conflict')
428
+ conflicts += 1;
429
+ else
430
+ skipped += 1;
431
+ }
432
+ }
433
+ return { ok: true, device_id: g.device, target: g.target, applied, tombstones, skipped, conflicts, stalled };
434
+ }
435
+ /**
436
+ * The merge rule, and the only place a remote version reaches local state.
437
+ *
438
+ * `held` is the version this device last agreed on; `local` is what it actually
439
+ * holds now. They come apart when somebody edited here and has not pushed yet,
440
+ * and that gap is the whole point: an unstaged local edit is still a local edit,
441
+ * so comparing the incoming record against `held` alone would let a pull
442
+ * silently overwrite work whose only crime was arriving between two syncs.
443
+ *
444
+ * So: identical content, nothing to do but agree on a name for it. Nothing here
445
+ * yet, or an incoming version that descends from exactly what is held and held
446
+ * is unmodified -- a fast-forward, applied. Anything else is two devices having
447
+ * edited the same entry, which no timestamp can adjudicate: the local version
448
+ * stays live and the incoming one is quarantined whole, for `repairConflict`.
449
+ */
450
+ function applyRecord(db, rec) {
451
+ const held = localRecord(db, rec.entry_uid);
452
+ const local = localContent(db, rec.entry_uid);
453
+ if (local && local.hash === rec.hash) {
454
+ // The two devices reached the same content independently. Adopting the
455
+ // incoming version name costs nothing and lines up the next fast-forward.
456
+ settle(db, rec);
457
+ return 'skipped';
458
+ }
459
+ if (held) {
460
+ const dirty = Boolean(local) && local.hash !== held.hash;
461
+ const fastForward = !dirty &&
462
+ (rec.base === ref(held.device_id, held.revision) ||
463
+ (held.device_id === rec.device_id && rec.revision > held.revision));
464
+ if (!dirty && held.device_id === rec.device_id && rec.revision <= held.revision)
465
+ return 'skipped';
466
+ if (!fastForward) {
467
+ db.prepare(`INSERT INTO sync_conflicts (entry_uid, device_id, revision, base, local_ref, payload, detected_at)
468
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(rec.entry_uid, rec.device_id, rec.revision, rec.base, ref(held.device_id, held.revision), JSON.stringify(rec), nowIso());
469
+ return 'conflict';
470
+ }
471
+ }
472
+ else if (local) {
473
+ // No agreed version, but content here already: two independent creations
474
+ // under one uid. Rare enough to be suspicious, so it is not merged either.
475
+ db.prepare(`INSERT INTO sync_conflicts (entry_uid, device_id, revision, base, local_ref, payload, detected_at)
476
+ VALUES (?, ?, ?, ?, 'local:unstaged', ?, ?)`).run(rec.entry_uid, rec.device_id, rec.revision, rec.base, JSON.stringify(rec), nowIso());
477
+ return 'conflict';
478
+ }
479
+ const outcome = materialize(db, rec);
480
+ settle(db, rec);
481
+ return outcome;
482
+ }
483
+ /**
484
+ * Records the version now agreed on, and retires the quarantine it settles --
485
+ * a repair chosen on one device resolves the mirror-image conflict on the other
486
+ * without anybody choosing twice.
487
+ */
488
+ function settle(db, rec) {
489
+ writeRecord(db, {
490
+ entry_uid: rec.entry_uid,
491
+ device_id: rec.device_id,
492
+ revision: rec.revision,
493
+ base: rec.base,
494
+ op: rec.op,
495
+ hash: rec.hash,
496
+ });
497
+ db.prepare("UPDATE sync_conflicts SET resolved_at = ?, resolution = 'superseded' WHERE entry_uid = ? AND resolved_at IS NULL").run(nowIso(), rec.entry_uid);
498
+ }
499
+ /** Writes the entry itself. Memory tables only -- nothing here knows another. */
500
+ function materialize(db, rec) {
501
+ const existing = db.prepare('SELECT id FROM memory_entries WHERE entry_uid = ?').get(rec.entry_uid);
502
+ if (rec.op === 'delete') {
503
+ // No local row means the tombstone arrived before the entry ever did, or
504
+ // after it was hard-deleted. The record alone is enough: it is what stops a
505
+ // peer that still holds the entry from putting it back.
506
+ if (existing)
507
+ deleteEntry(db, existing.id);
508
+ return 'tombstone';
509
+ }
510
+ const entry = rec.entry;
511
+ const columns = ENTRY_COLUMNS.join(', ');
512
+ const placeholders = ENTRY_COLUMNS.map(() => '?').join(', ');
513
+ const updates = ENTRY_COLUMNS.map((c) => `${c} = excluded.${c}`).join(', ');
514
+ db.prepare(`INSERT INTO memory_entries (entry_uid, ${columns}, deleted_at) VALUES (?, ${placeholders}, NULL)
515
+ ON CONFLICT(entry_uid) DO UPDATE SET ${updates}, deleted_at = NULL`).run(rec.entry_uid, ...ENTRY_COLUMNS.map((c) => entry[c] ?? null));
516
+ const id = existing?.id ??
517
+ db.prepare('SELECT id FROM memory_entries WHERE entry_uid = ?').get(rec.entry_uid).id;
518
+ db.prepare('DELETE FROM memory_entry_tags WHERE entry_id = ?').run(id);
519
+ const tag = db.prepare('INSERT OR IGNORE INTO memory_entry_tags (entry_id, tag) VALUES (?, ?)');
520
+ for (const t of rec.tags)
521
+ tag.run(id, t.toLowerCase());
522
+ indexVector(db, id, [entry.title, entry.narrative ?? '', entry.facts ?? '', entry.files ?? ''].join('\n'));
523
+ return 'applied';
524
+ }
525
+ export function conflicts(db, includeResolved = false) {
526
+ const sql = includeResolved
527
+ ? 'SELECT * FROM sync_conflicts ORDER BY id'
528
+ : 'SELECT * FROM sync_conflicts WHERE resolved_at IS NULL ORDER BY id';
529
+ return db.prepare(sql).all();
530
+ }
531
+ /**
532
+ * Picks a side, and makes the choice travel.
533
+ *
534
+ * Either way the winner is restamped as a **new local revision whose `base` is
535
+ * the quarantined version**. That is what turns one person's decision into
536
+ * convergence: the peer holds exactly that version, so on its next pull the
537
+ * resolution is an ordinary fast-forward rather than a second conflict to
538
+ * adjudicate by hand.
539
+ */
540
+ export function repairConflict(db, config, id, side) {
541
+ const row = db.prepare('SELECT * FROM sync_conflicts WHERE id = ?').get(id);
542
+ if (!row || row.resolved_at)
543
+ return false;
544
+ const device = deviceId(db, config);
545
+ const rec = JSON.parse(row.payload);
546
+ db.transaction(() => {
547
+ if (side === 'remote')
548
+ materialize(db, rec);
549
+ // Whatever is in the database once the choice is made is what the repair
550
+ // publishes -- read back rather than assumed, because the local side may
551
+ // have been edited again since the conflict was recorded.
552
+ const winner = localContent(db, row.entry_uid) ?? { hash: rec.hash, op: rec.op };
553
+ writeRecord(db, {
554
+ entry_uid: row.entry_uid,
555
+ device_id: device,
556
+ revision: nextRevision(db, device),
557
+ base: ref(rec.device_id, rec.revision),
558
+ op: winner.op,
559
+ hash: winner.hash,
560
+ });
561
+ db.prepare('UPDATE sync_conflicts SET resolved_at = ?, resolution = ? WHERE id = ?').run(nowIso(), side, id);
562
+ })();
563
+ return true;
564
+ }
565
+ // ---------------------------------------------------------------------------
566
+ // Status
567
+ // ---------------------------------------------------------------------------
568
+ export function syncStatus(db, config, opts = {}) {
569
+ const target = resolveTarget(config, opts.target);
570
+ const base = {
571
+ ok: config.sync.enabled && Boolean(target),
572
+ enabled: config.sync.enabled,
573
+ configured_target: config.sync.target,
574
+ target: target ?? undefined,
575
+ local_revision: 0,
576
+ pending: 0,
577
+ open_conflicts: 0,
578
+ peers: [],
579
+ };
580
+ if (!config.sync.enabled)
581
+ return { ...base, reason: 'disabled' };
582
+ if (!target)
583
+ return { ...base, reason: 'no-target' };
584
+ const device = deviceId(db, config);
585
+ const peers = db.prepare('SELECT * FROM sync_state ORDER BY device_id').all().filter((p) => p.device_id !== device);
586
+ return {
587
+ ...base,
588
+ device_id: device,
589
+ local_revision: highWater(db, device),
590
+ pending: scanLocal(db).length,
591
+ open_conflicts: conflicts(db).length,
592
+ peers,
593
+ };
594
+ }
595
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/memory/sync.ts"],"names":[],"mappings":"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"}