engrm 0.4.19 → 0.4.22
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 +64 -4
- package/dist/hooks/elicitation-result.js +58 -4
- package/dist/hooks/post-tool-use.js +195 -6
- package/dist/hooks/pre-compact.js +91 -7
- package/dist/hooks/sentinel.js +58 -4
- package/dist/hooks/session-start.js +139 -11
- package/dist/hooks/stop.js +106 -50
- package/dist/hooks/user-prompt-submit.js +111 -5
- package/dist/server.js +144 -52
- 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: 12,
|
|
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",
|
|
@@ -664,6 +674,9 @@ function inferLegacySchemaVersion(db) {
|
|
|
664
674
|
version = Math.max(version, 10);
|
|
665
675
|
if (columnExists(db, "observations", "source_tool"))
|
|
666
676
|
version = Math.max(version, 11);
|
|
677
|
+
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")) {
|
|
678
|
+
version = Math.max(version, 12);
|
|
679
|
+
}
|
|
667
680
|
return version;
|
|
668
681
|
}
|
|
669
682
|
function runMigrations(db) {
|
|
@@ -742,6 +755,23 @@ function ensureObservationTypes(db) {
|
|
|
742
755
|
}
|
|
743
756
|
}
|
|
744
757
|
}
|
|
758
|
+
function ensureSessionSummaryColumns(db) {
|
|
759
|
+
const required = [
|
|
760
|
+
"capture_state",
|
|
761
|
+
"recent_tool_names",
|
|
762
|
+
"hot_files",
|
|
763
|
+
"recent_outcomes"
|
|
764
|
+
];
|
|
765
|
+
for (const column of required) {
|
|
766
|
+
if (columnExists(db, "session_summaries", column))
|
|
767
|
+
continue;
|
|
768
|
+
db.exec(`ALTER TABLE session_summaries ADD COLUMN ${column} TEXT`);
|
|
769
|
+
}
|
|
770
|
+
const current = getSchemaVersion(db);
|
|
771
|
+
if (current < 12) {
|
|
772
|
+
db.exec("PRAGMA user_version = 12");
|
|
773
|
+
}
|
|
774
|
+
}
|
|
745
775
|
function getSchemaVersion(db) {
|
|
746
776
|
const result = db.query("PRAGMA user_version").get();
|
|
747
777
|
return result.user_version;
|
|
@@ -900,6 +930,7 @@ class MemDatabase {
|
|
|
900
930
|
this.vecAvailable = this.loadVecExtension();
|
|
901
931
|
runMigrations(this.db);
|
|
902
932
|
ensureObservationTypes(this.db);
|
|
933
|
+
ensureSessionSummaryColumns(this.db);
|
|
903
934
|
}
|
|
904
935
|
loadVecExtension() {
|
|
905
936
|
try {
|
|
@@ -1125,6 +1156,10 @@ class MemDatabase {
|
|
|
1125
1156
|
p.name AS project_name,
|
|
1126
1157
|
ss.request AS request,
|
|
1127
1158
|
ss.completed AS completed,
|
|
1159
|
+
ss.capture_state AS capture_state,
|
|
1160
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1161
|
+
ss.hot_files AS hot_files,
|
|
1162
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1128
1163
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1129
1164
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1130
1165
|
FROM sessions s
|
|
@@ -1139,6 +1174,10 @@ class MemDatabase {
|
|
|
1139
1174
|
p.name AS project_name,
|
|
1140
1175
|
ss.request AS request,
|
|
1141
1176
|
ss.completed AS completed,
|
|
1177
|
+
ss.capture_state AS capture_state,
|
|
1178
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1179
|
+
ss.hot_files AS hot_files,
|
|
1180
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1142
1181
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1143
1182
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1144
1183
|
FROM sessions s
|
|
@@ -1311,8 +1350,11 @@ class MemDatabase {
|
|
|
1311
1350
|
completed: normalizeSummarySection(summary.completed),
|
|
1312
1351
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
1313
1352
|
};
|
|
1314
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1315
|
-
|
|
1353
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1354
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
1355
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
1356
|
+
)
|
|
1357
|
+
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
1358
|
const id = Number(result.lastInsertRowid);
|
|
1317
1359
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
1318
1360
|
}
|
|
@@ -1327,7 +1369,11 @@ class MemDatabase {
|
|
|
1327
1369
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
1328
1370
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
1329
1371
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
1330
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
1372
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
1373
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
1374
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
1375
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
1376
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
1331
1377
|
};
|
|
1332
1378
|
this.db.query(`UPDATE session_summaries
|
|
1333
1379
|
SET project_id = ?,
|
|
@@ -1337,8 +1383,12 @@ class MemDatabase {
|
|
|
1337
1383
|
learned = ?,
|
|
1338
1384
|
completed = ?,
|
|
1339
1385
|
next_steps = ?,
|
|
1386
|
+
capture_state = ?,
|
|
1387
|
+
recent_tool_names = ?,
|
|
1388
|
+
hot_files = ?,
|
|
1389
|
+
recent_outcomes = ?,
|
|
1340
1390
|
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);
|
|
1391
|
+
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
1392
|
return this.getSessionSummary(summary.session_id);
|
|
1343
1393
|
}
|
|
1344
1394
|
getSessionSummary(sessionId) {
|
|
@@ -1843,6 +1893,16 @@ var MIGRATIONS2 = [
|
|
|
1843
1893
|
ON tool_events(created_at_epoch DESC, id DESC);
|
|
1844
1894
|
`
|
|
1845
1895
|
},
|
|
1896
|
+
{
|
|
1897
|
+
version: 12,
|
|
1898
|
+
description: "Add synced handoff metadata to session summaries",
|
|
1899
|
+
sql: `
|
|
1900
|
+
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
1901
|
+
ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
|
|
1902
|
+
ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
|
|
1903
|
+
ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
|
|
1904
|
+
`
|
|
1905
|
+
},
|
|
1846
1906
|
{
|
|
1847
1907
|
version: 11,
|
|
1848
1908
|
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: 12,
|
|
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",
|
|
@@ -1497,6 +1507,9 @@ function inferLegacySchemaVersion(db) {
|
|
|
1497
1507
|
version = Math.max(version, 10);
|
|
1498
1508
|
if (columnExists(db, "observations", "source_tool"))
|
|
1499
1509
|
version = Math.max(version, 11);
|
|
1510
|
+
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")) {
|
|
1511
|
+
version = Math.max(version, 12);
|
|
1512
|
+
}
|
|
1500
1513
|
return version;
|
|
1501
1514
|
}
|
|
1502
1515
|
function runMigrations(db) {
|
|
@@ -1575,6 +1588,27 @@ function ensureObservationTypes(db) {
|
|
|
1575
1588
|
}
|
|
1576
1589
|
}
|
|
1577
1590
|
}
|
|
1591
|
+
function ensureSessionSummaryColumns(db) {
|
|
1592
|
+
const required = [
|
|
1593
|
+
"capture_state",
|
|
1594
|
+
"recent_tool_names",
|
|
1595
|
+
"hot_files",
|
|
1596
|
+
"recent_outcomes"
|
|
1597
|
+
];
|
|
1598
|
+
for (const column of required) {
|
|
1599
|
+
if (columnExists(db, "session_summaries", column))
|
|
1600
|
+
continue;
|
|
1601
|
+
db.exec(`ALTER TABLE session_summaries ADD COLUMN ${column} TEXT`);
|
|
1602
|
+
}
|
|
1603
|
+
const current = getSchemaVersion(db);
|
|
1604
|
+
if (current < 12) {
|
|
1605
|
+
db.exec("PRAGMA user_version = 12");
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
function getSchemaVersion(db) {
|
|
1609
|
+
const result = db.query("PRAGMA user_version").get();
|
|
1610
|
+
return result.user_version;
|
|
1611
|
+
}
|
|
1578
1612
|
var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
|
|
1579
1613
|
|
|
1580
1614
|
// src/storage/sqlite.ts
|
|
@@ -1729,6 +1763,7 @@ class MemDatabase {
|
|
|
1729
1763
|
this.vecAvailable = this.loadVecExtension();
|
|
1730
1764
|
runMigrations(this.db);
|
|
1731
1765
|
ensureObservationTypes(this.db);
|
|
1766
|
+
ensureSessionSummaryColumns(this.db);
|
|
1732
1767
|
}
|
|
1733
1768
|
loadVecExtension() {
|
|
1734
1769
|
try {
|
|
@@ -1954,6 +1989,10 @@ class MemDatabase {
|
|
|
1954
1989
|
p.name AS project_name,
|
|
1955
1990
|
ss.request AS request,
|
|
1956
1991
|
ss.completed AS completed,
|
|
1992
|
+
ss.capture_state AS capture_state,
|
|
1993
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1994
|
+
ss.hot_files AS hot_files,
|
|
1995
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1957
1996
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1958
1997
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1959
1998
|
FROM sessions s
|
|
@@ -1968,6 +2007,10 @@ class MemDatabase {
|
|
|
1968
2007
|
p.name AS project_name,
|
|
1969
2008
|
ss.request AS request,
|
|
1970
2009
|
ss.completed AS completed,
|
|
2010
|
+
ss.capture_state AS capture_state,
|
|
2011
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
2012
|
+
ss.hot_files AS hot_files,
|
|
2013
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1971
2014
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1972
2015
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1973
2016
|
FROM sessions s
|
|
@@ -2140,8 +2183,11 @@ class MemDatabase {
|
|
|
2140
2183
|
completed: normalizeSummarySection(summary.completed),
|
|
2141
2184
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
2142
2185
|
};
|
|
2143
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
2144
|
-
|
|
2186
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
2187
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
2188
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
2189
|
+
)
|
|
2190
|
+
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
2191
|
const id = Number(result.lastInsertRowid);
|
|
2146
2192
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
2147
2193
|
}
|
|
@@ -2156,7 +2202,11 @@ class MemDatabase {
|
|
|
2156
2202
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
2157
2203
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
2158
2204
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
2159
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
2205
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
2206
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
2207
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
2208
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
2209
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
2160
2210
|
};
|
|
2161
2211
|
this.db.query(`UPDATE session_summaries
|
|
2162
2212
|
SET project_id = ?,
|
|
@@ -2166,8 +2216,12 @@ class MemDatabase {
|
|
|
2166
2216
|
learned = ?,
|
|
2167
2217
|
completed = ?,
|
|
2168
2218
|
next_steps = ?,
|
|
2219
|
+
capture_state = ?,
|
|
2220
|
+
recent_tool_names = ?,
|
|
2221
|
+
hot_files = ?,
|
|
2222
|
+
recent_outcomes = ?,
|
|
2169
2223
|
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);
|
|
2224
|
+
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
2225
|
return this.getSessionSummary(summary.session_id);
|
|
2172
2226
|
}
|
|
2173
2227
|
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: 12,
|
|
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",
|
|
@@ -842,6 +852,9 @@ function inferLegacySchemaVersion(db) {
|
|
|
842
852
|
version = Math.max(version, 10);
|
|
843
853
|
if (columnExists(db, "observations", "source_tool"))
|
|
844
854
|
version = Math.max(version, 11);
|
|
855
|
+
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")) {
|
|
856
|
+
version = Math.max(version, 12);
|
|
857
|
+
}
|
|
845
858
|
return version;
|
|
846
859
|
}
|
|
847
860
|
function runMigrations(db) {
|
|
@@ -920,6 +933,27 @@ function ensureObservationTypes(db) {
|
|
|
920
933
|
}
|
|
921
934
|
}
|
|
922
935
|
}
|
|
936
|
+
function ensureSessionSummaryColumns(db) {
|
|
937
|
+
const required = [
|
|
938
|
+
"capture_state",
|
|
939
|
+
"recent_tool_names",
|
|
940
|
+
"hot_files",
|
|
941
|
+
"recent_outcomes"
|
|
942
|
+
];
|
|
943
|
+
for (const column of required) {
|
|
944
|
+
if (columnExists(db, "session_summaries", column))
|
|
945
|
+
continue;
|
|
946
|
+
db.exec(`ALTER TABLE session_summaries ADD COLUMN ${column} TEXT`);
|
|
947
|
+
}
|
|
948
|
+
const current = getSchemaVersion(db);
|
|
949
|
+
if (current < 12) {
|
|
950
|
+
db.exec("PRAGMA user_version = 12");
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
function getSchemaVersion(db) {
|
|
954
|
+
const result = db.query("PRAGMA user_version").get();
|
|
955
|
+
return result.user_version;
|
|
956
|
+
}
|
|
923
957
|
var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
|
|
924
958
|
|
|
925
959
|
// src/storage/sqlite.ts
|
|
@@ -1074,6 +1108,7 @@ class MemDatabase {
|
|
|
1074
1108
|
this.vecAvailable = this.loadVecExtension();
|
|
1075
1109
|
runMigrations(this.db);
|
|
1076
1110
|
ensureObservationTypes(this.db);
|
|
1111
|
+
ensureSessionSummaryColumns(this.db);
|
|
1077
1112
|
}
|
|
1078
1113
|
loadVecExtension() {
|
|
1079
1114
|
try {
|
|
@@ -1299,6 +1334,10 @@ class MemDatabase {
|
|
|
1299
1334
|
p.name AS project_name,
|
|
1300
1335
|
ss.request AS request,
|
|
1301
1336
|
ss.completed AS completed,
|
|
1337
|
+
ss.capture_state AS capture_state,
|
|
1338
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1339
|
+
ss.hot_files AS hot_files,
|
|
1340
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1302
1341
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1303
1342
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1304
1343
|
FROM sessions s
|
|
@@ -1313,6 +1352,10 @@ class MemDatabase {
|
|
|
1313
1352
|
p.name AS project_name,
|
|
1314
1353
|
ss.request AS request,
|
|
1315
1354
|
ss.completed AS completed,
|
|
1355
|
+
ss.capture_state AS capture_state,
|
|
1356
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1357
|
+
ss.hot_files AS hot_files,
|
|
1358
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1316
1359
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1317
1360
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1318
1361
|
FROM sessions s
|
|
@@ -1485,8 +1528,11 @@ class MemDatabase {
|
|
|
1485
1528
|
completed: normalizeSummarySection(summary.completed),
|
|
1486
1529
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
1487
1530
|
};
|
|
1488
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1489
|
-
|
|
1531
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1532
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
1533
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
1534
|
+
)
|
|
1535
|
+
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
1536
|
const id = Number(result.lastInsertRowid);
|
|
1491
1537
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
1492
1538
|
}
|
|
@@ -1501,7 +1547,11 @@ class MemDatabase {
|
|
|
1501
1547
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
1502
1548
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
1503
1549
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
1504
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
1550
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
1551
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
1552
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
1553
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
1554
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
1505
1555
|
};
|
|
1506
1556
|
this.db.query(`UPDATE session_summaries
|
|
1507
1557
|
SET project_id = ?,
|
|
@@ -1511,8 +1561,12 @@ class MemDatabase {
|
|
|
1511
1561
|
learned = ?,
|
|
1512
1562
|
completed = ?,
|
|
1513
1563
|
next_steps = ?,
|
|
1564
|
+
capture_state = ?,
|
|
1565
|
+
recent_tool_names = ?,
|
|
1566
|
+
hot_files = ?,
|
|
1567
|
+
recent_outcomes = ?,
|
|
1514
1568
|
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);
|
|
1569
|
+
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
1570
|
return this.getSessionSummary(summary.session_id);
|
|
1517
1571
|
}
|
|
1518
1572
|
getSessionSummary(sessionId) {
|
|
@@ -3166,6 +3220,107 @@ function checkSessionFatigue(db, sessionId) {
|
|
|
3166
3220
|
};
|
|
3167
3221
|
}
|
|
3168
3222
|
|
|
3223
|
+
// src/capture/live-summary.ts
|
|
3224
|
+
var LOW_SIGNAL_TITLE_PATTERNS = [
|
|
3225
|
+
/^(Modified|Extended|Reduced|Created)\s+\S+/i,
|
|
3226
|
+
/^Dependency change:/i,
|
|
3227
|
+
/^[a-z0-9._-]+\s*\(error\)$/i,
|
|
3228
|
+
/^(engrm|unknown):\s+/i
|
|
3229
|
+
];
|
|
3230
|
+
function buildLiveSummaryUpdate(observation) {
|
|
3231
|
+
const title = compactSummaryTitle(observation.title);
|
|
3232
|
+
if (!title)
|
|
3233
|
+
return null;
|
|
3234
|
+
switch (observation.type) {
|
|
3235
|
+
case "discovery":
|
|
3236
|
+
case "pattern":
|
|
3237
|
+
return { investigated: title };
|
|
3238
|
+
case "decision":
|
|
3239
|
+
return { learned: title };
|
|
3240
|
+
case "bugfix":
|
|
3241
|
+
case "feature":
|
|
3242
|
+
case "change":
|
|
3243
|
+
case "refactor":
|
|
3244
|
+
return { completed: title };
|
|
3245
|
+
default:
|
|
3246
|
+
return null;
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
function compactSummaryTitle(title) {
|
|
3250
|
+
const trimmed = title?.trim();
|
|
3251
|
+
if (!trimmed)
|
|
3252
|
+
return null;
|
|
3253
|
+
if (LOW_SIGNAL_TITLE_PATTERNS.some((pattern) => pattern.test(trimmed))) {
|
|
3254
|
+
return null;
|
|
3255
|
+
}
|
|
3256
|
+
return trimmed.length > 180 ? `${trimmed.slice(0, 177)}...` : trimmed;
|
|
3257
|
+
}
|
|
3258
|
+
function mergeLiveSummarySections(existing, update) {
|
|
3259
|
+
return {
|
|
3260
|
+
investigated: mergeSectionItem(existing?.investigated ?? null, update.investigated ?? null),
|
|
3261
|
+
learned: mergeSectionItem(existing?.learned ?? null, update.learned ?? null),
|
|
3262
|
+
completed: mergeSectionItem(existing?.completed ?? null, update.completed ?? null)
|
|
3263
|
+
};
|
|
3264
|
+
}
|
|
3265
|
+
function mergeSectionItem(existing, item) {
|
|
3266
|
+
if (!item)
|
|
3267
|
+
return existing;
|
|
3268
|
+
if (!existing)
|
|
3269
|
+
return item;
|
|
3270
|
+
const lines = existing.split(`
|
|
3271
|
+
`).map((line) => line.replace(/^\s*-\s*/, "").trim()).filter(Boolean);
|
|
3272
|
+
const normalizedItem = item.trim();
|
|
3273
|
+
if (lines.some((line) => line.toLowerCase() === normalizedItem.toLowerCase())) {
|
|
3274
|
+
return existing;
|
|
3275
|
+
}
|
|
3276
|
+
return `${existing}
|
|
3277
|
+
- ${normalizedItem}`;
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
// src/capture/session-handoff.ts
|
|
3281
|
+
function buildSessionHandoffMetadata(prompts, toolEvents, observations) {
|
|
3282
|
+
const latestRequest = prompts.length > 0 ? prompts[prompts.length - 1]?.prompt ?? null : null;
|
|
3283
|
+
const recentRequestPrompts = prompts.slice(-3).map((prompt) => prompt.prompt.trim()).filter(Boolean);
|
|
3284
|
+
const recentToolNames = [...new Set(toolEvents.slice(-8).map((tool) => tool.tool_name).filter(Boolean))];
|
|
3285
|
+
const recentToolCommands = [...new Set(toolEvents.slice(-5).map((tool) => (tool.command ?? tool.file_path ?? "").trim()).filter(Boolean))];
|
|
3286
|
+
const hotFiles = [...new Set(observations.flatMap((obs) => [
|
|
3287
|
+
...parseJsonArray(obs.files_modified),
|
|
3288
|
+
...parseJsonArray(obs.files_read)
|
|
3289
|
+
]).filter(Boolean))].slice(0, 6);
|
|
3290
|
+
const recentOutcomes = observations.filter((obs) => ["bugfix", "feature", "refactor", "change", "decision"].includes(obs.type)).map((obs) => obs.title.trim()).filter((title) => title.length > 0).slice(0, 6);
|
|
3291
|
+
const captureState = prompts.length > 0 && toolEvents.length > 0 ? "rich" : prompts.length > 0 || toolEvents.length > 0 ? "partial" : "summary-only";
|
|
3292
|
+
const observationSourceTools = Array.from(observations.reduce((acc, obs) => {
|
|
3293
|
+
if (!obs.source_tool)
|
|
3294
|
+
return acc;
|
|
3295
|
+
acc.set(obs.source_tool, (acc.get(obs.source_tool) ?? 0) + 1);
|
|
3296
|
+
return acc;
|
|
3297
|
+
}, new Map).entries()).map(([tool, count]) => ({ tool, count })).sort((a, b) => b.count - a.count || a.tool.localeCompare(b.tool)).slice(0, 6);
|
|
3298
|
+
const latestObservationPromptNumber = observations.map((obs) => obs.source_prompt_number).filter((value) => typeof value === "number").sort((a, b) => b - a)[0] ?? null;
|
|
3299
|
+
return {
|
|
3300
|
+
prompt_count: prompts.length,
|
|
3301
|
+
tool_event_count: toolEvents.length,
|
|
3302
|
+
recent_request_prompts: recentRequestPrompts,
|
|
3303
|
+
latest_request: latestRequest,
|
|
3304
|
+
recent_tool_names: recentToolNames,
|
|
3305
|
+
recent_tool_commands: recentToolCommands,
|
|
3306
|
+
capture_state: captureState,
|
|
3307
|
+
hot_files: hotFiles,
|
|
3308
|
+
recent_outcomes: recentOutcomes,
|
|
3309
|
+
observation_source_tools: observationSourceTools,
|
|
3310
|
+
latest_observation_prompt_number: latestObservationPromptNumber
|
|
3311
|
+
};
|
|
3312
|
+
}
|
|
3313
|
+
function parseJsonArray(value) {
|
|
3314
|
+
if (!value)
|
|
3315
|
+
return [];
|
|
3316
|
+
try {
|
|
3317
|
+
const parsed = JSON.parse(value);
|
|
3318
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
3319
|
+
} catch {
|
|
3320
|
+
return [];
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3169
3324
|
// hooks/post-tool-use.ts
|
|
3170
3325
|
async function main() {
|
|
3171
3326
|
const raw = await readStdin();
|
|
@@ -3269,7 +3424,8 @@ async function main() {
|
|
|
3269
3424
|
timeoutMs: 800
|
|
3270
3425
|
}), 1000);
|
|
3271
3426
|
if (observed) {
|
|
3272
|
-
await saveObservation(db, config, observed);
|
|
3427
|
+
const result = await saveObservation(db, config, observed);
|
|
3428
|
+
updateRollingSummaryFromObservation(db, result.observation_id, event, config.user_id);
|
|
3273
3429
|
incrementObserverSaveCount(event.session_id);
|
|
3274
3430
|
saved = true;
|
|
3275
3431
|
}
|
|
@@ -3278,7 +3434,7 @@ async function main() {
|
|
|
3278
3434
|
if (!saved) {
|
|
3279
3435
|
const extracted = extractObservation(event);
|
|
3280
3436
|
if (extracted) {
|
|
3281
|
-
await saveObservation(db, config, {
|
|
3437
|
+
const result = await saveObservation(db, config, {
|
|
3282
3438
|
type: extracted.type,
|
|
3283
3439
|
title: extracted.title,
|
|
3284
3440
|
narrative: extracted.narrative,
|
|
@@ -3288,6 +3444,7 @@ async function main() {
|
|
|
3288
3444
|
cwd: event.cwd,
|
|
3289
3445
|
source_tool: event.tool_name
|
|
3290
3446
|
});
|
|
3447
|
+
updateRollingSummaryFromObservation(db, result.observation_id, event, config.user_id);
|
|
3291
3448
|
incrementObserverSaveCount(event.session_id);
|
|
3292
3449
|
}
|
|
3293
3450
|
}
|
|
@@ -3352,6 +3509,38 @@ function detectProjectForEvent(event) {
|
|
|
3352
3509
|
const touchedPaths = extractTouchedPaths(event);
|
|
3353
3510
|
return touchedPaths.length > 0 ? detectProjectFromTouchedPaths(touchedPaths, event.cwd) : detectProject(event.cwd);
|
|
3354
3511
|
}
|
|
3512
|
+
function updateRollingSummaryFromObservation(db, observationId, event, userId) {
|
|
3513
|
+
if (!observationId || !event.session_id)
|
|
3514
|
+
return;
|
|
3515
|
+
const observation = db.getObservationById(observationId);
|
|
3516
|
+
if (!observation)
|
|
3517
|
+
return;
|
|
3518
|
+
const update = buildLiveSummaryUpdate(observation);
|
|
3519
|
+
if (!update)
|
|
3520
|
+
return;
|
|
3521
|
+
const existing = db.getSessionSummary(event.session_id);
|
|
3522
|
+
const sessionPrompts = db.getSessionUserPrompts(event.session_id, 20);
|
|
3523
|
+
const sessionToolEvents = db.getSessionToolEvents(event.session_id, 20);
|
|
3524
|
+
const sessionObservations = db.getObservationsBySession(event.session_id);
|
|
3525
|
+
const merged = mergeLiveSummarySections(existing, update);
|
|
3526
|
+
const handoff = buildSessionHandoffMetadata(sessionPrompts, sessionToolEvents, sessionObservations);
|
|
3527
|
+
const currentRequest = existing?.request ?? handoff.latest_request ?? null;
|
|
3528
|
+
const summary = db.upsertSessionSummary({
|
|
3529
|
+
session_id: event.session_id,
|
|
3530
|
+
project_id: observation.project_id,
|
|
3531
|
+
user_id: userId,
|
|
3532
|
+
request: currentRequest,
|
|
3533
|
+
investigated: merged.investigated,
|
|
3534
|
+
learned: merged.learned,
|
|
3535
|
+
completed: merged.completed,
|
|
3536
|
+
next_steps: existing?.next_steps ?? null,
|
|
3537
|
+
capture_state: handoff.capture_state,
|
|
3538
|
+
recent_tool_names: JSON.stringify(handoff.recent_tool_names),
|
|
3539
|
+
hot_files: JSON.stringify(handoff.hot_files),
|
|
3540
|
+
recent_outcomes: JSON.stringify(handoff.recent_outcomes)
|
|
3541
|
+
});
|
|
3542
|
+
db.addToOutbox("summary", summary.id);
|
|
3543
|
+
}
|
|
3355
3544
|
function extractTouchedPaths(event) {
|
|
3356
3545
|
const paths = [];
|
|
3357
3546
|
const filePath = event.tool_input["file_path"];
|