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,399 @@
1
+ import crypto from 'node:crypto';
2
+ import { nowIso } from '../time.js';
3
+ import { embedLocal, LOCAL_DIM, LOCAL_EMBEDDER, toBlob } from './embed.js';
4
+ import { entryUid, receiptUid } from './identity.js';
5
+ /**
6
+ * Appends one accepted event. Returns the row id and whether it was new.
7
+ *
8
+ * `INSERT OR IGNORE` on `event_uid` is the convergence point for hook capture
9
+ * and transcript replay (PRD CAP-02): whichever arrives second is a no-op, and
10
+ * neither needs to know the other exists.
11
+ */
12
+ export function appendEvent(db, input) {
13
+ const occurredAt = input.occurredAt ?? nowIso();
14
+ const info = db
15
+ .prepare(`INSERT OR IGNORE INTO evidence_events
16
+ (event_uid, project, checkout, session_id, agent_id, host, source, kind, tool,
17
+ title, body, files, occurred_at, received_at, redacted)
18
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
19
+ .run(input.eventUid, input.project, input.checkout ?? null, input.sessionId, input.agentId ?? null, input.host ?? 'claude-code', input.source ?? 'hook', input.kind, input.tool ?? null, input.title ?? null, input.body, input.files && input.files.length ? JSON.stringify(input.files) : null, occurredAt, nowIso(), input.redacted ? 1 : 0);
20
+ if (info.changes > 0)
21
+ return { id: Number(info.lastInsertRowid), inserted: true };
22
+ const existing = db
23
+ .prepare('SELECT id FROM evidence_events WHERE event_uid = ?')
24
+ .get(input.eventUid);
25
+ return { id: existing?.id ?? 0, inserted: false };
26
+ }
27
+ export function eventsForSession(db, sessionId, limit = 200) {
28
+ return db
29
+ .prepare('SELECT * FROM evidence_events WHERE session_id = ? ORDER BY occurred_at DESC LIMIT ?')
30
+ .all(sessionId, limit);
31
+ }
32
+ export function pendingEventCount(db, project) {
33
+ const row = project
34
+ ? db
35
+ .prepare("SELECT COUNT(*) AS n FROM evidence_events WHERE status = 'accepted' AND project = ?")
36
+ .get(project)
37
+ : db.prepare("SELECT COUNT(*) AS n FROM evidence_events WHERE status = 'accepted'").get();
38
+ return row.n;
39
+ }
40
+ /**
41
+ * Claims the accepted events of one session into an immutable batch and queues
42
+ * a job for it. The claim and the queue are one transaction: a batch with no
43
+ * job is work that silently never happens, and a job with no batch is a request
44
+ * that cannot be reconstructed (PRD DATA-03).
45
+ */
46
+ export function batchSession(db, opts) {
47
+ const max = opts.maxEvents ?? 60;
48
+ return db.transaction(() => {
49
+ const events = db
50
+ .prepare(`SELECT id, length(body) AS bytes FROM evidence_events
51
+ WHERE status = 'accepted' AND session_id = ? AND project = ?
52
+ ORDER BY occurred_at LIMIT ?`)
53
+ .all(opts.sessionId, opts.project, max);
54
+ if (events.length === 0)
55
+ return null;
56
+ const bytes = events.reduce((sum, e) => sum + e.bytes, 0);
57
+ const batchId = Number(db
58
+ .prepare(`INSERT INTO memory_batches (project, session_id, reason, event_count, byte_count, created_at)
59
+ VALUES (?, ?, ?, ?, ?, ?)`)
60
+ .run(opts.project, opts.sessionId, opts.reason, events.length, bytes, nowIso()).lastInsertRowid);
61
+ const claim = db.prepare("UPDATE evidence_events SET status = 'batched', batch_id = ? WHERE id = ?");
62
+ for (const e of events)
63
+ claim.run(batchId, e.id);
64
+ const jobId = Number(db
65
+ .prepare(`INSERT INTO memory_jobs (batch_id, kind, status, created_at, updated_at)
66
+ VALUES (?, 'summarize', 'pending', ?, ?)`)
67
+ .run(batchId, nowIso(), nowIso()).lastInsertRowid);
68
+ return { batchId, jobId, eventCount: events.length };
69
+ })();
70
+ }
71
+ /**
72
+ * Takes the next runnable job under a lease.
73
+ *
74
+ * The lease, not a process list, is what makes "one worker per job" true. A
75
+ * crashed worker's lease expires and the job becomes claimable again; a worker
76
+ * that merely hangs cannot corrupt anything, because completion is a conditional
77
+ * update on the lease it still holds.
78
+ *
79
+ * `next_attempt` is the second gate: a job that just failed transiently is
80
+ * still `pending`, and without it the next hook — seconds later — would claim
81
+ * it again and spend another attempt on a provider that has not recovered.
82
+ */
83
+ export function claimJob(db, owner, leaseSeconds = 120) {
84
+ const now = nowIso();
85
+ const until = new Date(Date.now() + leaseSeconds * 1000).toISOString();
86
+ return db.transaction(() => {
87
+ const job = db
88
+ .prepare(`SELECT * FROM memory_jobs
89
+ WHERE (next_attempt IS NULL OR next_attempt <= ?)
90
+ AND (status = 'pending' OR (status = 'claimed' AND (lease_until IS NULL OR lease_until < ?)))
91
+ ORDER BY id LIMIT 1`)
92
+ .get(now, now);
93
+ if (!job)
94
+ return null;
95
+ db.prepare(`UPDATE memory_jobs
96
+ SET status = 'claimed', attempts = attempts + 1, lease_owner = ?, lease_until = ?, updated_at = ?
97
+ WHERE id = ?`).run(owner, until, now, job.id);
98
+ return { ...job, status: 'claimed', attempts: job.attempts + 1, lease_owner: owner, lease_until: until };
99
+ })();
100
+ }
101
+ export function finishJob(db, jobId, owner) {
102
+ db.prepare(`UPDATE memory_jobs SET status = 'done', lease_owner = NULL, lease_until = NULL, updated_at = ?
103
+ WHERE id = ? AND lease_owner = ?`).run(nowIso(), jobId, owner);
104
+ }
105
+ /** First retry floor, doubled per attempt up to `RETRY_CEILING_MS`. */
106
+ const RETRY_BASE_MS = 30_000;
107
+ /** Five minutes. Past that the queue is waiting on a human, not on a blip. */
108
+ const RETRY_CEILING_MS = 300_000;
109
+ /**
110
+ * Records a failure and decides whether — and when — the job may run again.
111
+ *
112
+ * The classification is the point. A timeout should be retried; a rejected API
113
+ * key should not be retried a thousand times at the developer's expense, and a
114
+ * batch whose provider output will never parse should stop rather than spin.
115
+ *
116
+ * A retryable failure carries a floor as well as a verdict. `processPending`
117
+ * runs at every session seam, so "pending" alone means "claimable by whatever
118
+ * happens next", and a provider having a bad minute would take all five
119
+ * attempts inside it. Half the window is fixed and half is jitter, because
120
+ * several checkouts sharing one database and one dead provider otherwise wake
121
+ * up in the same second and retry as a group.
122
+ */
123
+ export function failJob(db, jobId, owner, errorClass, message, maxAttempts = 5, random = Math.random) {
124
+ const row = db.prepare('SELECT attempts FROM memory_jobs WHERE id = ?').get(jobId);
125
+ const attempts = row?.attempts ?? 0;
126
+ const terminal = errorClass === 'permanent' || errorClass === 'malformed' || attempts >= maxAttempts;
127
+ const paused = errorClass === 'auth' || errorClass === 'quota';
128
+ const window = Math.min(RETRY_CEILING_MS, RETRY_BASE_MS * 2 ** Math.max(0, attempts - 1));
129
+ db.prepare(`UPDATE memory_jobs
130
+ SET status = ?, last_error = ?, error_class = ?, next_attempt = ?,
131
+ lease_owner = NULL, lease_until = NULL, updated_at = ?
132
+ WHERE id = ? AND lease_owner = ?`).run(terminal ? 'failed' : paused ? 'paused' : 'pending', message.slice(0, 500), errorClass, terminal || paused
133
+ ? null
134
+ : new Date(Date.now() + window / 2 + random() * (window / 2)).toISOString(), nowIso(), jobId, owner);
135
+ }
136
+ /**
137
+ * Puts every paused job back in the queue, and returns how many moved.
138
+ *
139
+ * `failJob` parks an auth or quota failure at 'paused' and nothing else ever
140
+ * moves it, so this is the only way out — deliberately, and deliberately not
141
+ * automatic. Running `eklavya memory process` is the developer saying they have
142
+ * repaired the credential; a hook doing it on their behalf would re-spend a
143
+ * rejected key at every session seam and never say why it stopped again.
144
+ *
145
+ * The attempt count resets with it: a queue paused on its fifth attempt is one
146
+ * that would otherwise be marked permanently failed by the first call after the
147
+ * repair, which is the same dead end with a different label.
148
+ */
149
+ export function resumePaused(db) {
150
+ return db
151
+ .prepare(`UPDATE memory_jobs
152
+ SET status = 'pending', attempts = 0, next_attempt = NULL,
153
+ lease_owner = NULL, lease_until = NULL, updated_at = ?
154
+ WHERE status = 'paused'`)
155
+ .run(nowIso()).changes;
156
+ }
157
+ export function batchEvents(db, batchId) {
158
+ return db
159
+ .prepare('SELECT * FROM evidence_events WHERE batch_id = ? ORDER BY occurred_at')
160
+ .all(batchId);
161
+ }
162
+ export function batchById(db, batchId) {
163
+ return db.prepare('SELECT id, project, session_id FROM memory_batches WHERE id = ?').get(batchId);
164
+ }
165
+ /** Writes an entry, its links, its tags and its vector in one transaction. */
166
+ export function insertEntry(db, input) {
167
+ const occurredAt = input.occurredAt ?? nowIso();
168
+ const uid = input.entryUid ??
169
+ entryUid({
170
+ project: input.project,
171
+ title: input.title,
172
+ occurredAt,
173
+ salt: String(input.batchId ?? crypto.randomUUID()),
174
+ });
175
+ return db.transaction(() => {
176
+ const existing = db.prepare('SELECT id FROM memory_entries WHERE entry_uid = ?').get(uid);
177
+ if (existing)
178
+ return existing.id;
179
+ const id = Number(db
180
+ .prepare(`INSERT INTO memory_entries
181
+ (entry_uid, project, session_id, batch_id, kind, type, title, narrative, facts, files,
182
+ generator, confidence, occurred_at, created_at, import_source)
183
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
184
+ .run(uid, input.project, input.sessionId ?? null, input.batchId ?? null, input.kind ?? 'observation', input.type ?? null, input.title, input.narrative ?? '', input.facts?.length ? JSON.stringify(input.facts) : null, input.files?.length ? JSON.stringify(input.files) : null, input.generator ?? 'local-v1', input.confidence ?? null, occurredAt, nowIso(), input.importSource ?? null).lastInsertRowid);
185
+ const tag = db.prepare('INSERT OR IGNORE INTO memory_entry_tags (entry_id, tag) VALUES (?, ?)');
186
+ for (const t of input.tags ?? [])
187
+ tag.run(id, t.toLowerCase());
188
+ const link = db.prepare('INSERT OR IGNORE INTO memory_entry_events (entry_id, event_id) VALUES (?, ?)');
189
+ for (const eventId of input.eventIds ?? [])
190
+ link.run(id, eventId);
191
+ indexVector(db, id, vectorText(input));
192
+ return id;
193
+ })();
194
+ }
195
+ function vectorText(input) {
196
+ return [input.title, input.narrative ?? '', (input.facts ?? []).join(' '), (input.files ?? []).join(' ')].join('\n');
197
+ }
198
+ /**
199
+ * Rewrites an entry's content in place and re-indexes it.
200
+ *
201
+ * The one entry that legitimately changes is the session summary: it rolls up a
202
+ * session that is still running, so every seam has more to say than the last.
203
+ * A correction still supersedes rather than edits (MEM-03) — this is not a
204
+ * correction, and neither of the other two options survives being done once a
205
+ * turn. Superseding leaves a dead row per turn in the timeline; hard deleting
206
+ * cascades away the `context_receipt_items` that record what a past recall
207
+ * already cost, rewriting a ledger after the fact.
208
+ *
209
+ * FTS keeps itself in step through the `memory_entries_au` trigger; the vector
210
+ * has no trigger, so it is refreshed here.
211
+ */
212
+ export function replaceEntry(db, id, input) {
213
+ db.transaction(() => {
214
+ db.prepare(`UPDATE memory_entries
215
+ SET type = ?, title = ?, narrative = ?, facts = ?, files = ?, confidence = ?, occurred_at = ?
216
+ WHERE id = ?`).run(input.type ?? null, input.title, input.narrative ?? '', input.facts?.length ? JSON.stringify(input.facts) : null, input.files?.length ? JSON.stringify(input.files) : null, input.confidence ?? null, input.occurredAt ?? nowIso(), id);
217
+ db.prepare('DELETE FROM memory_entry_tags WHERE entry_id = ?').run(id);
218
+ const tag = db.prepare('INSERT OR IGNORE INTO memory_entry_tags (entry_id, tag) VALUES (?, ?)');
219
+ for (const t of input.tags ?? [])
220
+ tag.run(id, t.toLowerCase());
221
+ indexVector(db, id, vectorText(input));
222
+ })();
223
+ }
224
+ export function indexVector(db, entryId, text) {
225
+ const vec = embedLocal(text);
226
+ db.prepare(`INSERT INTO memory_vectors (entry_id, embedder_id, dim, vec) VALUES (?, ?, ?, ?)
227
+ ON CONFLICT(entry_id, embedder_id) DO UPDATE SET vec = excluded.vec, dim = excluded.dim`).run(entryId, LOCAL_EMBEDDER, LOCAL_DIM, toBlob(vec));
228
+ }
229
+ export function entryById(db, id) {
230
+ return db.prepare('SELECT * FROM memory_entries WHERE id = ?').get(id);
231
+ }
232
+ export function entriesByIds(db, ids) {
233
+ if (!ids.length)
234
+ return [];
235
+ const placeholders = ids.map(() => '?').join(',');
236
+ return db
237
+ .prepare(`SELECT * FROM memory_entries WHERE id IN (${placeholders})`)
238
+ .all(...ids);
239
+ }
240
+ export function entryEvents(db, entryId) {
241
+ return db
242
+ .prepare(`SELECT e.* FROM evidence_events e
243
+ JOIN memory_entry_events m ON m.event_id = e.id
244
+ WHERE m.entry_id = ? ORDER BY e.occurred_at`)
245
+ .all(entryId);
246
+ }
247
+ export function entryTags(db, entryId) {
248
+ return db.prepare('SELECT tag FROM memory_entry_tags WHERE entry_id = ?').all(entryId).map((r) => r.tag);
249
+ }
250
+ /**
251
+ * A correction supersedes rather than edits (PRD MEM-03): the stale claim keeps
252
+ * its evidence links and its place in the audit trail, and retrieval prefers the
253
+ * row that replaced it.
254
+ */
255
+ export function supersedeEntry(db, staleId, replacementId) {
256
+ db.prepare('UPDATE memory_entries SET superseded_by = ? WHERE id = ?').run(replacementId, staleId);
257
+ }
258
+ /**
259
+ * Soft delete by default so a deletion is auditable, with a hard mode that also
260
+ * removes the derived index rows — a deletion that leaves the vector behind is
261
+ * a deletion the search can still surface (PRD SEC-02).
262
+ */
263
+ export function deleteEntry(db, id, hard = false) {
264
+ if (hard) {
265
+ db.prepare('DELETE FROM memory_entries WHERE id = ?').run(id);
266
+ return;
267
+ }
268
+ db.transaction(() => {
269
+ db.prepare('UPDATE memory_entries SET deleted_at = ? WHERE id = ?').run(nowIso(), id);
270
+ db.prepare('DELETE FROM memory_vectors WHERE entry_id = ?').run(id);
271
+ db.prepare(`INSERT INTO memory_fts(memory_fts, rowid, title, narrative, facts, files)
272
+ SELECT 'delete', id, title, narrative, facts, files FROM memory_entries WHERE id = ?`).run(id);
273
+ })();
274
+ }
275
+ /** Stable ordering and a cursor-able offset (PRD RET-02). */
276
+ export function timeline(db, filter = {}) {
277
+ const where = [];
278
+ const args = [];
279
+ if (filter.project) {
280
+ where.push('project = ?');
281
+ args.push(filter.project);
282
+ }
283
+ if (filter.sessionId) {
284
+ where.push('session_id = ?');
285
+ args.push(filter.sessionId);
286
+ }
287
+ if (filter.kind) {
288
+ where.push('kind = ?');
289
+ args.push(filter.kind);
290
+ }
291
+ if (filter.type) {
292
+ where.push('type = ?');
293
+ args.push(filter.type);
294
+ }
295
+ if (filter.since) {
296
+ where.push('occurred_at >= ?');
297
+ args.push(filter.since);
298
+ }
299
+ if (filter.until) {
300
+ where.push('occurred_at <= ?');
301
+ args.push(filter.until);
302
+ }
303
+ if (!filter.includeDeleted)
304
+ where.push('deleted_at IS NULL');
305
+ const sql = `SELECT * FROM memory_entries
306
+ ${where.length ? `WHERE ${where.join(' AND ')}` : ''}
307
+ ORDER BY occurred_at DESC, id DESC
308
+ LIMIT ? OFFSET ?`;
309
+ return db.prepare(sql).all(...args, filter.limit ?? 50, filter.offset ?? 0);
310
+ }
311
+ export function countEntries(db, project) {
312
+ const row = project
313
+ ? db
314
+ .prepare('SELECT COUNT(*) AS n FROM memory_entries WHERE project = ? AND deleted_at IS NULL')
315
+ .get(project)
316
+ : db.prepare('SELECT COUNT(*) AS n FROM memory_entries WHERE deleted_at IS NULL').get();
317
+ return row.n;
318
+ }
319
+ /**
320
+ * Writes one reuse receipt (PRD MET-01). `delivery` is the honesty valve: an
321
+ * adapter that cannot confirm the host consumed the context records `prepared`,
322
+ * and nothing derived from that row is ever shown as a confirmed saving.
323
+ */
324
+ export function recordReceipt(db, opts) {
325
+ const base = opts.items.reduce((s, i) => s + i.sourceTokens, 0);
326
+ const delivered = opts.items.reduce((s, i) => s + i.sentTokens, 0) + (opts.wrapperTokens ?? 0);
327
+ return db.transaction(() => {
328
+ const id = Number(db
329
+ .prepare(`INSERT INTO context_receipts
330
+ (receipt_uid, project, session_id, scope, method, base_tokens, delivered_tokens,
331
+ item_count, delivery, created_at)
332
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
333
+ .run(receiptUid(), opts.project, opts.sessionId ?? null, opts.scope, opts.method, base, delivered, opts.items.length, opts.delivery, nowIso()).lastInsertRowid);
334
+ const item = db.prepare(`INSERT OR REPLACE INTO context_receipt_items (receipt_id, entry_id, source_tokens, sent_tokens, stage)
335
+ VALUES (?, ?, ?, ?, ?)`);
336
+ for (const i of opts.items)
337
+ item.run(id, i.entryId, i.sourceTokens, i.sentTokens, i.stage ?? 'index');
338
+ return id;
339
+ })();
340
+ }
341
+ /**
342
+ * Charges a later detail fetch to the receipt that proposed the entry, so the
343
+ * episode's percentage falls to what the reuse actually cost instead of keeping
344
+ * the optimistic index-only figure (PRD MET-01).
345
+ */
346
+ export function chargeDetail(db, receiptId, entryId, sentTokens) {
347
+ db.transaction(() => {
348
+ db.prepare(`INSERT OR REPLACE INTO context_receipt_items (receipt_id, entry_id, source_tokens, sent_tokens, stage)
349
+ VALUES (?, ?, 0, ?, 'detail')`).run(receiptId, entryId, sentTokens);
350
+ db.prepare(`UPDATE context_receipts SET delivered_tokens =
351
+ (SELECT COALESCE(SUM(sent_tokens), 0) FROM context_receipt_items WHERE receipt_id = ?)
352
+ WHERE id = ?`).run(receiptId, receiptId);
353
+ })();
354
+ }
355
+ export function receiptTotals(db, project, sinceDays) {
356
+ const where = [];
357
+ const args = [];
358
+ if (project) {
359
+ where.push('project = ?');
360
+ args.push(project);
361
+ }
362
+ if (sinceDays) {
363
+ where.push("created_at >= datetime('now', ?)");
364
+ args.push(`-${sinceDays} days`);
365
+ }
366
+ const row = db
367
+ .prepare(`SELECT COALESCE(SUM(CASE WHEN delivery = 'confirmed' THEN base_tokens ELSE 0 END), 0) AS base,
368
+ COALESCE(SUM(CASE WHEN delivery = 'confirmed' THEN delivered_tokens ELSE 0 END), 0) AS delivered,
369
+ COUNT(*) AS receipts,
370
+ COALESCE(SUM(CASE WHEN delivery = 'confirmed' THEN 1 ELSE 0 END), 0) AS confirmed
371
+ FROM context_receipts ${where.length ? `WHERE ${where.join(' AND ')}` : ''}`)
372
+ .get(...args);
373
+ return row;
374
+ }
375
+ /**
376
+ * A concept candidate derived from evidence (PRD LRN-02).
377
+ *
378
+ * `status = 'candidate'` is load-bearing: this row is a proposal, not a
379
+ * concept. Nothing here touches `mastery`, `attempts` or a gate — exposure is
380
+ * not assessment, and the only path to a passing attempt is still answering a
381
+ * question.
382
+ */
383
+ export function addCandidate(db, input) {
384
+ return Number(db
385
+ .prepare(`INSERT INTO learning_sources (entry_id, event_id, slug, name, domain, confidence, status, project, created_at)
386
+ VALUES (?, ?, ?, ?, ?, ?, 'candidate', ?, ?)`)
387
+ .run(input.entryId ?? null, input.eventId ?? null, input.slug, input.name, input.domain, input.confidence, input.project ?? null, nowIso()).lastInsertRowid);
388
+ }
389
+ export function pendingCandidates(db, project, limit = 20) {
390
+ const sql = project
391
+ ? `SELECT * FROM learning_sources WHERE status = 'candidate' AND project = ?
392
+ ORDER BY confidence DESC, id LIMIT ?`
393
+ : `SELECT * FROM learning_sources WHERE status = 'candidate' ORDER BY confidence DESC, id LIMIT ?`;
394
+ return (project ? db.prepare(sql).all(project, limit) : db.prepare(sql).all(limit));
395
+ }
396
+ export function resolveCandidate(db, id, status, conceptId) {
397
+ db.prepare('UPDATE learning_sources SET status = ?, concept_id = ? WHERE id = ?').run(status, conceptId ?? null, id);
398
+ }
399
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/memory/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,178 @@
1
+ import path from 'node:path';
2
+ function jsonList(value) {
3
+ if (!value)
4
+ return [];
5
+ try {
6
+ const parsed = JSON.parse(value);
7
+ return Array.isArray(parsed) ? parsed : [];
8
+ }
9
+ catch {
10
+ // A malformed files column is a bug elsewhere; here it is simply no files.
11
+ return [];
12
+ }
13
+ }
14
+ function filesOf(rows) {
15
+ const seen = new Set();
16
+ for (const row of rows)
17
+ for (const f of jsonList(row.files))
18
+ seen.add(f);
19
+ return [...seen];
20
+ }
21
+ /**
22
+ * Tags from the paths the work touched.
23
+ *
24
+ * Path segments are a genuinely good signal for "what area was this" — `src/auth`
25
+ * and `migrations` say more about a batch than any keyword in its prose — and
26
+ * they cost nothing. Extensions add the language.
27
+ */
28
+ function tagsFor(files, events) {
29
+ const tags = new Set();
30
+ for (const file of files) {
31
+ const ext = path.extname(file).replace('.', '').toLowerCase();
32
+ if (ext && ext.length <= 5)
33
+ tags.add(ext);
34
+ for (const segment of path.dirname(file).split(/[\\/]/)) {
35
+ const clean = segment.toLowerCase();
36
+ if (clean && clean !== '.' && clean !== 'src' && clean.length > 2 && !clean.startsWith('.')) {
37
+ tags.add(clean);
38
+ }
39
+ }
40
+ }
41
+ if (events.some((e) => e.kind === 'tool_error'))
42
+ tags.add('failure');
43
+ return [...tags].slice(0, 12);
44
+ }
45
+ const TYPE_HINTS = [
46
+ [/\b(fix|bug|broken|regression|crash|failing)\b/i, 'bugfix'],
47
+ [/\b(refactor|extract|rename|clean ?up|simplify)\b/i, 'refactor'],
48
+ [/\b(decide|decision|chose|instead of|tradeoff|trade-off)\b/i, 'decision'],
49
+ [/\b(add|implement|build|introduce|support)\b/i, 'feature'],
50
+ [/\b(why|turns out|discovered|found that|because)\b/i, 'discovery'],
51
+ ];
52
+ function typeFor(text, events) {
53
+ for (const [re, type] of TYPE_HINTS)
54
+ if (re.test(text))
55
+ return type;
56
+ return events.some((e) => e.kind === 'tool_error') ? 'bugfix' : 'change';
57
+ }
58
+ function firstLine(text, max = 90) {
59
+ const line = text.split('\n').map((l) => l.trim()).find(Boolean) ?? '';
60
+ return line.length > max ? `${line.slice(0, max - 1)}…` : line;
61
+ }
62
+ /**
63
+ * The default summariser: no network, no key, no waiting.
64
+ *
65
+ * It is extractive rather than generative — it quotes and counts what the
66
+ * evidence already says instead of writing prose about it. That ceiling is
67
+ * real: it cannot explain *why* a change was made unless the developer said so
68
+ * in a prompt. It is also why it can run inside a hook without a budget.
69
+ */
70
+ export class LocalSummarizer {
71
+ id = 'local-v1';
72
+ async summarize(input) {
73
+ const { events } = input;
74
+ if (!events.length)
75
+ return [];
76
+ // A batch of nothing but session markers is a session in which nothing
77
+ // happened. Recording "session started" as a memory is how a corpus fills
78
+ // with rows that push real work out of a bounded recall.
79
+ if (events.every((e) => e.kind === 'lifecycle'))
80
+ return [];
81
+ const prompts = events.filter((e) => e.kind === 'prompt');
82
+ const errors = events.filter((e) => e.kind === 'tool_error');
83
+ const edits = events.filter((e) => e.kind === 'file_edit');
84
+ const files = filesOf(events);
85
+ const intent = prompts[0]?.body ?? events.find((e) => e.title)?.title ?? '';
86
+ const title = firstLine(intent) || (files[0] ? `Worked on ${files[0]}` : 'Session work');
87
+ const lines = [];
88
+ if (intent)
89
+ lines.push(`Asked: ${firstLine(intent, 200)}`);
90
+ if (edits.length)
91
+ lines.push(`Edited ${edits.length} time(s) across ${files.length} file(s).`);
92
+ if (errors.length)
93
+ lines.push(`${errors.length} tool failure(s) during the work.`);
94
+ for (const later of prompts.slice(1, 4))
95
+ lines.push(`Then asked: ${firstLine(later.body, 160)}`);
96
+ const facts = [];
97
+ for (const error of errors.slice(0, 3))
98
+ facts.push(`Failed: ${firstLine(error.body, 160)}`);
99
+ for (const file of files.slice(0, 6))
100
+ facts.push(`Touched ${file}`);
101
+ return [
102
+ {
103
+ title,
104
+ type: typeFor(`${intent}\n${events.map((e) => e.title ?? '').join('\n')}`, events),
105
+ narrative: lines.join('\n'),
106
+ facts,
107
+ files,
108
+ tags: tagsFor(files, events),
109
+ // Extractive, so the claims are as reliable as their sources — but it
110
+ // did not understand anything, and a confidence of 1 would say it did.
111
+ confidence: 0.5,
112
+ eventIds: events.map((e) => e.id),
113
+ },
114
+ ];
115
+ }
116
+ }
117
+ /** How many observations a summary will name before it stops listing them. */
118
+ const SUMMARY_MAX_LINES = 12;
119
+ /**
120
+ * The session summary (PRD MEM-01, PAR-03).
121
+ *
122
+ * An observation answers "what happened in this batch". A session summary
123
+ * answers the only question the *next* session opens with — where did I leave
124
+ * off — and it exists because recall hands back the top of the timeline. Without
125
+ * one, a developer resuming on Monday is handed whichever ten tool calls
126
+ * happened to end Friday, rather than what Friday was about. Until now only the
127
+ * Claude Mem importer ever wrote `kind = 'session_summary'`, so an imported
128
+ * history had them and a natively captured one never did.
129
+ *
130
+ * It rolls up the session's own observations rather than re-reading the raw
131
+ * evidence. The evidence has already been read once: a second pass would mean a
132
+ * second provider request per session, which is exactly the wait LRN-04 keeps
133
+ * out of a seam, and evidence the summariser already judged not worth keeping
134
+ * should not come back in through a different door.
135
+ *
136
+ * Returns null when there is nothing to say, because an entry is forever and a
137
+ * summary of nothing is noise in every future recall. Two observations is the
138
+ * floor, deliberately: with one, the summary is that observation retyped, and a
139
+ * near-duplicate row competing with its own source for a bounded recall budget
140
+ * costs more than it tells anyone.
141
+ */
142
+ export function summarizeSession(observations) {
143
+ if (observations.length < 2)
144
+ return null;
145
+ const ordered = [...observations].sort((a, b) => a.occurred_at.localeCompare(b.occurred_at));
146
+ const files = filesOf(ordered);
147
+ const counts = new Map();
148
+ for (const o of ordered)
149
+ counts.set(o.type ?? 'change', (counts.get(o.type ?? 'change') ?? 0) + 1);
150
+ const commonest = [...counts].sort((a, b) => b[1] - a[1])[0][0];
151
+ const listed = ordered.slice(0, SUMMARY_MAX_LINES);
152
+ const narrative = [
153
+ `${ordered.length} observations across ${files.length} file(s) in this session.`,
154
+ ...listed.map((o) => `- ${o.type ?? 'change'}: ${firstLine(o.title, 120)}`),
155
+ ordered.length > listed.length ? `- …and ${ordered.length - listed.length} more.` : '',
156
+ ]
157
+ .filter(Boolean)
158
+ .join('\n');
159
+ return {
160
+ title: `Session: ${firstLine(ordered[0].title, 80)}`,
161
+ type: commonest,
162
+ narrative,
163
+ facts: files.slice(0, 10).map((f) => `Touched ${f}`),
164
+ files,
165
+ // `session` first so the tag survives the cap on a session that touched a
166
+ // deep tree, and so a summary is filterable as one.
167
+ tags: ['session', ...tagsFor(files, [])].slice(0, 12),
168
+ // No more reliable than the least reliable thing underneath it.
169
+ confidence: Math.min(...ordered.map((o) => o.confidence ?? 0.5)),
170
+ // Deliberately no event links. `pruneEvidence` keeps any event an entry
171
+ // still points at, so a summary citing a whole session would pin that
172
+ // session's raw evidence past `memory.retention_days` for ever — the one
173
+ // promise SEC-02 makes about raw capture. The price is a receipt that
174
+ // scores this row as pure overhead; the other trade is worse.
175
+ eventIds: [],
176
+ };
177
+ }
178
+ //# sourceMappingURL=summarize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summarize.js","sourceRoot":"","sources":["../../src/memory/summarize.ts"],"names":[],"mappings":"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"}