engrm 0.4.21 → 0.4.23
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/README.md +18 -0
- package/dist/cli.js +326 -21
- package/dist/hooks/elicitation-result.js +245 -12
- package/dist/hooks/post-tool-use.js +333 -14
- package/dist/hooks/pre-compact.js +984 -14
- package/dist/hooks/sentinel.js +245 -12
- package/dist/hooks/session-start.js +1097 -90
- package/dist/hooks/stop.js +430 -75
- package/dist/hooks/user-prompt-submit.js +342 -13
- package/dist/server.js +923 -86
- package/package.json +1 -1
|
@@ -1138,7 +1138,7 @@ var MIGRATIONS = [
|
|
|
1138
1138
|
-- Sync outbox (offline-first queue)
|
|
1139
1139
|
CREATE TABLE sync_outbox (
|
|
1140
1140
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1141
|
-
record_type TEXT NOT NULL CHECK (record_type IN ('observation', 'summary')),
|
|
1141
|
+
record_type TEXT NOT NULL CHECK (record_type IN ('observation', 'summary', 'chat_message')),
|
|
1142
1142
|
record_id INTEGER NOT NULL,
|
|
1143
1143
|
status TEXT DEFAULT 'pending' CHECK (status IN (
|
|
1144
1144
|
'pending', 'syncing', 'synced', 'failed'
|
|
@@ -1433,6 +1433,18 @@ var MIGRATIONS = [
|
|
|
1433
1433
|
},
|
|
1434
1434
|
{
|
|
1435
1435
|
version: 11,
|
|
1436
|
+
description: "Add observation provenance from tool and prompt chronology",
|
|
1437
|
+
sql: `
|
|
1438
|
+
ALTER TABLE observations ADD COLUMN source_tool TEXT;
|
|
1439
|
+
ALTER TABLE observations ADD COLUMN source_prompt_number INTEGER;
|
|
1440
|
+
CREATE INDEX IF NOT EXISTS idx_observations_source_tool
|
|
1441
|
+
ON observations(source_tool, created_at_epoch DESC);
|
|
1442
|
+
CREATE INDEX IF NOT EXISTS idx_observations_source_prompt
|
|
1443
|
+
ON observations(session_id, source_prompt_number DESC);
|
|
1444
|
+
`
|
|
1445
|
+
},
|
|
1446
|
+
{
|
|
1447
|
+
version: 12,
|
|
1436
1448
|
description: "Add synced handoff metadata to session summaries",
|
|
1437
1449
|
sql: `
|
|
1438
1450
|
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
@@ -1442,15 +1454,79 @@ var MIGRATIONS = [
|
|
|
1442
1454
|
`
|
|
1443
1455
|
},
|
|
1444
1456
|
{
|
|
1445
|
-
version:
|
|
1446
|
-
description: "Add
|
|
1457
|
+
version: 13,
|
|
1458
|
+
description: "Add current_thread to session summaries",
|
|
1447
1459
|
sql: `
|
|
1448
|
-
ALTER TABLE
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1460
|
+
ALTER TABLE session_summaries ADD COLUMN current_thread TEXT;
|
|
1461
|
+
`
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
version: 14,
|
|
1465
|
+
description: "Add chat_messages lane for raw conversation recall",
|
|
1466
|
+
sql: `
|
|
1467
|
+
CREATE TABLE IF NOT EXISTS chat_messages (
|
|
1468
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1469
|
+
session_id TEXT NOT NULL,
|
|
1470
|
+
project_id INTEGER REFERENCES projects(id),
|
|
1471
|
+
role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
|
|
1472
|
+
content TEXT NOT NULL,
|
|
1473
|
+
user_id TEXT NOT NULL,
|
|
1474
|
+
device_id TEXT NOT NULL,
|
|
1475
|
+
agent TEXT DEFAULT 'claude-code',
|
|
1476
|
+
created_at_epoch INTEGER NOT NULL
|
|
1477
|
+
);
|
|
1478
|
+
|
|
1479
|
+
CREATE INDEX IF NOT EXISTS idx_chat_messages_session
|
|
1480
|
+
ON chat_messages(session_id, created_at_epoch DESC, id DESC);
|
|
1481
|
+
CREATE INDEX IF NOT EXISTS idx_chat_messages_project
|
|
1482
|
+
ON chat_messages(project_id, created_at_epoch DESC, id DESC);
|
|
1483
|
+
CREATE INDEX IF NOT EXISTS idx_chat_messages_created
|
|
1484
|
+
ON chat_messages(created_at_epoch DESC, id DESC);
|
|
1485
|
+
`
|
|
1486
|
+
},
|
|
1487
|
+
{
|
|
1488
|
+
version: 15,
|
|
1489
|
+
description: "Add remote_source_id for chat message sync deduplication",
|
|
1490
|
+
sql: `
|
|
1491
|
+
ALTER TABLE chat_messages ADD COLUMN remote_source_id TEXT;
|
|
1492
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_chat_messages_remote_source
|
|
1493
|
+
ON chat_messages(remote_source_id)
|
|
1494
|
+
WHERE remote_source_id IS NOT NULL;
|
|
1495
|
+
`
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
version: 16,
|
|
1499
|
+
description: "Allow chat_message records in sync_outbox",
|
|
1500
|
+
sql: `
|
|
1501
|
+
CREATE TABLE sync_outbox_new (
|
|
1502
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1503
|
+
record_type TEXT NOT NULL CHECK (record_type IN ('observation', 'summary', 'chat_message')),
|
|
1504
|
+
record_id INTEGER NOT NULL,
|
|
1505
|
+
status TEXT DEFAULT 'pending' CHECK (status IN (
|
|
1506
|
+
'pending', 'syncing', 'synced', 'failed'
|
|
1507
|
+
)),
|
|
1508
|
+
retry_count INTEGER DEFAULT 0,
|
|
1509
|
+
max_retries INTEGER DEFAULT 10,
|
|
1510
|
+
last_error TEXT,
|
|
1511
|
+
created_at_epoch INTEGER NOT NULL,
|
|
1512
|
+
synced_at_epoch INTEGER,
|
|
1513
|
+
next_retry_epoch INTEGER
|
|
1514
|
+
);
|
|
1515
|
+
|
|
1516
|
+
INSERT INTO sync_outbox_new (
|
|
1517
|
+
id, record_type, record_id, status, retry_count, max_retries,
|
|
1518
|
+
last_error, created_at_epoch, synced_at_epoch, next_retry_epoch
|
|
1519
|
+
)
|
|
1520
|
+
SELECT
|
|
1521
|
+
id, record_type, record_id, status, retry_count, max_retries,
|
|
1522
|
+
last_error, created_at_epoch, synced_at_epoch, next_retry_epoch
|
|
1523
|
+
FROM sync_outbox;
|
|
1524
|
+
|
|
1525
|
+
DROP TABLE sync_outbox;
|
|
1526
|
+
ALTER TABLE sync_outbox_new RENAME TO sync_outbox;
|
|
1527
|
+
|
|
1528
|
+
CREATE INDEX idx_outbox_status ON sync_outbox(status, next_retry_epoch);
|
|
1529
|
+
CREATE INDEX idx_outbox_record ON sync_outbox(record_type, record_id);
|
|
1454
1530
|
`
|
|
1455
1531
|
}
|
|
1456
1532
|
];
|
|
@@ -1507,6 +1583,21 @@ function inferLegacySchemaVersion(db) {
|
|
|
1507
1583
|
version = Math.max(version, 10);
|
|
1508
1584
|
if (columnExists(db, "observations", "source_tool"))
|
|
1509
1585
|
version = Math.max(version, 11);
|
|
1586
|
+
if (columnExists(db, "session_summaries", "capture_state") && columnExists(db, "session_summaries", "recent_tool_names") && columnExists(db, "session_summaries", "hot_files") && columnExists(db, "session_summaries", "recent_outcomes")) {
|
|
1587
|
+
version = Math.max(version, 12);
|
|
1588
|
+
}
|
|
1589
|
+
if (columnExists(db, "session_summaries", "current_thread")) {
|
|
1590
|
+
version = Math.max(version, 13);
|
|
1591
|
+
}
|
|
1592
|
+
if (tableExists(db, "chat_messages")) {
|
|
1593
|
+
version = Math.max(version, 14);
|
|
1594
|
+
}
|
|
1595
|
+
if (columnExists(db, "chat_messages", "remote_source_id")) {
|
|
1596
|
+
version = Math.max(version, 15);
|
|
1597
|
+
}
|
|
1598
|
+
if (syncOutboxSupportsChatMessages(db)) {
|
|
1599
|
+
version = Math.max(version, 16);
|
|
1600
|
+
}
|
|
1510
1601
|
return version;
|
|
1511
1602
|
}
|
|
1512
1603
|
function runMigrations(db) {
|
|
@@ -1585,6 +1676,93 @@ function ensureObservationTypes(db) {
|
|
|
1585
1676
|
}
|
|
1586
1677
|
}
|
|
1587
1678
|
}
|
|
1679
|
+
function ensureSessionSummaryColumns(db) {
|
|
1680
|
+
const required = [
|
|
1681
|
+
"capture_state",
|
|
1682
|
+
"recent_tool_names",
|
|
1683
|
+
"hot_files",
|
|
1684
|
+
"recent_outcomes",
|
|
1685
|
+
"current_thread"
|
|
1686
|
+
];
|
|
1687
|
+
for (const column of required) {
|
|
1688
|
+
if (columnExists(db, "session_summaries", column))
|
|
1689
|
+
continue;
|
|
1690
|
+
db.exec(`ALTER TABLE session_summaries ADD COLUMN ${column} TEXT`);
|
|
1691
|
+
}
|
|
1692
|
+
const current = getSchemaVersion(db);
|
|
1693
|
+
if (current < 13) {
|
|
1694
|
+
db.exec("PRAGMA user_version = 13");
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
function ensureChatMessageColumns(db) {
|
|
1698
|
+
if (!tableExists(db, "chat_messages"))
|
|
1699
|
+
return;
|
|
1700
|
+
if (!columnExists(db, "chat_messages", "remote_source_id")) {
|
|
1701
|
+
db.exec("ALTER TABLE chat_messages ADD COLUMN remote_source_id TEXT");
|
|
1702
|
+
}
|
|
1703
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_chat_messages_remote_source ON chat_messages(remote_source_id) WHERE remote_source_id IS NOT NULL");
|
|
1704
|
+
const current = getSchemaVersion(db);
|
|
1705
|
+
if (current < 15) {
|
|
1706
|
+
db.exec("PRAGMA user_version = 15");
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
function ensureSyncOutboxSupportsChatMessages(db) {
|
|
1710
|
+
if (syncOutboxSupportsChatMessages(db)) {
|
|
1711
|
+
const current = getSchemaVersion(db);
|
|
1712
|
+
if (current < 16) {
|
|
1713
|
+
db.exec("PRAGMA user_version = 16");
|
|
1714
|
+
}
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
db.exec("BEGIN TRANSACTION");
|
|
1718
|
+
try {
|
|
1719
|
+
db.exec(`
|
|
1720
|
+
CREATE TABLE sync_outbox_new (
|
|
1721
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1722
|
+
record_type TEXT NOT NULL CHECK (record_type IN ('observation', 'summary', 'chat_message')),
|
|
1723
|
+
record_id INTEGER NOT NULL,
|
|
1724
|
+
status TEXT DEFAULT 'pending' CHECK (status IN (
|
|
1725
|
+
'pending', 'syncing', 'synced', 'failed'
|
|
1726
|
+
)),
|
|
1727
|
+
retry_count INTEGER DEFAULT 0,
|
|
1728
|
+
max_retries INTEGER DEFAULT 10,
|
|
1729
|
+
last_error TEXT,
|
|
1730
|
+
created_at_epoch INTEGER NOT NULL,
|
|
1731
|
+
synced_at_epoch INTEGER,
|
|
1732
|
+
next_retry_epoch INTEGER
|
|
1733
|
+
);
|
|
1734
|
+
|
|
1735
|
+
INSERT INTO sync_outbox_new (
|
|
1736
|
+
id, record_type, record_id, status, retry_count, max_retries,
|
|
1737
|
+
last_error, created_at_epoch, synced_at_epoch, next_retry_epoch
|
|
1738
|
+
)
|
|
1739
|
+
SELECT
|
|
1740
|
+
id, record_type, record_id, status, retry_count, max_retries,
|
|
1741
|
+
last_error, created_at_epoch, synced_at_epoch, next_retry_epoch
|
|
1742
|
+
FROM sync_outbox;
|
|
1743
|
+
|
|
1744
|
+
DROP TABLE sync_outbox;
|
|
1745
|
+
ALTER TABLE sync_outbox_new RENAME TO sync_outbox;
|
|
1746
|
+
|
|
1747
|
+
CREATE INDEX idx_outbox_status ON sync_outbox(status, next_retry_epoch);
|
|
1748
|
+
CREATE INDEX idx_outbox_record ON sync_outbox(record_type, record_id);
|
|
1749
|
+
`);
|
|
1750
|
+
db.exec("PRAGMA user_version = 16");
|
|
1751
|
+
db.exec("COMMIT");
|
|
1752
|
+
} catch (error) {
|
|
1753
|
+
db.exec("ROLLBACK");
|
|
1754
|
+
throw new Error(`sync_outbox repair failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
function syncOutboxSupportsChatMessages(db) {
|
|
1758
|
+
const row = db.query("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?").get("sync_outbox");
|
|
1759
|
+
const sql = row?.sql ?? "";
|
|
1760
|
+
return sql.includes("'chat_message'");
|
|
1761
|
+
}
|
|
1762
|
+
function getSchemaVersion(db) {
|
|
1763
|
+
const result = db.query("PRAGMA user_version").get();
|
|
1764
|
+
return result.user_version;
|
|
1765
|
+
}
|
|
1588
1766
|
var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
|
|
1589
1767
|
|
|
1590
1768
|
// src/storage/sqlite.ts
|
|
@@ -1739,6 +1917,9 @@ class MemDatabase {
|
|
|
1739
1917
|
this.vecAvailable = this.loadVecExtension();
|
|
1740
1918
|
runMigrations(this.db);
|
|
1741
1919
|
ensureObservationTypes(this.db);
|
|
1920
|
+
ensureSessionSummaryColumns(this.db);
|
|
1921
|
+
ensureChatMessageColumns(this.db);
|
|
1922
|
+
ensureSyncOutboxSupportsChatMessages(this.db);
|
|
1742
1923
|
}
|
|
1743
1924
|
loadVecExtension() {
|
|
1744
1925
|
try {
|
|
@@ -1964,6 +2145,7 @@ class MemDatabase {
|
|
|
1964
2145
|
p.name AS project_name,
|
|
1965
2146
|
ss.request AS request,
|
|
1966
2147
|
ss.completed AS completed,
|
|
2148
|
+
ss.current_thread AS current_thread,
|
|
1967
2149
|
ss.capture_state AS capture_state,
|
|
1968
2150
|
ss.recent_tool_names AS recent_tool_names,
|
|
1969
2151
|
ss.hot_files AS hot_files,
|
|
@@ -1982,6 +2164,7 @@ class MemDatabase {
|
|
|
1982
2164
|
p.name AS project_name,
|
|
1983
2165
|
ss.request AS request,
|
|
1984
2166
|
ss.completed AS completed,
|
|
2167
|
+
ss.current_thread AS current_thread,
|
|
1985
2168
|
ss.capture_state AS capture_state,
|
|
1986
2169
|
ss.recent_tool_names AS recent_tool_names,
|
|
1987
2170
|
ss.hot_files AS hot_files,
|
|
@@ -2072,6 +2255,54 @@ class MemDatabase {
|
|
|
2072
2255
|
ORDER BY created_at_epoch DESC, id DESC
|
|
2073
2256
|
LIMIT ?`).all(...userId ? [userId] : [], limit);
|
|
2074
2257
|
}
|
|
2258
|
+
insertChatMessage(input) {
|
|
2259
|
+
const createdAt = input.created_at_epoch ?? Math.floor(Date.now() / 1000);
|
|
2260
|
+
const content = input.content.trim();
|
|
2261
|
+
const result = this.db.query(`INSERT INTO chat_messages (
|
|
2262
|
+
session_id, project_id, role, content, user_id, device_id, agent, created_at_epoch, remote_source_id
|
|
2263
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.session_id, input.project_id, input.role, content, input.user_id, input.device_id, input.agent ?? "claude-code", createdAt, input.remote_source_id ?? null);
|
|
2264
|
+
return this.getChatMessageById(Number(result.lastInsertRowid));
|
|
2265
|
+
}
|
|
2266
|
+
getChatMessageById(id) {
|
|
2267
|
+
return this.db.query("SELECT * FROM chat_messages WHERE id = ?").get(id) ?? null;
|
|
2268
|
+
}
|
|
2269
|
+
getChatMessageByRemoteSourceId(remoteSourceId) {
|
|
2270
|
+
return this.db.query("SELECT * FROM chat_messages WHERE remote_source_id = ?").get(remoteSourceId) ?? null;
|
|
2271
|
+
}
|
|
2272
|
+
getSessionChatMessages(sessionId, limit = 50) {
|
|
2273
|
+
return this.db.query(`SELECT * FROM chat_messages
|
|
2274
|
+
WHERE session_id = ?
|
|
2275
|
+
ORDER BY created_at_epoch ASC, id ASC
|
|
2276
|
+
LIMIT ?`).all(sessionId, limit);
|
|
2277
|
+
}
|
|
2278
|
+
getRecentChatMessages(projectId, limit = 20, userId) {
|
|
2279
|
+
const visibilityClause = userId ? " AND user_id = ?" : "";
|
|
2280
|
+
if (projectId !== null) {
|
|
2281
|
+
return this.db.query(`SELECT * FROM chat_messages
|
|
2282
|
+
WHERE project_id = ?${visibilityClause}
|
|
2283
|
+
ORDER BY created_at_epoch DESC, id DESC
|
|
2284
|
+
LIMIT ?`).all(projectId, ...userId ? [userId] : [], limit);
|
|
2285
|
+
}
|
|
2286
|
+
return this.db.query(`SELECT * FROM chat_messages
|
|
2287
|
+
WHERE 1 = 1${visibilityClause}
|
|
2288
|
+
ORDER BY created_at_epoch DESC, id DESC
|
|
2289
|
+
LIMIT ?`).all(...userId ? [userId] : [], limit);
|
|
2290
|
+
}
|
|
2291
|
+
searchChatMessages(query, projectId, limit = 20, userId) {
|
|
2292
|
+
const needle = `%${query.toLowerCase()}%`;
|
|
2293
|
+
const visibilityClause = userId ? " AND user_id = ?" : "";
|
|
2294
|
+
if (projectId !== null) {
|
|
2295
|
+
return this.db.query(`SELECT * FROM chat_messages
|
|
2296
|
+
WHERE project_id = ?
|
|
2297
|
+
AND lower(content) LIKE ?${visibilityClause}
|
|
2298
|
+
ORDER BY created_at_epoch DESC, id DESC
|
|
2299
|
+
LIMIT ?`).all(projectId, needle, ...userId ? [userId] : [], limit);
|
|
2300
|
+
}
|
|
2301
|
+
return this.db.query(`SELECT * FROM chat_messages
|
|
2302
|
+
WHERE lower(content) LIKE ?${visibilityClause}
|
|
2303
|
+
ORDER BY created_at_epoch DESC, id DESC
|
|
2304
|
+
LIMIT ?`).all(needle, ...userId ? [userId] : [], limit);
|
|
2305
|
+
}
|
|
2075
2306
|
addToOutbox(recordType, recordId) {
|
|
2076
2307
|
const now = Math.floor(Date.now() / 1000);
|
|
2077
2308
|
this.db.query(`INSERT INTO sync_outbox (record_type, record_id, created_at_epoch)
|
|
@@ -2160,9 +2391,9 @@ class MemDatabase {
|
|
|
2160
2391
|
};
|
|
2161
2392
|
const result = this.db.query(`INSERT INTO session_summaries (
|
|
2162
2393
|
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
2163
|
-
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
2394
|
+
current_thread, capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
2164
2395
|
)
|
|
2165
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(summary.session_id, summary.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, summary.capture_state ?? null, summary.recent_tool_names ?? null, summary.hot_files ?? null, summary.recent_outcomes ?? null, now);
|
|
2396
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(summary.session_id, summary.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, summary.current_thread ?? null, summary.capture_state ?? null, summary.recent_tool_names ?? null, summary.hot_files ?? null, summary.recent_outcomes ?? null, now);
|
|
2166
2397
|
const id = Number(result.lastInsertRowid);
|
|
2167
2398
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
2168
2399
|
}
|
|
@@ -2178,6 +2409,7 @@ class MemDatabase {
|
|
|
2178
2409
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
2179
2410
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
2180
2411
|
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
2412
|
+
current_thread: summary.current_thread ?? existing.current_thread,
|
|
2181
2413
|
capture_state: summary.capture_state ?? existing.capture_state,
|
|
2182
2414
|
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
2183
2415
|
hot_files: summary.hot_files ?? existing.hot_files,
|
|
@@ -2191,12 +2423,13 @@ class MemDatabase {
|
|
|
2191
2423
|
learned = ?,
|
|
2192
2424
|
completed = ?,
|
|
2193
2425
|
next_steps = ?,
|
|
2426
|
+
current_thread = ?,
|
|
2194
2427
|
capture_state = ?,
|
|
2195
2428
|
recent_tool_names = ?,
|
|
2196
2429
|
hot_files = ?,
|
|
2197
2430
|
recent_outcomes = ?,
|
|
2198
2431
|
created_at_epoch = ?
|
|
2199
|
-
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, normalized.capture_state, normalized.recent_tool_names, normalized.hot_files, normalized.recent_outcomes, now, summary.session_id);
|
|
2432
|
+
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, normalized.current_thread, normalized.capture_state, normalized.recent_tool_names, normalized.hot_files, normalized.recent_outcomes, now, summary.session_id);
|
|
2200
2433
|
return this.getSessionSummary(summary.session_id);
|
|
2201
2434
|
}
|
|
2202
2435
|
getSessionSummary(sessionId) {
|