engrm 0.4.18 → 0.4.21
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/dist/cli.js +43 -4
- package/dist/hooks/elicitation-result.js +33 -4
- package/dist/hooks/post-tool-use.js +118 -6
- package/dist/hooks/pre-compact.js +66 -7
- package/dist/hooks/sentinel.js +33 -4
- package/dist/hooks/session-start.js +120 -21
- package/dist/hooks/stop.js +199 -87
- package/dist/hooks/user-prompt-submit.js +33 -4
- package/dist/server.js +78 -9
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -598,6 +598,16 @@ var MIGRATIONS = [
|
|
|
598
598
|
ON tool_events(created_at_epoch DESC, id DESC);
|
|
599
599
|
`
|
|
600
600
|
},
|
|
601
|
+
{
|
|
602
|
+
version: 11,
|
|
603
|
+
description: "Add synced handoff metadata to session summaries",
|
|
604
|
+
sql: `
|
|
605
|
+
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
606
|
+
ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
|
|
607
|
+
ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
|
|
608
|
+
ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
|
|
609
|
+
`
|
|
610
|
+
},
|
|
601
611
|
{
|
|
602
612
|
version: 11,
|
|
603
613
|
description: "Add observation provenance from tool and prompt chronology",
|
|
@@ -1125,6 +1135,10 @@ class MemDatabase {
|
|
|
1125
1135
|
p.name AS project_name,
|
|
1126
1136
|
ss.request AS request,
|
|
1127
1137
|
ss.completed AS completed,
|
|
1138
|
+
ss.capture_state AS capture_state,
|
|
1139
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1140
|
+
ss.hot_files AS hot_files,
|
|
1141
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1128
1142
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1129
1143
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1130
1144
|
FROM sessions s
|
|
@@ -1139,6 +1153,10 @@ class MemDatabase {
|
|
|
1139
1153
|
p.name AS project_name,
|
|
1140
1154
|
ss.request AS request,
|
|
1141
1155
|
ss.completed AS completed,
|
|
1156
|
+
ss.capture_state AS capture_state,
|
|
1157
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1158
|
+
ss.hot_files AS hot_files,
|
|
1159
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1142
1160
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1143
1161
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1144
1162
|
FROM sessions s
|
|
@@ -1311,8 +1329,11 @@ class MemDatabase {
|
|
|
1311
1329
|
completed: normalizeSummarySection(summary.completed),
|
|
1312
1330
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
1313
1331
|
};
|
|
1314
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1315
|
-
|
|
1332
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1333
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
1334
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
1335
|
+
)
|
|
1336
|
+
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);
|
|
1316
1337
|
const id = Number(result.lastInsertRowid);
|
|
1317
1338
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
1318
1339
|
}
|
|
@@ -1327,7 +1348,11 @@ class MemDatabase {
|
|
|
1327
1348
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
1328
1349
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
1329
1350
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
1330
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
1351
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
1352
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
1353
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
1354
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
1355
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
1331
1356
|
};
|
|
1332
1357
|
this.db.query(`UPDATE session_summaries
|
|
1333
1358
|
SET project_id = ?,
|
|
@@ -1337,8 +1362,12 @@ class MemDatabase {
|
|
|
1337
1362
|
learned = ?,
|
|
1338
1363
|
completed = ?,
|
|
1339
1364
|
next_steps = ?,
|
|
1365
|
+
capture_state = ?,
|
|
1366
|
+
recent_tool_names = ?,
|
|
1367
|
+
hot_files = ?,
|
|
1368
|
+
recent_outcomes = ?,
|
|
1340
1369
|
created_at_epoch = ?
|
|
1341
|
-
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now, summary.session_id);
|
|
1370
|
+
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);
|
|
1342
1371
|
return this.getSessionSummary(summary.session_id);
|
|
1343
1372
|
}
|
|
1344
1373
|
getSessionSummary(sessionId) {
|
|
@@ -1843,6 +1872,16 @@ var MIGRATIONS2 = [
|
|
|
1843
1872
|
ON tool_events(created_at_epoch DESC, id DESC);
|
|
1844
1873
|
`
|
|
1845
1874
|
},
|
|
1875
|
+
{
|
|
1876
|
+
version: 11,
|
|
1877
|
+
description: "Add synced handoff metadata to session summaries",
|
|
1878
|
+
sql: `
|
|
1879
|
+
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
1880
|
+
ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
|
|
1881
|
+
ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
|
|
1882
|
+
ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
|
|
1883
|
+
`
|
|
1884
|
+
},
|
|
1846
1885
|
{
|
|
1847
1886
|
version: 11,
|
|
1848
1887
|
description: "Add observation provenance from tool and prompt chronology",
|
|
@@ -1431,6 +1431,16 @@ var MIGRATIONS = [
|
|
|
1431
1431
|
ON tool_events(created_at_epoch DESC, id DESC);
|
|
1432
1432
|
`
|
|
1433
1433
|
},
|
|
1434
|
+
{
|
|
1435
|
+
version: 11,
|
|
1436
|
+
description: "Add synced handoff metadata to session summaries",
|
|
1437
|
+
sql: `
|
|
1438
|
+
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
1439
|
+
ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
|
|
1440
|
+
ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
|
|
1441
|
+
ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
|
|
1442
|
+
`
|
|
1443
|
+
},
|
|
1434
1444
|
{
|
|
1435
1445
|
version: 11,
|
|
1436
1446
|
description: "Add observation provenance from tool and prompt chronology",
|
|
@@ -1954,6 +1964,10 @@ class MemDatabase {
|
|
|
1954
1964
|
p.name AS project_name,
|
|
1955
1965
|
ss.request AS request,
|
|
1956
1966
|
ss.completed AS completed,
|
|
1967
|
+
ss.capture_state AS capture_state,
|
|
1968
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1969
|
+
ss.hot_files AS hot_files,
|
|
1970
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1957
1971
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1958
1972
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1959
1973
|
FROM sessions s
|
|
@@ -1968,6 +1982,10 @@ class MemDatabase {
|
|
|
1968
1982
|
p.name AS project_name,
|
|
1969
1983
|
ss.request AS request,
|
|
1970
1984
|
ss.completed AS completed,
|
|
1985
|
+
ss.capture_state AS capture_state,
|
|
1986
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1987
|
+
ss.hot_files AS hot_files,
|
|
1988
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1971
1989
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1972
1990
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1973
1991
|
FROM sessions s
|
|
@@ -2140,8 +2158,11 @@ class MemDatabase {
|
|
|
2140
2158
|
completed: normalizeSummarySection(summary.completed),
|
|
2141
2159
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
2142
2160
|
};
|
|
2143
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
2144
|
-
|
|
2161
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
2162
|
+
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
|
|
2164
|
+
)
|
|
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);
|
|
2145
2166
|
const id = Number(result.lastInsertRowid);
|
|
2146
2167
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
2147
2168
|
}
|
|
@@ -2156,7 +2177,11 @@ class MemDatabase {
|
|
|
2156
2177
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
2157
2178
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
2158
2179
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
2159
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
2180
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
2181
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
2182
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
2183
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
2184
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
2160
2185
|
};
|
|
2161
2186
|
this.db.query(`UPDATE session_summaries
|
|
2162
2187
|
SET project_id = ?,
|
|
@@ -2166,8 +2191,12 @@ class MemDatabase {
|
|
|
2166
2191
|
learned = ?,
|
|
2167
2192
|
completed = ?,
|
|
2168
2193
|
next_steps = ?,
|
|
2194
|
+
capture_state = ?,
|
|
2195
|
+
recent_tool_names = ?,
|
|
2196
|
+
hot_files = ?,
|
|
2197
|
+
recent_outcomes = ?,
|
|
2169
2198
|
created_at_epoch = ?
|
|
2170
|
-
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now, summary.session_id);
|
|
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);
|
|
2171
2200
|
return this.getSessionSummary(summary.session_id);
|
|
2172
2201
|
}
|
|
2173
2202
|
getSessionSummary(sessionId) {
|
|
@@ -776,6 +776,16 @@ var MIGRATIONS = [
|
|
|
776
776
|
ON tool_events(created_at_epoch DESC, id DESC);
|
|
777
777
|
`
|
|
778
778
|
},
|
|
779
|
+
{
|
|
780
|
+
version: 11,
|
|
781
|
+
description: "Add synced handoff metadata to session summaries",
|
|
782
|
+
sql: `
|
|
783
|
+
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
784
|
+
ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
|
|
785
|
+
ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
|
|
786
|
+
ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
|
|
787
|
+
`
|
|
788
|
+
},
|
|
779
789
|
{
|
|
780
790
|
version: 11,
|
|
781
791
|
description: "Add observation provenance from tool and prompt chronology",
|
|
@@ -1299,6 +1309,10 @@ class MemDatabase {
|
|
|
1299
1309
|
p.name AS project_name,
|
|
1300
1310
|
ss.request AS request,
|
|
1301
1311
|
ss.completed AS completed,
|
|
1312
|
+
ss.capture_state AS capture_state,
|
|
1313
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1314
|
+
ss.hot_files AS hot_files,
|
|
1315
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1302
1316
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1303
1317
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1304
1318
|
FROM sessions s
|
|
@@ -1313,6 +1327,10 @@ class MemDatabase {
|
|
|
1313
1327
|
p.name AS project_name,
|
|
1314
1328
|
ss.request AS request,
|
|
1315
1329
|
ss.completed AS completed,
|
|
1330
|
+
ss.capture_state AS capture_state,
|
|
1331
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1332
|
+
ss.hot_files AS hot_files,
|
|
1333
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1316
1334
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1317
1335
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1318
1336
|
FROM sessions s
|
|
@@ -1485,8 +1503,11 @@ class MemDatabase {
|
|
|
1485
1503
|
completed: normalizeSummarySection(summary.completed),
|
|
1486
1504
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
1487
1505
|
};
|
|
1488
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1489
|
-
|
|
1506
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1507
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
1508
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
1509
|
+
)
|
|
1510
|
+
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);
|
|
1490
1511
|
const id = Number(result.lastInsertRowid);
|
|
1491
1512
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
1492
1513
|
}
|
|
@@ -1501,7 +1522,11 @@ class MemDatabase {
|
|
|
1501
1522
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
1502
1523
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
1503
1524
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
1504
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
1525
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
1526
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
1527
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
1528
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
1529
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
1505
1530
|
};
|
|
1506
1531
|
this.db.query(`UPDATE session_summaries
|
|
1507
1532
|
SET project_id = ?,
|
|
@@ -1511,8 +1536,12 @@ class MemDatabase {
|
|
|
1511
1536
|
learned = ?,
|
|
1512
1537
|
completed = ?,
|
|
1513
1538
|
next_steps = ?,
|
|
1539
|
+
capture_state = ?,
|
|
1540
|
+
recent_tool_names = ?,
|
|
1541
|
+
hot_files = ?,
|
|
1542
|
+
recent_outcomes = ?,
|
|
1514
1543
|
created_at_epoch = ?
|
|
1515
|
-
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now, summary.session_id);
|
|
1544
|
+
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);
|
|
1516
1545
|
return this.getSessionSummary(summary.session_id);
|
|
1517
1546
|
}
|
|
1518
1547
|
getSessionSummary(sessionId) {
|
|
@@ -3166,6 +3195,63 @@ function checkSessionFatigue(db, sessionId) {
|
|
|
3166
3195
|
};
|
|
3167
3196
|
}
|
|
3168
3197
|
|
|
3198
|
+
// src/capture/live-summary.ts
|
|
3199
|
+
var LOW_SIGNAL_TITLE_PATTERNS = [
|
|
3200
|
+
/^(Modified|Extended|Reduced|Created)\s+\S+/i,
|
|
3201
|
+
/^Dependency change:/i,
|
|
3202
|
+
/^[a-z0-9._-]+\s*\(error\)$/i,
|
|
3203
|
+
/^(engrm|unknown):\s+/i
|
|
3204
|
+
];
|
|
3205
|
+
function buildLiveSummaryUpdate(observation) {
|
|
3206
|
+
const title = compactSummaryTitle(observation.title);
|
|
3207
|
+
if (!title)
|
|
3208
|
+
return null;
|
|
3209
|
+
switch (observation.type) {
|
|
3210
|
+
case "discovery":
|
|
3211
|
+
case "pattern":
|
|
3212
|
+
return { investigated: title };
|
|
3213
|
+
case "decision":
|
|
3214
|
+
return { learned: title };
|
|
3215
|
+
case "bugfix":
|
|
3216
|
+
case "feature":
|
|
3217
|
+
case "change":
|
|
3218
|
+
case "refactor":
|
|
3219
|
+
return { completed: title };
|
|
3220
|
+
default:
|
|
3221
|
+
return null;
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
function compactSummaryTitle(title) {
|
|
3225
|
+
const trimmed = title?.trim();
|
|
3226
|
+
if (!trimmed)
|
|
3227
|
+
return null;
|
|
3228
|
+
if (LOW_SIGNAL_TITLE_PATTERNS.some((pattern) => pattern.test(trimmed))) {
|
|
3229
|
+
return null;
|
|
3230
|
+
}
|
|
3231
|
+
return trimmed.length > 180 ? `${trimmed.slice(0, 177)}...` : trimmed;
|
|
3232
|
+
}
|
|
3233
|
+
function mergeLiveSummarySections(existing, update) {
|
|
3234
|
+
return {
|
|
3235
|
+
investigated: mergeSectionItem(existing?.investigated ?? null, update.investigated ?? null),
|
|
3236
|
+
learned: mergeSectionItem(existing?.learned ?? null, update.learned ?? null),
|
|
3237
|
+
completed: mergeSectionItem(existing?.completed ?? null, update.completed ?? null)
|
|
3238
|
+
};
|
|
3239
|
+
}
|
|
3240
|
+
function mergeSectionItem(existing, item) {
|
|
3241
|
+
if (!item)
|
|
3242
|
+
return existing;
|
|
3243
|
+
if (!existing)
|
|
3244
|
+
return item;
|
|
3245
|
+
const lines = existing.split(`
|
|
3246
|
+
`).map((line) => line.replace(/^\s*-\s*/, "").trim()).filter(Boolean);
|
|
3247
|
+
const normalizedItem = item.trim();
|
|
3248
|
+
if (lines.some((line) => line.toLowerCase() === normalizedItem.toLowerCase())) {
|
|
3249
|
+
return existing;
|
|
3250
|
+
}
|
|
3251
|
+
return `${existing}
|
|
3252
|
+
- ${normalizedItem}`;
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3169
3255
|
// hooks/post-tool-use.ts
|
|
3170
3256
|
async function main() {
|
|
3171
3257
|
const raw = await readStdin();
|
|
@@ -3269,7 +3355,8 @@ async function main() {
|
|
|
3269
3355
|
timeoutMs: 800
|
|
3270
3356
|
}), 1000);
|
|
3271
3357
|
if (observed) {
|
|
3272
|
-
await saveObservation(db, config, observed);
|
|
3358
|
+
const result = await saveObservation(db, config, observed);
|
|
3359
|
+
updateRollingSummaryFromObservation(db, result.observation_id, event, config.user_id);
|
|
3273
3360
|
incrementObserverSaveCount(event.session_id);
|
|
3274
3361
|
saved = true;
|
|
3275
3362
|
}
|
|
@@ -3278,7 +3365,7 @@ async function main() {
|
|
|
3278
3365
|
if (!saved) {
|
|
3279
3366
|
const extracted = extractObservation(event);
|
|
3280
3367
|
if (extracted) {
|
|
3281
|
-
await saveObservation(db, config, {
|
|
3368
|
+
const result = await saveObservation(db, config, {
|
|
3282
3369
|
type: extracted.type,
|
|
3283
3370
|
title: extracted.title,
|
|
3284
3371
|
narrative: extracted.narrative,
|
|
@@ -3288,6 +3375,7 @@ async function main() {
|
|
|
3288
3375
|
cwd: event.cwd,
|
|
3289
3376
|
source_tool: event.tool_name
|
|
3290
3377
|
});
|
|
3378
|
+
updateRollingSummaryFromObservation(db, result.observation_id, event, config.user_id);
|
|
3291
3379
|
incrementObserverSaveCount(event.session_id);
|
|
3292
3380
|
}
|
|
3293
3381
|
}
|
|
@@ -3352,6 +3440,30 @@ function detectProjectForEvent(event) {
|
|
|
3352
3440
|
const touchedPaths = extractTouchedPaths(event);
|
|
3353
3441
|
return touchedPaths.length > 0 ? detectProjectFromTouchedPaths(touchedPaths, event.cwd) : detectProject(event.cwd);
|
|
3354
3442
|
}
|
|
3443
|
+
function updateRollingSummaryFromObservation(db, observationId, event, userId) {
|
|
3444
|
+
if (!observationId || !event.session_id)
|
|
3445
|
+
return;
|
|
3446
|
+
const observation = db.getObservationById(observationId);
|
|
3447
|
+
if (!observation)
|
|
3448
|
+
return;
|
|
3449
|
+
const update = buildLiveSummaryUpdate(observation);
|
|
3450
|
+
if (!update)
|
|
3451
|
+
return;
|
|
3452
|
+
const existing = db.getSessionSummary(event.session_id);
|
|
3453
|
+
const merged = mergeLiveSummarySections(existing, update);
|
|
3454
|
+
const currentRequest = existing?.request ?? db.getSessionUserPrompts(event.session_id, 1).at(-1)?.prompt ?? null;
|
|
3455
|
+
const summary = db.upsertSessionSummary({
|
|
3456
|
+
session_id: event.session_id,
|
|
3457
|
+
project_id: observation.project_id,
|
|
3458
|
+
user_id: userId,
|
|
3459
|
+
request: currentRequest,
|
|
3460
|
+
investigated: merged.investigated,
|
|
3461
|
+
learned: merged.learned,
|
|
3462
|
+
completed: merged.completed,
|
|
3463
|
+
next_steps: existing?.next_steps ?? null
|
|
3464
|
+
});
|
|
3465
|
+
db.addToOutbox("summary", summary.id);
|
|
3466
|
+
}
|
|
3355
3467
|
function extractTouchedPaths(event) {
|
|
3356
3468
|
const paths = [];
|
|
3357
3469
|
const filePath = event.tool_input["file_path"];
|
|
@@ -570,6 +570,16 @@ var MIGRATIONS = [
|
|
|
570
570
|
ON tool_events(created_at_epoch DESC, id DESC);
|
|
571
571
|
`
|
|
572
572
|
},
|
|
573
|
+
{
|
|
574
|
+
version: 11,
|
|
575
|
+
description: "Add synced handoff metadata to session summaries",
|
|
576
|
+
sql: `
|
|
577
|
+
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
578
|
+
ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
|
|
579
|
+
ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
|
|
580
|
+
ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
|
|
581
|
+
`
|
|
582
|
+
},
|
|
573
583
|
{
|
|
574
584
|
version: 11,
|
|
575
585
|
description: "Add observation provenance from tool and prompt chronology",
|
|
@@ -1093,6 +1103,10 @@ class MemDatabase {
|
|
|
1093
1103
|
p.name AS project_name,
|
|
1094
1104
|
ss.request AS request,
|
|
1095
1105
|
ss.completed AS completed,
|
|
1106
|
+
ss.capture_state AS capture_state,
|
|
1107
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1108
|
+
ss.hot_files AS hot_files,
|
|
1109
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1096
1110
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1097
1111
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1098
1112
|
FROM sessions s
|
|
@@ -1107,6 +1121,10 @@ class MemDatabase {
|
|
|
1107
1121
|
p.name AS project_name,
|
|
1108
1122
|
ss.request AS request,
|
|
1109
1123
|
ss.completed AS completed,
|
|
1124
|
+
ss.capture_state AS capture_state,
|
|
1125
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1126
|
+
ss.hot_files AS hot_files,
|
|
1127
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1110
1128
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1111
1129
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1112
1130
|
FROM sessions s
|
|
@@ -1279,8 +1297,11 @@ class MemDatabase {
|
|
|
1279
1297
|
completed: normalizeSummarySection(summary.completed),
|
|
1280
1298
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
1281
1299
|
};
|
|
1282
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1283
|
-
|
|
1300
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1301
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
1302
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
1303
|
+
)
|
|
1304
|
+
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);
|
|
1284
1305
|
const id = Number(result.lastInsertRowid);
|
|
1285
1306
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
1286
1307
|
}
|
|
@@ -1295,7 +1316,11 @@ class MemDatabase {
|
|
|
1295
1316
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
1296
1317
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
1297
1318
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
1298
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
1319
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
1320
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
1321
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
1322
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
1323
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
1299
1324
|
};
|
|
1300
1325
|
this.db.query(`UPDATE session_summaries
|
|
1301
1326
|
SET project_id = ?,
|
|
@@ -1305,8 +1330,12 @@ class MemDatabase {
|
|
|
1305
1330
|
learned = ?,
|
|
1306
1331
|
completed = ?,
|
|
1307
1332
|
next_steps = ?,
|
|
1333
|
+
capture_state = ?,
|
|
1334
|
+
recent_tool_names = ?,
|
|
1335
|
+
hot_files = ?,
|
|
1336
|
+
recent_outcomes = ?,
|
|
1308
1337
|
created_at_epoch = ?
|
|
1309
|
-
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now, summary.session_id);
|
|
1338
|
+
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);
|
|
1310
1339
|
return this.getSessionSummary(summary.session_id);
|
|
1311
1340
|
}
|
|
1312
1341
|
getSessionSummary(sessionId) {
|
|
@@ -1790,6 +1819,16 @@ function computeObservationPriority(obs, nowEpoch) {
|
|
|
1790
1819
|
function tokenizeProjectHint(text) {
|
|
1791
1820
|
return Array.from(new Set((text.toLowerCase().match(/[a-z0-9_+-]{4,}/g) ?? []).filter(Boolean)));
|
|
1792
1821
|
}
|
|
1822
|
+
function parseSummaryJsonList(value) {
|
|
1823
|
+
if (!value)
|
|
1824
|
+
return [];
|
|
1825
|
+
try {
|
|
1826
|
+
const parsed = JSON.parse(value);
|
|
1827
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
1828
|
+
} catch {
|
|
1829
|
+
return [];
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1793
1832
|
function isObservationRelatedToProject(obs, detected) {
|
|
1794
1833
|
const hints = new Set([
|
|
1795
1834
|
...tokenizeProjectHint(detected.name),
|
|
@@ -1921,7 +1960,7 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
1921
1960
|
const recentToolEvents2 = db.getRecentToolEvents(isNewProject ? null : projectId, isNewProject ? 8 : 6, opts.userId);
|
|
1922
1961
|
const recentSessions2 = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
|
|
1923
1962
|
const projectTypeCounts2 = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
|
|
1924
|
-
const recentOutcomes2 = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId);
|
|
1963
|
+
const recentOutcomes2 = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions2);
|
|
1925
1964
|
return {
|
|
1926
1965
|
project_name: projectName,
|
|
1927
1966
|
canonical_id: canonicalId,
|
|
@@ -1959,7 +1998,7 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
1959
1998
|
const recentToolEvents = db.getRecentToolEvents(isNewProject ? null : projectId, isNewProject ? 8 : 6, opts.userId);
|
|
1960
1999
|
const recentSessions = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
|
|
1961
2000
|
const projectTypeCounts = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
|
|
1962
|
-
const recentOutcomes = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId);
|
|
2001
|
+
const recentOutcomes = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions);
|
|
1963
2002
|
let securityFindings = [];
|
|
1964
2003
|
if (!isNewProject) {
|
|
1965
2004
|
try {
|
|
@@ -2302,7 +2341,7 @@ function getProjectTypeCounts(db, projectId, userId) {
|
|
|
2302
2341
|
}
|
|
2303
2342
|
return counts;
|
|
2304
2343
|
}
|
|
2305
|
-
function getRecentOutcomes(db, projectId, userId) {
|
|
2344
|
+
function getRecentOutcomes(db, projectId, userId, recentSessions) {
|
|
2306
2345
|
const visibilityClause = userId ? " AND (sensitivity != 'personal' OR user_id = ?)" : "";
|
|
2307
2346
|
const visibilityParams = userId ? [userId] : [];
|
|
2308
2347
|
const summaries = db.db.query(`SELECT * FROM session_summaries
|
|
@@ -2312,6 +2351,15 @@ function getRecentOutcomes(db, projectId, userId) {
|
|
|
2312
2351
|
const picked = [];
|
|
2313
2352
|
const seen = new Set;
|
|
2314
2353
|
for (const summary of summaries) {
|
|
2354
|
+
for (const item of parseSummaryJsonList(summary.recent_outcomes)) {
|
|
2355
|
+
const normalized = item.toLowerCase().replace(/\s+/g, " ").trim();
|
|
2356
|
+
if (!normalized || seen.has(normalized))
|
|
2357
|
+
continue;
|
|
2358
|
+
seen.add(normalized);
|
|
2359
|
+
picked.push(item);
|
|
2360
|
+
if (picked.length >= 5)
|
|
2361
|
+
return picked;
|
|
2362
|
+
}
|
|
2315
2363
|
for (const line of [
|
|
2316
2364
|
...extractMeaningfulLines(summary.completed, 2),
|
|
2317
2365
|
...extractMeaningfulLines(summary.learned, 1)
|
|
@@ -2325,6 +2373,17 @@ function getRecentOutcomes(db, projectId, userId) {
|
|
|
2325
2373
|
return picked;
|
|
2326
2374
|
}
|
|
2327
2375
|
}
|
|
2376
|
+
for (const session of recentSessions ?? []) {
|
|
2377
|
+
for (const item of parseSummaryJsonList(session.recent_outcomes)) {
|
|
2378
|
+
const normalized = item.toLowerCase().replace(/\s+/g, " ").trim();
|
|
2379
|
+
if (!normalized || seen.has(normalized))
|
|
2380
|
+
continue;
|
|
2381
|
+
seen.add(normalized);
|
|
2382
|
+
picked.push(item);
|
|
2383
|
+
if (picked.length >= 5)
|
|
2384
|
+
return picked;
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2328
2387
|
const rows = db.db.query(`SELECT * FROM observations
|
|
2329
2388
|
WHERE project_id = ?
|
|
2330
2389
|
AND lifecycle IN ('active', 'aging', 'pinned')
|
package/dist/hooks/sentinel.js
CHANGED
|
@@ -646,6 +646,16 @@ var MIGRATIONS = [
|
|
|
646
646
|
ON tool_events(created_at_epoch DESC, id DESC);
|
|
647
647
|
`
|
|
648
648
|
},
|
|
649
|
+
{
|
|
650
|
+
version: 11,
|
|
651
|
+
description: "Add synced handoff metadata to session summaries",
|
|
652
|
+
sql: `
|
|
653
|
+
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
654
|
+
ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
|
|
655
|
+
ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
|
|
656
|
+
ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
|
|
657
|
+
`
|
|
658
|
+
},
|
|
649
659
|
{
|
|
650
660
|
version: 11,
|
|
651
661
|
description: "Add observation provenance from tool and prompt chronology",
|
|
@@ -1169,6 +1179,10 @@ class MemDatabase {
|
|
|
1169
1179
|
p.name AS project_name,
|
|
1170
1180
|
ss.request AS request,
|
|
1171
1181
|
ss.completed AS completed,
|
|
1182
|
+
ss.capture_state AS capture_state,
|
|
1183
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1184
|
+
ss.hot_files AS hot_files,
|
|
1185
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1172
1186
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1173
1187
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1174
1188
|
FROM sessions s
|
|
@@ -1183,6 +1197,10 @@ class MemDatabase {
|
|
|
1183
1197
|
p.name AS project_name,
|
|
1184
1198
|
ss.request AS request,
|
|
1185
1199
|
ss.completed AS completed,
|
|
1200
|
+
ss.capture_state AS capture_state,
|
|
1201
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1202
|
+
ss.hot_files AS hot_files,
|
|
1203
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1186
1204
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1187
1205
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1188
1206
|
FROM sessions s
|
|
@@ -1355,8 +1373,11 @@ class MemDatabase {
|
|
|
1355
1373
|
completed: normalizeSummarySection(summary.completed),
|
|
1356
1374
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
1357
1375
|
};
|
|
1358
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1359
|
-
|
|
1376
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1377
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
1378
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
1379
|
+
)
|
|
1380
|
+
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);
|
|
1360
1381
|
const id = Number(result.lastInsertRowid);
|
|
1361
1382
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
1362
1383
|
}
|
|
@@ -1371,7 +1392,11 @@ class MemDatabase {
|
|
|
1371
1392
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
1372
1393
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
1373
1394
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
1374
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
1395
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
1396
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
1397
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
1398
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
1399
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
1375
1400
|
};
|
|
1376
1401
|
this.db.query(`UPDATE session_summaries
|
|
1377
1402
|
SET project_id = ?,
|
|
@@ -1381,8 +1406,12 @@ class MemDatabase {
|
|
|
1381
1406
|
learned = ?,
|
|
1382
1407
|
completed = ?,
|
|
1383
1408
|
next_steps = ?,
|
|
1409
|
+
capture_state = ?,
|
|
1410
|
+
recent_tool_names = ?,
|
|
1411
|
+
hot_files = ?,
|
|
1412
|
+
recent_outcomes = ?,
|
|
1384
1413
|
created_at_epoch = ?
|
|
1385
|
-
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now, summary.session_id);
|
|
1414
|
+
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);
|
|
1386
1415
|
return this.getSessionSummary(summary.session_id);
|
|
1387
1416
|
}
|
|
1388
1417
|
getSessionSummary(sessionId) {
|