claude-mem-lite 6.13.5 → 6.13.6
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/hook-llm.mjs +43 -5
- package/hook.mjs +35 -31
- package/lib/fast-summary.mjs +87 -61
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"plugins": [
|
|
10
10
|
{
|
|
11
11
|
"name": "claude-mem-lite",
|
|
12
|
-
"version": "6.13.
|
|
12
|
+
"version": "6.13.6",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"homepage": "https://github.com/sdsrss/claude-mem-lite",
|
|
15
15
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark)."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "6.13.
|
|
3
|
+
"version": "6.13.6",
|
|
4
4
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sdsrss"
|
package/hook-llm.mjs
CHANGED
|
@@ -25,7 +25,9 @@ import {
|
|
|
25
25
|
import { acquireLLMSlot, releaseLLMSlot } from './hook-semaphore.mjs';
|
|
26
26
|
import { BG_LLM_TIMEOUT_MS } from './haiku-client.mjs';
|
|
27
27
|
import { scrubRecord, scrubFilePaths } from './lib/scrub-record.mjs';
|
|
28
|
-
import { mergeModelSummary } from './lib/fast-summary.mjs';
|
|
28
|
+
import { mergeModelSummary, summarySuperseded } from './lib/fast-summary.mjs';
|
|
29
|
+
import { recordMetric } from './lib/metrics.mjs';
|
|
30
|
+
import { DB_DIR } from './schema.mjs';
|
|
29
31
|
import {
|
|
30
32
|
insertObservationRow,
|
|
31
33
|
insertObservationFiles,
|
|
@@ -1374,8 +1376,28 @@ export async function handleLLMSummary() {
|
|
|
1374
1376
|
);
|
|
1375
1377
|
}
|
|
1376
1378
|
|
|
1379
|
+
// The spawning Stop's epoch (absent for the /clear spawn and pre-upgrade workers). Checked
|
|
1380
|
+
// before the model call and again in the write's transaction, because two workers of one
|
|
1381
|
+
// session can finish out of order (P3-6); the later Stop's worker reads the newer window.
|
|
1382
|
+
const parsedEpoch = Number(process.argv[5]);
|
|
1383
|
+
const spawnEpoch = Number.isFinite(parsedEpoch) && parsedEpoch > 0 ? parsedEpoch : null;
|
|
1384
|
+
// One `summary_worker` metric row per exit (CLAUDE_MEM_METRICS=1), carrying the session and
|
|
1385
|
+
// the Stop epoch so a superseded worker can be paired with its successor's outcome, and how
|
|
1386
|
+
// many model calls a session costs can be counted (D#95). This process runs detached with no
|
|
1387
|
+
// stderr, so nothing else shows it.
|
|
1388
|
+
const metricSession = process.argv[3] || null;
|
|
1389
|
+
let llmMs;
|
|
1390
|
+
const outcome = (name) =>
|
|
1391
|
+
recordMetric(DB_DIR, {
|
|
1392
|
+
event: 'summary_worker',
|
|
1393
|
+
outcome: name,
|
|
1394
|
+
session: metricSession,
|
|
1395
|
+
stopEpoch: spawnEpoch,
|
|
1396
|
+
...(llmMs === undefined ? {} : { llmMs }),
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1377
1399
|
const db = openDb();
|
|
1378
|
-
if (!db) return;
|
|
1400
|
+
if (!db) return outcome('no-db');
|
|
1379
1401
|
|
|
1380
1402
|
try {
|
|
1381
1403
|
const sessionId = process.argv[3] || getSessionId();
|
|
@@ -1397,7 +1419,7 @@ export async function handleLLMSummary() {
|
|
|
1397
1419
|
)
|
|
1398
1420
|
.all(sessionId);
|
|
1399
1421
|
|
|
1400
|
-
if (recentObs.length < 1) return;
|
|
1422
|
+
if (recentObs.length < 1) return outcome('no-obs');
|
|
1401
1423
|
|
|
1402
1424
|
const obsList = recentObs
|
|
1403
1425
|
.map(
|
|
@@ -1433,12 +1455,18 @@ ${obsList}`;
|
|
|
1433
1455
|
|
|
1434
1456
|
if (!(await acquireLLMSlot())) {
|
|
1435
1457
|
debugLog('WARN', 'llm-summary', 'semaphore timeout, skipping summary');
|
|
1436
|
-
return;
|
|
1458
|
+
return outcome('slot-timeout');
|
|
1437
1459
|
}
|
|
1438
1460
|
|
|
1439
1461
|
let raw, llmParsed;
|
|
1440
1462
|
try {
|
|
1463
|
+
if (summarySuperseded(db, sessionId, spawnEpoch)) {
|
|
1464
|
+
debugLog('DEBUG', 'llm-summary', 'a later Stop owns this session summary, skipping');
|
|
1465
|
+
return outcome('superseded-before-call');
|
|
1466
|
+
}
|
|
1467
|
+
const callStart = Date.now();
|
|
1441
1468
|
raw = await callLLM(prompt, BG_LLM_TIMEOUT_MS);
|
|
1469
|
+
llmMs = Date.now() - callStart;
|
|
1442
1470
|
llmParsed = parseJsonFromLLM(raw);
|
|
1443
1471
|
} finally {
|
|
1444
1472
|
releaseLLMSlot();
|
|
@@ -1502,8 +1530,18 @@ ${obsList}`;
|
|
|
1502
1530
|
lessons: lessonsJson,
|
|
1503
1531
|
key_decisions: decisionsJson,
|
|
1504
1532
|
});
|
|
1505
|
-
mergeModelSummary(db, { sessionId, project, fields: safe, now })
|
|
1533
|
+
if (mergeModelSummary(db, { sessionId, project, fields: safe, now, spawnEpoch })) {
|
|
1534
|
+
outcome('written');
|
|
1535
|
+
} else {
|
|
1536
|
+
debugLog('DEBUG', 'llm-summary', 'a later Stop landed during the model call, reply dropped');
|
|
1537
|
+
outcome('superseded-at-write');
|
|
1538
|
+
}
|
|
1539
|
+
} else {
|
|
1540
|
+
outcome('no-content');
|
|
1506
1541
|
}
|
|
1542
|
+
} catch (e) {
|
|
1543
|
+
outcome('error');
|
|
1544
|
+
throw e;
|
|
1507
1545
|
} finally {
|
|
1508
1546
|
db.close();
|
|
1509
1547
|
}
|
package/hook.mjs
CHANGED
|
@@ -1075,13 +1075,20 @@ function flushEpisodeAtStop(sessionId, project) {
|
|
|
1075
1075
|
* parallel-safe row identity). Without the split, CC UUID-based queries miss
|
|
1076
1076
|
* user_prompts and the handoff row is silently skipped (see hook-handoff.mjs).
|
|
1077
1077
|
*/
|
|
1078
|
-
function markSessionCompletedAndSaveHandoff(
|
|
1078
|
+
function markSessionCompletedAndSaveHandoff(
|
|
1079
|
+
db,
|
|
1080
|
+
{ sessionId, project, ccSessionId, episodeSnapshot, stopEpoch },
|
|
1081
|
+
) {
|
|
1082
|
+
// Every Stop, not only the first: Stop fires per assistant turn, and `status = 'active'` alone
|
|
1083
|
+
// matched the first turn only, so completed_at kept the first turn's end for the whole
|
|
1084
|
+
// session. completed_at_epoch is now the session's LATEST Stop, which the llm-summary worker
|
|
1085
|
+
// compares against its own Stop's epoch to learn that a later worker owns the row (P3-6).
|
|
1079
1086
|
db.prepare(
|
|
1080
1087
|
`
|
|
1081
1088
|
UPDATE sdk_sessions SET status = 'completed', completed_at = ?, completed_at_epoch = ?
|
|
1082
|
-
WHERE content_session_id = ? AND status
|
|
1089
|
+
WHERE content_session_id = ? AND status IN ('active', 'completed')
|
|
1083
1090
|
`,
|
|
1084
|
-
).run(new Date().toISOString(),
|
|
1091
|
+
).run(new Date(stopEpoch).toISOString(), stopEpoch, sessionId);
|
|
1085
1092
|
// Save handoff snapshot for cross-session continuity.
|
|
1086
1093
|
// sessionId = mem-internal (query key); ccSessionId = CC UUID (scope key for
|
|
1087
1094
|
// parallel-safe row identity). Without the split, CC UUID-based queries miss
|
|
@@ -1577,11 +1584,13 @@ async function handleStop() {
|
|
|
1577
1584
|
|
|
1578
1585
|
flushEpisodeAtStop(sessionId, project);
|
|
1579
1586
|
|
|
1580
|
-
// Mark session completed + save handoff (sync, instant)
|
|
1587
|
+
// Mark session completed + save handoff (sync, instant). The same epoch goes to the summary
|
|
1588
|
+
// worker below, so it can tell whether a later Stop has superseded it.
|
|
1589
|
+
const stopEpoch = Date.now();
|
|
1581
1590
|
const db = openDb();
|
|
1582
1591
|
if (db) {
|
|
1583
1592
|
try {
|
|
1584
|
-
markSessionCompletedAndSaveHandoff(db, { sessionId, project, ccSessionId, episodeSnapshot });
|
|
1593
|
+
markSessionCompletedAndSaveHandoff(db, { sessionId, project, ccSessionId, episodeSnapshot, stopEpoch });
|
|
1585
1594
|
// Citations first: they read the subagent transcripts before the parent, so the parent is
|
|
1586
1595
|
// parsed once and stays memoized (D#152). The summary reads the parent's tail on every
|
|
1587
1596
|
// turn; run before them, it parsed the parent a second time in any session with subagents.
|
|
@@ -1600,7 +1609,8 @@ async function handleStop() {
|
|
|
1600
1609
|
// waits on, then recreates the sandbox tree behind the test's cleanup. Any
|
|
1601
1610
|
// grace period for that is a race, not a barrier — the post-tag review timed a
|
|
1602
1611
|
// recreate at 432ms and watched a 300ms grace lose.
|
|
1603
|
-
if (!process.env.CLAUDE_MEM_SKIP_SUMMARY)
|
|
1612
|
+
if (!process.env.CLAUDE_MEM_SKIP_SUMMARY)
|
|
1613
|
+
spawnBackground('llm-summary', sessionId, project, String(stopEpoch));
|
|
1604
1614
|
|
|
1605
1615
|
// The session file deliberately SURVIVES Stop (R10-P1-1). It used to be unlinked here,
|
|
1606
1616
|
// on the model "Stop = /exit = the session is over". The host does not work that way:
|
|
@@ -2246,38 +2256,32 @@ function buildFallbackFastSummary(db, { project, now, prevSessionId }) {
|
|
|
2246
2256
|
const recentSession = db
|
|
2247
2257
|
.prepare(
|
|
2248
2258
|
`
|
|
2249
|
-
SELECT content_session_id, project FROM sdk_sessions
|
|
2259
|
+
SELECT content_session_id, project FROM sdk_sessions s
|
|
2250
2260
|
WHERE project = ? AND status = 'completed' AND completed_at_epoch > ?
|
|
2261
|
+
AND NOT EXISTS (SELECT 1 FROM session_summaries WHERE memory_session_id = s.content_session_id)
|
|
2251
2262
|
ORDER BY completed_at_epoch DESC LIMIT 1
|
|
2252
2263
|
`,
|
|
2253
2264
|
)
|
|
2254
2265
|
.get(project, Date.now() - 120000); // within last 2 minutes
|
|
2255
2266
|
|
|
2267
|
+
// "Has no summary" is in the WHERE, not checked after LIMIT 1: every Stop now records
|
|
2268
|
+
// itself (P3-6), so a parallel session still live ranks by its latest turn and, holding a
|
|
2269
|
+
// summary, would take the one slot from the session that actually exited.
|
|
2256
2270
|
if (recentSession) {
|
|
2257
|
-
const
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
.
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
// No remaining_items on this path: an /exit restart has no handoff and no
|
|
2272
|
-
// episode snapshot to infer one from. It was a bare '' in the SQL before.
|
|
2273
|
-
insertFastSummary(db, {
|
|
2274
|
-
sessionId: recentSession.content_session_id,
|
|
2275
|
-
project,
|
|
2276
|
-
now,
|
|
2277
|
-
values: { request: frRaw, completed: fcRaw },
|
|
2278
|
-
limits: FAST_SUMMARY_LIMITS.exitRestart,
|
|
2279
|
-
});
|
|
2280
|
-
}
|
|
2271
|
+
const { request: frRaw, completed: fcRaw } = readFastSummarySource(
|
|
2272
|
+
db,
|
|
2273
|
+
recentSession.content_session_id,
|
|
2274
|
+
);
|
|
2275
|
+
if (frRaw || fcRaw) {
|
|
2276
|
+
// No remaining_items on this path: an /exit restart has no handoff and no
|
|
2277
|
+
// episode snapshot to infer one from. It was a bare '' in the SQL before.
|
|
2278
|
+
insertFastSummary(db, {
|
|
2279
|
+
sessionId: recentSession.content_session_id,
|
|
2280
|
+
project,
|
|
2281
|
+
now,
|
|
2282
|
+
values: { request: frRaw, completed: fcRaw },
|
|
2283
|
+
limits: FAST_SUMMARY_LIMITS.exitRestart,
|
|
2284
|
+
});
|
|
2281
2285
|
}
|
|
2282
2286
|
}
|
|
2283
2287
|
} catch (e) {
|
package/lib/fast-summary.mjs
CHANGED
|
@@ -309,6 +309,24 @@ export function writeClearSummary(db, { sessionId, project, values, limits, now
|
|
|
309
309
|
}).immediate();
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Whether a Stop later than the one at `spawnEpoch` has been recorded for the session
|
|
314
|
+
* (sdk_sessions.completed_at_epoch holds the LATEST Stop). Stop spawns one model worker per
|
|
315
|
+
* turn and two of them can finish out of order; the later Stop's worker reads the newer
|
|
316
|
+
* window and owns the row, so this one's reply must not land after it (P3-6). Not a strict
|
|
317
|
+
* superset: observations can be deleted between the two reads (the episode upgrade-delete),
|
|
318
|
+
* and the later worker can exit on no-obs / slot-timeout / an empty reply, leaving the row an
|
|
319
|
+
* earlier turn behind — the summary_worker metric pairs the two by session. A missing or
|
|
320
|
+
* non-numeric epoch is never superseded.
|
|
321
|
+
*/
|
|
322
|
+
export function summarySuperseded(db, sessionId, spawnEpoch) {
|
|
323
|
+
if (!Number.isFinite(spawnEpoch) || spawnEpoch <= 0) return false;
|
|
324
|
+
const latest = db
|
|
325
|
+
.prepare('SELECT completed_at_epoch AS e FROM sdk_sessions WHERE content_session_id = ?')
|
|
326
|
+
.get(sessionId)?.e;
|
|
327
|
+
return Number.isFinite(latest) && latest > spawnEpoch;
|
|
328
|
+
}
|
|
329
|
+
|
|
312
330
|
const MODEL_TEXT_FIELDS = ['request', 'investigated', 'learned', 'next_steps'];
|
|
313
331
|
const MODEL_JSON_FIELDS = ['lessons', 'key_decisions'];
|
|
314
332
|
const SUMMARY_COLUMNS = [...MODEL_TEXT_FIELDS, 'completed', 'remaining_items', ...MODEL_JSON_FIELDS];
|
|
@@ -325,70 +343,78 @@ const SUMMARY_COLUMNS = [...MODEL_TEXT_FIELDS, 'completed', 'remaining_items', .
|
|
|
325
343
|
* The row's timestamp is NOT moved (D#79): this worker can finish after the NEXT session has
|
|
326
344
|
* written its first row. A session with no row is inserted at its own last prompt, not at the
|
|
327
345
|
* worker's finish, for the same reason.
|
|
346
|
+
*
|
|
347
|
+
* `spawnEpoch` is the epoch of the Stop that spawned this worker; when a later Stop has been
|
|
348
|
+
* recorded, nothing is written and false is returned (see summarySuperseded). Omitted — the
|
|
349
|
+
* /clear spawn, a worker spawned by an older version — it always writes.
|
|
328
350
|
*/
|
|
329
|
-
export function mergeModelSummary(db, { sessionId, project, fields, now }) {
|
|
330
|
-
db
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
351
|
+
export function mergeModelSummary(db, { sessionId, project, fields, now, spawnEpoch }) {
|
|
352
|
+
return db
|
|
353
|
+
.transaction(() => {
|
|
354
|
+
if (summarySuperseded(db, sessionId, spawnEpoch)) return false;
|
|
355
|
+
const id = newestSummaryId(db, sessionId);
|
|
356
|
+
if (id === null) {
|
|
357
|
+
const last = db
|
|
358
|
+
.prepare('SELECT MAX(created_at_epoch) AS e FROM user_prompts WHERE content_session_id = ?')
|
|
359
|
+
.get(sessionId)?.e;
|
|
360
|
+
const stamp = Number.isFinite(last) && last > 0 && last <= now.getTime() ? new Date(last) : now;
|
|
361
|
+
db.prepare(
|
|
362
|
+
`INSERT INTO session_summaries (memory_session_id, project, request, investigated, learned, completed, next_steps,
|
|
339
363
|
remaining_items, files_read, files_edited, notes, lessons, key_decisions, created_at, created_at_epoch)
|
|
340
364
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, '[]', '[]', ?, ?, ?, ?, ?)`,
|
|
365
|
+
).run(
|
|
366
|
+
sessionId,
|
|
367
|
+
project,
|
|
368
|
+
fields.request || '',
|
|
369
|
+
fields.investigated || '',
|
|
370
|
+
fields.learned || '',
|
|
371
|
+
fields.completed || '',
|
|
372
|
+
fields.next_steps || '',
|
|
373
|
+
fields.remaining_items || '',
|
|
374
|
+
formatSummaryNotes({
|
|
375
|
+
done: nonEmpty(fields.completed) ? 'model' : 'titles',
|
|
376
|
+
left: 'other',
|
|
377
|
+
lines: '',
|
|
378
|
+
}),
|
|
379
|
+
fields.lessons ?? null,
|
|
380
|
+
fields.key_decisions ?? null,
|
|
381
|
+
stamp.toISOString(),
|
|
382
|
+
stamp.getTime(),
|
|
383
|
+
);
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
const rows = db
|
|
387
|
+
.prepare(
|
|
388
|
+
`SELECT id, notes, ${SUMMARY_COLUMNS.join(', ')} FROM session_summaries
|
|
389
|
+
WHERE memory_session_id = ? ORDER BY created_at_epoch DESC, id DESC`,
|
|
390
|
+
)
|
|
391
|
+
.all(sessionId);
|
|
392
|
+
const own = rows.find((r) => r.id === id);
|
|
393
|
+
const siblings = rows.filter((r) => r.id !== id);
|
|
394
|
+
const floor = (col) =>
|
|
395
|
+
nonEmpty(own[col]) ?? siblings.map((r) => nonEmpty(r[col])).find(Boolean) ?? own[col];
|
|
396
|
+
const prov = parseSummaryNotes(own.notes);
|
|
397
|
+
const next = {};
|
|
398
|
+
for (const col of [...MODEL_TEXT_FIELDS, ...MODEL_JSON_FIELDS])
|
|
399
|
+
next[col] = nonEmpty(fields[col]) ?? floor(col);
|
|
400
|
+
if (prov.done !== 'report' && nonEmpty(fields.completed)) {
|
|
401
|
+
next.completed = fields.completed;
|
|
402
|
+
prov.done = 'model';
|
|
403
|
+
} else {
|
|
404
|
+
next.completed = floor('completed');
|
|
405
|
+
}
|
|
406
|
+
next.remaining_items =
|
|
407
|
+
prov.left === 'report'
|
|
408
|
+
? own.remaining_items
|
|
409
|
+
: (nonEmpty(fields.remaining_items) ?? floor('remaining_items'));
|
|
410
|
+
db.prepare(
|
|
411
|
+
`UPDATE session_summaries SET ${SUMMARY_COLUMNS.map((c) => `${c} = ?`).join(', ')}, notes = ? WHERE id = ?`,
|
|
341
412
|
).run(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
fields.investigated || '',
|
|
346
|
-
fields.learned || '',
|
|
347
|
-
fields.completed || '',
|
|
348
|
-
fields.next_steps || '',
|
|
349
|
-
fields.remaining_items || '',
|
|
350
|
-
formatSummaryNotes({
|
|
351
|
-
done: nonEmpty(fields.completed) ? 'model' : 'titles',
|
|
352
|
-
left: 'other',
|
|
353
|
-
lines: '',
|
|
354
|
-
}),
|
|
355
|
-
fields.lessons ?? null,
|
|
356
|
-
fields.key_decisions ?? null,
|
|
357
|
-
stamp.toISOString(),
|
|
358
|
-
stamp.getTime(),
|
|
413
|
+
...SUMMARY_COLUMNS.map((c) => next[c]),
|
|
414
|
+
formatSummaryNotes(prov, FAST_SUMMARY_LIMITS.stop.notes),
|
|
415
|
+
id,
|
|
359
416
|
);
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
.prepare(
|
|
364
|
-
`SELECT id, notes, ${SUMMARY_COLUMNS.join(', ')} FROM session_summaries
|
|
365
|
-
WHERE memory_session_id = ? ORDER BY created_at_epoch DESC, id DESC`,
|
|
366
|
-
)
|
|
367
|
-
.all(sessionId);
|
|
368
|
-
const own = rows.find((r) => r.id === id);
|
|
369
|
-
const siblings = rows.filter((r) => r.id !== id);
|
|
370
|
-
const floor = (col) =>
|
|
371
|
-
nonEmpty(own[col]) ?? siblings.map((r) => nonEmpty(r[col])).find(Boolean) ?? own[col];
|
|
372
|
-
const prov = parseSummaryNotes(own.notes);
|
|
373
|
-
const next = {};
|
|
374
|
-
for (const col of [...MODEL_TEXT_FIELDS, ...MODEL_JSON_FIELDS])
|
|
375
|
-
next[col] = nonEmpty(fields[col]) ?? floor(col);
|
|
376
|
-
if (prov.done !== 'report' && nonEmpty(fields.completed)) {
|
|
377
|
-
next.completed = fields.completed;
|
|
378
|
-
prov.done = 'model';
|
|
379
|
-
} else {
|
|
380
|
-
next.completed = floor('completed');
|
|
381
|
-
}
|
|
382
|
-
next.remaining_items =
|
|
383
|
-
prov.left === 'report'
|
|
384
|
-
? own.remaining_items
|
|
385
|
-
: (nonEmpty(fields.remaining_items) ?? floor('remaining_items'));
|
|
386
|
-
db.prepare(
|
|
387
|
-
`UPDATE session_summaries SET ${SUMMARY_COLUMNS.map((c) => `${c} = ?`).join(', ')}, notes = ? WHERE id = ?`,
|
|
388
|
-
).run(
|
|
389
|
-
...SUMMARY_COLUMNS.map((c) => next[c]),
|
|
390
|
-
formatSummaryNotes(prov, FAST_SUMMARY_LIMITS.stop.notes),
|
|
391
|
-
id,
|
|
392
|
-
);
|
|
393
|
-
}).immediate();
|
|
417
|
+
return true;
|
|
418
|
+
})
|
|
419
|
+
.immediate();
|
|
394
420
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "6.13.
|
|
3
|
+
"version": "6.13.6",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "claude-mem-lite",
|
|
9
|
-
"version": "6.13.
|
|
9
|
+
"version": "6.13.6",
|
|
10
10
|
"os": [
|
|
11
11
|
"darwin",
|
|
12
12
|
"linux",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "6.13.
|
|
3
|
+
"version": "6.13.6",
|
|
4
4
|
"description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@10.9.2",
|