engrm 0.4.10 → 0.4.11
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 +50 -0
- package/dist/hooks/elicitation-result.js +50 -0
- package/dist/hooks/post-tool-use.js +50 -0
- package/dist/hooks/pre-compact.js +50 -0
- package/dist/hooks/sentinel.js +50 -0
- package/dist/hooks/session-start.js +118 -7
- package/dist/hooks/stop.js +98 -7
- package/dist/hooks/user-prompt-submit.js +50 -0
- package/dist/server.js +110 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -607,9 +607,59 @@ function isVecExtensionLoaded(db) {
|
|
|
607
607
|
return false;
|
|
608
608
|
}
|
|
609
609
|
}
|
|
610
|
+
function tableExists(db, name) {
|
|
611
|
+
const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
|
|
612
|
+
return Boolean(row?.name);
|
|
613
|
+
}
|
|
614
|
+
function columnExists(db, table, column) {
|
|
615
|
+
if (!tableExists(db, table))
|
|
616
|
+
return false;
|
|
617
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
618
|
+
return rows.some((row) => row.name === column);
|
|
619
|
+
}
|
|
620
|
+
function ensureLegacyBaseTables(db) {
|
|
621
|
+
db.exec(`
|
|
622
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
623
|
+
key TEXT PRIMARY KEY,
|
|
624
|
+
value TEXT NOT NULL
|
|
625
|
+
);
|
|
626
|
+
`);
|
|
627
|
+
}
|
|
628
|
+
function inferLegacySchemaVersion(db) {
|
|
629
|
+
const hasProjects = tableExists(db, "projects");
|
|
630
|
+
const hasObservations = tableExists(db, "observations");
|
|
631
|
+
const hasSessions = tableExists(db, "sessions");
|
|
632
|
+
if (!(hasProjects && hasObservations && hasSessions))
|
|
633
|
+
return 0;
|
|
634
|
+
ensureLegacyBaseTables(db);
|
|
635
|
+
let version = 1;
|
|
636
|
+
if (columnExists(db, "observations", "superseded_by"))
|
|
637
|
+
version = Math.max(version, 2);
|
|
638
|
+
if (columnExists(db, "observations", "remote_source_id"))
|
|
639
|
+
version = Math.max(version, 3);
|
|
640
|
+
if (tableExists(db, "vec_observations"))
|
|
641
|
+
version = Math.max(version, 4);
|
|
642
|
+
const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
|
|
643
|
+
if (hasSessionMetrics || tableExists(db, "security_findings"))
|
|
644
|
+
version = Math.max(version, 5);
|
|
645
|
+
if (columnExists(db, "sessions", "risk_score"))
|
|
646
|
+
version = Math.max(version, 6);
|
|
647
|
+
if (tableExists(db, "packs_installed"))
|
|
648
|
+
version = Math.max(version, 7);
|
|
649
|
+
if (tableExists(db, "user_prompts"))
|
|
650
|
+
version = Math.max(version, 9);
|
|
651
|
+
if (tableExists(db, "tool_events"))
|
|
652
|
+
version = Math.max(version, 10);
|
|
653
|
+
return version;
|
|
654
|
+
}
|
|
610
655
|
function runMigrations(db) {
|
|
611
656
|
const currentVersion = db.query("PRAGMA user_version").get();
|
|
612
657
|
let version = currentVersion.user_version;
|
|
658
|
+
const inferred = inferLegacySchemaVersion(db);
|
|
659
|
+
if (inferred > version) {
|
|
660
|
+
db.exec(`PRAGMA user_version = ${inferred}`);
|
|
661
|
+
version = inferred;
|
|
662
|
+
}
|
|
613
663
|
for (const migration of MIGRATIONS) {
|
|
614
664
|
if (migration.version <= version)
|
|
615
665
|
continue;
|
|
@@ -1437,9 +1437,59 @@ function isVecExtensionLoaded(db) {
|
|
|
1437
1437
|
return false;
|
|
1438
1438
|
}
|
|
1439
1439
|
}
|
|
1440
|
+
function tableExists(db, name) {
|
|
1441
|
+
const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
|
|
1442
|
+
return Boolean(row?.name);
|
|
1443
|
+
}
|
|
1444
|
+
function columnExists(db, table, column) {
|
|
1445
|
+
if (!tableExists(db, table))
|
|
1446
|
+
return false;
|
|
1447
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
1448
|
+
return rows.some((row) => row.name === column);
|
|
1449
|
+
}
|
|
1450
|
+
function ensureLegacyBaseTables(db) {
|
|
1451
|
+
db.exec(`
|
|
1452
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
1453
|
+
key TEXT PRIMARY KEY,
|
|
1454
|
+
value TEXT NOT NULL
|
|
1455
|
+
);
|
|
1456
|
+
`);
|
|
1457
|
+
}
|
|
1458
|
+
function inferLegacySchemaVersion(db) {
|
|
1459
|
+
const hasProjects = tableExists(db, "projects");
|
|
1460
|
+
const hasObservations = tableExists(db, "observations");
|
|
1461
|
+
const hasSessions = tableExists(db, "sessions");
|
|
1462
|
+
if (!(hasProjects && hasObservations && hasSessions))
|
|
1463
|
+
return 0;
|
|
1464
|
+
ensureLegacyBaseTables(db);
|
|
1465
|
+
let version = 1;
|
|
1466
|
+
if (columnExists(db, "observations", "superseded_by"))
|
|
1467
|
+
version = Math.max(version, 2);
|
|
1468
|
+
if (columnExists(db, "observations", "remote_source_id"))
|
|
1469
|
+
version = Math.max(version, 3);
|
|
1470
|
+
if (tableExists(db, "vec_observations"))
|
|
1471
|
+
version = Math.max(version, 4);
|
|
1472
|
+
const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
|
|
1473
|
+
if (hasSessionMetrics || tableExists(db, "security_findings"))
|
|
1474
|
+
version = Math.max(version, 5);
|
|
1475
|
+
if (columnExists(db, "sessions", "risk_score"))
|
|
1476
|
+
version = Math.max(version, 6);
|
|
1477
|
+
if (tableExists(db, "packs_installed"))
|
|
1478
|
+
version = Math.max(version, 7);
|
|
1479
|
+
if (tableExists(db, "user_prompts"))
|
|
1480
|
+
version = Math.max(version, 9);
|
|
1481
|
+
if (tableExists(db, "tool_events"))
|
|
1482
|
+
version = Math.max(version, 10);
|
|
1483
|
+
return version;
|
|
1484
|
+
}
|
|
1440
1485
|
function runMigrations(db) {
|
|
1441
1486
|
const currentVersion = db.query("PRAGMA user_version").get();
|
|
1442
1487
|
let version = currentVersion.user_version;
|
|
1488
|
+
const inferred = inferLegacySchemaVersion(db);
|
|
1489
|
+
if (inferred > version) {
|
|
1490
|
+
db.exec(`PRAGMA user_version = ${inferred}`);
|
|
1491
|
+
version = inferred;
|
|
1492
|
+
}
|
|
1443
1493
|
for (const migration of MIGRATIONS) {
|
|
1444
1494
|
if (migration.version <= version)
|
|
1445
1495
|
continue;
|
|
@@ -785,9 +785,59 @@ function isVecExtensionLoaded(db) {
|
|
|
785
785
|
return false;
|
|
786
786
|
}
|
|
787
787
|
}
|
|
788
|
+
function tableExists(db, name) {
|
|
789
|
+
const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
|
|
790
|
+
return Boolean(row?.name);
|
|
791
|
+
}
|
|
792
|
+
function columnExists(db, table, column) {
|
|
793
|
+
if (!tableExists(db, table))
|
|
794
|
+
return false;
|
|
795
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
796
|
+
return rows.some((row) => row.name === column);
|
|
797
|
+
}
|
|
798
|
+
function ensureLegacyBaseTables(db) {
|
|
799
|
+
db.exec(`
|
|
800
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
801
|
+
key TEXT PRIMARY KEY,
|
|
802
|
+
value TEXT NOT NULL
|
|
803
|
+
);
|
|
804
|
+
`);
|
|
805
|
+
}
|
|
806
|
+
function inferLegacySchemaVersion(db) {
|
|
807
|
+
const hasProjects = tableExists(db, "projects");
|
|
808
|
+
const hasObservations = tableExists(db, "observations");
|
|
809
|
+
const hasSessions = tableExists(db, "sessions");
|
|
810
|
+
if (!(hasProjects && hasObservations && hasSessions))
|
|
811
|
+
return 0;
|
|
812
|
+
ensureLegacyBaseTables(db);
|
|
813
|
+
let version = 1;
|
|
814
|
+
if (columnExists(db, "observations", "superseded_by"))
|
|
815
|
+
version = Math.max(version, 2);
|
|
816
|
+
if (columnExists(db, "observations", "remote_source_id"))
|
|
817
|
+
version = Math.max(version, 3);
|
|
818
|
+
if (tableExists(db, "vec_observations"))
|
|
819
|
+
version = Math.max(version, 4);
|
|
820
|
+
const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
|
|
821
|
+
if (hasSessionMetrics || tableExists(db, "security_findings"))
|
|
822
|
+
version = Math.max(version, 5);
|
|
823
|
+
if (columnExists(db, "sessions", "risk_score"))
|
|
824
|
+
version = Math.max(version, 6);
|
|
825
|
+
if (tableExists(db, "packs_installed"))
|
|
826
|
+
version = Math.max(version, 7);
|
|
827
|
+
if (tableExists(db, "user_prompts"))
|
|
828
|
+
version = Math.max(version, 9);
|
|
829
|
+
if (tableExists(db, "tool_events"))
|
|
830
|
+
version = Math.max(version, 10);
|
|
831
|
+
return version;
|
|
832
|
+
}
|
|
788
833
|
function runMigrations(db) {
|
|
789
834
|
const currentVersion = db.query("PRAGMA user_version").get();
|
|
790
835
|
let version = currentVersion.user_version;
|
|
836
|
+
const inferred = inferLegacySchemaVersion(db);
|
|
837
|
+
if (inferred > version) {
|
|
838
|
+
db.exec(`PRAGMA user_version = ${inferred}`);
|
|
839
|
+
version = inferred;
|
|
840
|
+
}
|
|
791
841
|
for (const migration of MIGRATIONS) {
|
|
792
842
|
if (migration.version <= version)
|
|
793
843
|
continue;
|
|
@@ -579,9 +579,59 @@ function isVecExtensionLoaded(db) {
|
|
|
579
579
|
return false;
|
|
580
580
|
}
|
|
581
581
|
}
|
|
582
|
+
function tableExists(db, name) {
|
|
583
|
+
const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
|
|
584
|
+
return Boolean(row?.name);
|
|
585
|
+
}
|
|
586
|
+
function columnExists(db, table, column) {
|
|
587
|
+
if (!tableExists(db, table))
|
|
588
|
+
return false;
|
|
589
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
590
|
+
return rows.some((row) => row.name === column);
|
|
591
|
+
}
|
|
592
|
+
function ensureLegacyBaseTables(db) {
|
|
593
|
+
db.exec(`
|
|
594
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
595
|
+
key TEXT PRIMARY KEY,
|
|
596
|
+
value TEXT NOT NULL
|
|
597
|
+
);
|
|
598
|
+
`);
|
|
599
|
+
}
|
|
600
|
+
function inferLegacySchemaVersion(db) {
|
|
601
|
+
const hasProjects = tableExists(db, "projects");
|
|
602
|
+
const hasObservations = tableExists(db, "observations");
|
|
603
|
+
const hasSessions = tableExists(db, "sessions");
|
|
604
|
+
if (!(hasProjects && hasObservations && hasSessions))
|
|
605
|
+
return 0;
|
|
606
|
+
ensureLegacyBaseTables(db);
|
|
607
|
+
let version = 1;
|
|
608
|
+
if (columnExists(db, "observations", "superseded_by"))
|
|
609
|
+
version = Math.max(version, 2);
|
|
610
|
+
if (columnExists(db, "observations", "remote_source_id"))
|
|
611
|
+
version = Math.max(version, 3);
|
|
612
|
+
if (tableExists(db, "vec_observations"))
|
|
613
|
+
version = Math.max(version, 4);
|
|
614
|
+
const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
|
|
615
|
+
if (hasSessionMetrics || tableExists(db, "security_findings"))
|
|
616
|
+
version = Math.max(version, 5);
|
|
617
|
+
if (columnExists(db, "sessions", "risk_score"))
|
|
618
|
+
version = Math.max(version, 6);
|
|
619
|
+
if (tableExists(db, "packs_installed"))
|
|
620
|
+
version = Math.max(version, 7);
|
|
621
|
+
if (tableExists(db, "user_prompts"))
|
|
622
|
+
version = Math.max(version, 9);
|
|
623
|
+
if (tableExists(db, "tool_events"))
|
|
624
|
+
version = Math.max(version, 10);
|
|
625
|
+
return version;
|
|
626
|
+
}
|
|
582
627
|
function runMigrations(db) {
|
|
583
628
|
const currentVersion = db.query("PRAGMA user_version").get();
|
|
584
629
|
let version = currentVersion.user_version;
|
|
630
|
+
const inferred = inferLegacySchemaVersion(db);
|
|
631
|
+
if (inferred > version) {
|
|
632
|
+
db.exec(`PRAGMA user_version = ${inferred}`);
|
|
633
|
+
version = inferred;
|
|
634
|
+
}
|
|
585
635
|
for (const migration of MIGRATIONS) {
|
|
586
636
|
if (migration.version <= version)
|
|
587
637
|
continue;
|
package/dist/hooks/sentinel.js
CHANGED
|
@@ -655,9 +655,59 @@ function isVecExtensionLoaded(db) {
|
|
|
655
655
|
return false;
|
|
656
656
|
}
|
|
657
657
|
}
|
|
658
|
+
function tableExists(db, name) {
|
|
659
|
+
const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
|
|
660
|
+
return Boolean(row?.name);
|
|
661
|
+
}
|
|
662
|
+
function columnExists(db, table, column) {
|
|
663
|
+
if (!tableExists(db, table))
|
|
664
|
+
return false;
|
|
665
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
666
|
+
return rows.some((row) => row.name === column);
|
|
667
|
+
}
|
|
668
|
+
function ensureLegacyBaseTables(db) {
|
|
669
|
+
db.exec(`
|
|
670
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
671
|
+
key TEXT PRIMARY KEY,
|
|
672
|
+
value TEXT NOT NULL
|
|
673
|
+
);
|
|
674
|
+
`);
|
|
675
|
+
}
|
|
676
|
+
function inferLegacySchemaVersion(db) {
|
|
677
|
+
const hasProjects = tableExists(db, "projects");
|
|
678
|
+
const hasObservations = tableExists(db, "observations");
|
|
679
|
+
const hasSessions = tableExists(db, "sessions");
|
|
680
|
+
if (!(hasProjects && hasObservations && hasSessions))
|
|
681
|
+
return 0;
|
|
682
|
+
ensureLegacyBaseTables(db);
|
|
683
|
+
let version = 1;
|
|
684
|
+
if (columnExists(db, "observations", "superseded_by"))
|
|
685
|
+
version = Math.max(version, 2);
|
|
686
|
+
if (columnExists(db, "observations", "remote_source_id"))
|
|
687
|
+
version = Math.max(version, 3);
|
|
688
|
+
if (tableExists(db, "vec_observations"))
|
|
689
|
+
version = Math.max(version, 4);
|
|
690
|
+
const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
|
|
691
|
+
if (hasSessionMetrics || tableExists(db, "security_findings"))
|
|
692
|
+
version = Math.max(version, 5);
|
|
693
|
+
if (columnExists(db, "sessions", "risk_score"))
|
|
694
|
+
version = Math.max(version, 6);
|
|
695
|
+
if (tableExists(db, "packs_installed"))
|
|
696
|
+
version = Math.max(version, 7);
|
|
697
|
+
if (tableExists(db, "user_prompts"))
|
|
698
|
+
version = Math.max(version, 9);
|
|
699
|
+
if (tableExists(db, "tool_events"))
|
|
700
|
+
version = Math.max(version, 10);
|
|
701
|
+
return version;
|
|
702
|
+
}
|
|
658
703
|
function runMigrations(db) {
|
|
659
704
|
const currentVersion = db.query("PRAGMA user_version").get();
|
|
660
705
|
let version = currentVersion.user_version;
|
|
706
|
+
const inferred = inferLegacySchemaVersion(db);
|
|
707
|
+
if (inferred > version) {
|
|
708
|
+
db.exec(`PRAGMA user_version = ${inferred}`);
|
|
709
|
+
version = inferred;
|
|
710
|
+
}
|
|
661
711
|
for (const migration of MIGRATIONS) {
|
|
662
712
|
if (migration.version <= version)
|
|
663
713
|
continue;
|
|
@@ -2166,9 +2166,59 @@ function isVecExtensionLoaded(db) {
|
|
|
2166
2166
|
return false;
|
|
2167
2167
|
}
|
|
2168
2168
|
}
|
|
2169
|
+
function tableExists(db, name) {
|
|
2170
|
+
const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
|
|
2171
|
+
return Boolean(row?.name);
|
|
2172
|
+
}
|
|
2173
|
+
function columnExists(db, table, column) {
|
|
2174
|
+
if (!tableExists(db, table))
|
|
2175
|
+
return false;
|
|
2176
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
2177
|
+
return rows.some((row) => row.name === column);
|
|
2178
|
+
}
|
|
2179
|
+
function ensureLegacyBaseTables(db) {
|
|
2180
|
+
db.exec(`
|
|
2181
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
2182
|
+
key TEXT PRIMARY KEY,
|
|
2183
|
+
value TEXT NOT NULL
|
|
2184
|
+
);
|
|
2185
|
+
`);
|
|
2186
|
+
}
|
|
2187
|
+
function inferLegacySchemaVersion(db) {
|
|
2188
|
+
const hasProjects = tableExists(db, "projects");
|
|
2189
|
+
const hasObservations = tableExists(db, "observations");
|
|
2190
|
+
const hasSessions = tableExists(db, "sessions");
|
|
2191
|
+
if (!(hasProjects && hasObservations && hasSessions))
|
|
2192
|
+
return 0;
|
|
2193
|
+
ensureLegacyBaseTables(db);
|
|
2194
|
+
let version = 1;
|
|
2195
|
+
if (columnExists(db, "observations", "superseded_by"))
|
|
2196
|
+
version = Math.max(version, 2);
|
|
2197
|
+
if (columnExists(db, "observations", "remote_source_id"))
|
|
2198
|
+
version = Math.max(version, 3);
|
|
2199
|
+
if (tableExists(db, "vec_observations"))
|
|
2200
|
+
version = Math.max(version, 4);
|
|
2201
|
+
const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
|
|
2202
|
+
if (hasSessionMetrics || tableExists(db, "security_findings"))
|
|
2203
|
+
version = Math.max(version, 5);
|
|
2204
|
+
if (columnExists(db, "sessions", "risk_score"))
|
|
2205
|
+
version = Math.max(version, 6);
|
|
2206
|
+
if (tableExists(db, "packs_installed"))
|
|
2207
|
+
version = Math.max(version, 7);
|
|
2208
|
+
if (tableExists(db, "user_prompts"))
|
|
2209
|
+
version = Math.max(version, 9);
|
|
2210
|
+
if (tableExists(db, "tool_events"))
|
|
2211
|
+
version = Math.max(version, 10);
|
|
2212
|
+
return version;
|
|
2213
|
+
}
|
|
2169
2214
|
function runMigrations(db) {
|
|
2170
2215
|
const currentVersion = db.query("PRAGMA user_version").get();
|
|
2171
2216
|
let version = currentVersion.user_version;
|
|
2217
|
+
const inferred = inferLegacySchemaVersion(db);
|
|
2218
|
+
if (inferred > version) {
|
|
2219
|
+
db.exec(`PRAGMA user_version = ${inferred}`);
|
|
2220
|
+
version = inferred;
|
|
2221
|
+
}
|
|
2172
2222
|
for (const migration of MIGRATIONS) {
|
|
2173
2223
|
if (migration.version <= version)
|
|
2174
2224
|
continue;
|
|
@@ -3014,19 +3064,28 @@ function formatVisibleStartupBrief(context) {
|
|
|
3014
3064
|
const sessionFallbacks = sessionFallbacksFromContext(context);
|
|
3015
3065
|
const recentOutcomeLines = buildRecentOutcomeLines(context, latest);
|
|
3016
3066
|
const projectSignals = buildProjectSignalLine(context);
|
|
3067
|
+
const shownItems = new Set;
|
|
3017
3068
|
if (promptLines.length > 0) {
|
|
3018
3069
|
lines.push(`${c2.cyan}Recent Requests:${c2.reset}`);
|
|
3019
3070
|
for (const item of promptLines) {
|
|
3020
3071
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3072
|
+
rememberShownItem(shownItems, item);
|
|
3021
3073
|
}
|
|
3022
3074
|
}
|
|
3023
3075
|
if (latest) {
|
|
3076
|
+
const sanitizedNextSteps = sanitizeNextSteps(latest.next_steps, {
|
|
3077
|
+
request: currentRequest,
|
|
3078
|
+
investigated: chooseSection(latest.investigated, observationFallbacks.investigated, "Investigated"),
|
|
3079
|
+
learned: latest.learned,
|
|
3080
|
+
completed: chooseSection(latest.completed, observationFallbacks.completed, "Completed"),
|
|
3081
|
+
recentOutcomes: recentOutcomeLines
|
|
3082
|
+
});
|
|
3024
3083
|
const sections = [
|
|
3025
3084
|
["Request", currentRequest, 1],
|
|
3026
3085
|
["Investigated", chooseSection(latest.investigated, observationFallbacks.investigated, "Investigated"), 2],
|
|
3027
3086
|
["Learned", latest.learned, 2],
|
|
3028
3087
|
["Completed", chooseSection(latest.completed, observationFallbacks.completed, "Completed"), 2],
|
|
3029
|
-
["Next Steps",
|
|
3088
|
+
["Next Steps", sanitizedNextSteps, 2]
|
|
3030
3089
|
];
|
|
3031
3090
|
for (const [label, value, maxItems] of sections) {
|
|
3032
3091
|
const formatted = toSplashLines(value, maxItems ?? 2);
|
|
@@ -3034,33 +3093,45 @@ function formatVisibleStartupBrief(context) {
|
|
|
3034
3093
|
lines.push(`${c2.cyan}${label}:${c2.reset}`);
|
|
3035
3094
|
for (const item of formatted) {
|
|
3036
3095
|
lines.push(` ${item}`);
|
|
3096
|
+
rememberShownItem(shownItems, item);
|
|
3037
3097
|
}
|
|
3038
3098
|
}
|
|
3039
3099
|
}
|
|
3040
3100
|
} else if (currentRequest && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
|
|
3041
3101
|
lines.push(`${c2.cyan}Current Request:${c2.reset}`);
|
|
3042
3102
|
lines.push(` - ${truncateInline(currentRequest, 160)}`);
|
|
3103
|
+
rememberShownItem(shownItems, currentRequest);
|
|
3043
3104
|
if (toolFallbacks.length > 0) {
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
lines.push(
|
|
3105
|
+
const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
|
|
3106
|
+
if (additiveTools.length > 0) {
|
|
3107
|
+
lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
|
|
3108
|
+
for (const item of additiveTools) {
|
|
3109
|
+
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3110
|
+
rememberShownItem(shownItems, item);
|
|
3111
|
+
}
|
|
3047
3112
|
}
|
|
3048
3113
|
}
|
|
3049
3114
|
}
|
|
3050
3115
|
if (latest && currentRequest && !hasRequestSection(lines) && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
|
|
3051
3116
|
lines.push(`${c2.cyan}Current Request:${c2.reset}`);
|
|
3052
3117
|
lines.push(` - ${truncateInline(currentRequest, 160)}`);
|
|
3118
|
+
rememberShownItem(shownItems, currentRequest);
|
|
3053
3119
|
}
|
|
3054
3120
|
if (recentOutcomeLines.length > 0) {
|
|
3055
3121
|
lines.push(`${c2.cyan}Recent Work:${c2.reset}`);
|
|
3056
3122
|
for (const item of recentOutcomeLines) {
|
|
3057
3123
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3124
|
+
rememberShownItem(shownItems, item);
|
|
3058
3125
|
}
|
|
3059
3126
|
}
|
|
3060
3127
|
if (toolFallbacks.length > 0 && latest) {
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
lines.push(
|
|
3128
|
+
const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
|
|
3129
|
+
if (additiveTools.length > 0) {
|
|
3130
|
+
lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
|
|
3131
|
+
for (const item of additiveTools) {
|
|
3132
|
+
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3133
|
+
rememberShownItem(shownItems, item);
|
|
3134
|
+
}
|
|
3064
3135
|
}
|
|
3065
3136
|
}
|
|
3066
3137
|
if (sessionFallbacks.length > 0) {
|
|
@@ -3085,6 +3156,46 @@ function formatVisibleStartupBrief(context) {
|
|
|
3085
3156
|
}
|
|
3086
3157
|
return lines.slice(0, 14);
|
|
3087
3158
|
}
|
|
3159
|
+
function rememberShownItem(shown, value) {
|
|
3160
|
+
if (!value)
|
|
3161
|
+
return;
|
|
3162
|
+
for (const item of value.split(`
|
|
3163
|
+
`)) {
|
|
3164
|
+
const normalized = normalizeStartupItem(item);
|
|
3165
|
+
if (normalized)
|
|
3166
|
+
shown.add(normalized);
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
function extractNormalizedSplashItems(value) {
|
|
3170
|
+
if (!value)
|
|
3171
|
+
return [];
|
|
3172
|
+
return value.split(`
|
|
3173
|
+
`).map((line) => normalizeStartupItem(line)).filter(Boolean);
|
|
3174
|
+
}
|
|
3175
|
+
function sanitizeNextSteps(nextSteps, context) {
|
|
3176
|
+
if (!nextSteps)
|
|
3177
|
+
return null;
|
|
3178
|
+
const covered = new Set([
|
|
3179
|
+
normalizeStartupItem(context.request ?? ""),
|
|
3180
|
+
...extractNormalizedSplashItems(context.investigated),
|
|
3181
|
+
...extractNormalizedSplashItems(context.learned),
|
|
3182
|
+
...extractNormalizedSplashItems(context.completed),
|
|
3183
|
+
...context.recentOutcomes.map((item) => normalizeStartupItem(item))
|
|
3184
|
+
].filter(Boolean));
|
|
3185
|
+
const kept = nextSteps.split(`
|
|
3186
|
+
`).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "")).filter((line) => {
|
|
3187
|
+
const normalized = normalizeStartupItem(line.replace(/^(investigate|follow through):\s*/i, ""));
|
|
3188
|
+
return normalized && !covered.has(normalized);
|
|
3189
|
+
});
|
|
3190
|
+
return kept.length > 0 ? kept.map((line) => `- ${line}`).join(`
|
|
3191
|
+
`) : null;
|
|
3192
|
+
}
|
|
3193
|
+
function filterAdditiveToolFallbacks(toolFallbacks, shownItems) {
|
|
3194
|
+
return toolFallbacks.filter((item) => {
|
|
3195
|
+
const normalized = normalizeStartupItem(item);
|
|
3196
|
+
return normalized && !shownItems.has(normalized);
|
|
3197
|
+
});
|
|
3198
|
+
}
|
|
3088
3199
|
function buildPromptFallback(context) {
|
|
3089
3200
|
const latest = (context.recentPrompts ?? []).find((prompt) => isMeaningfulPrompt2(prompt.prompt));
|
|
3090
3201
|
if (!latest?.prompt)
|
package/dist/hooks/stop.js
CHANGED
|
@@ -43,7 +43,10 @@ function extractRetrospective(observations, sessionId, projectId, userId) {
|
|
|
43
43
|
const investigated = extractInvestigated(observations);
|
|
44
44
|
const learned = extractLearned(observations);
|
|
45
45
|
const completed = extractCompleted(observations);
|
|
46
|
-
const nextSteps = extractNextSteps(observations
|
|
46
|
+
const nextSteps = extractNextSteps(observations, {
|
|
47
|
+
request,
|
|
48
|
+
completed
|
|
49
|
+
});
|
|
47
50
|
if (!request && !investigated && !learned && !completed && !nextSteps) {
|
|
48
51
|
return null;
|
|
49
52
|
}
|
|
@@ -101,13 +104,17 @@ ${facts}` : `- ${title}`;
|
|
|
101
104
|
return dedupeBulletLines(lines).join(`
|
|
102
105
|
`);
|
|
103
106
|
}
|
|
104
|
-
function extractNextSteps(observations) {
|
|
107
|
+
function extractNextSteps(observations, existing) {
|
|
105
108
|
if (observations.length < 2)
|
|
106
109
|
return null;
|
|
107
110
|
const lastQuarterStart = Math.max(0, Math.min(observations.length - 1, observations.length - 3, Math.floor(observations.length * 0.75)));
|
|
108
111
|
const lastQuarter = observations.slice(lastQuarterStart);
|
|
109
|
-
const
|
|
110
|
-
|
|
112
|
+
const alreadyCovered = new Set([
|
|
113
|
+
normalizeObservationKey(existing.request ?? ""),
|
|
114
|
+
...extractNormalizedSummaryItems(existing.completed)
|
|
115
|
+
].filter(Boolean));
|
|
116
|
+
const unresolved = lastQuarter.filter((o) => o.type === "bugfix" && o.narrative && /error|fail|exception/i.test(o.narrative) && !alreadyCovered.has(normalizeObservationKey(o.title)));
|
|
117
|
+
const explicitDecisions = lastQuarter.filter((o) => o.type === "decision").sort((a, b) => scoreNarrativeObservation(b) - scoreNarrativeObservation(a)).slice(0, 2).filter((o) => !alreadyCovered.has(normalizeObservationKey(o.title))).map((o) => `- Follow through: ${o.title}`);
|
|
111
118
|
if (unresolved.length === 0 && explicitDecisions.length === 0)
|
|
112
119
|
return null;
|
|
113
120
|
const lines = unresolved.map((o) => `- Investigate: ${o.title}`).slice(0, 3).concat(explicitDecisions);
|
|
@@ -148,6 +155,12 @@ function dedupeObservationsByTitle(observations) {
|
|
|
148
155
|
}
|
|
149
156
|
return deduped;
|
|
150
157
|
}
|
|
158
|
+
function extractNormalizedSummaryItems(value) {
|
|
159
|
+
if (!value)
|
|
160
|
+
return [];
|
|
161
|
+
return value.split(`
|
|
162
|
+
`).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "")).map((line) => line.replace(/^(investigate|follow through):\s*/i, "")).map((line) => normalizeObservationKey(line)).filter(Boolean);
|
|
163
|
+
}
|
|
151
164
|
function scoreCompletedObservation(obs) {
|
|
152
165
|
let score = scoreNarrativeObservation(obs);
|
|
153
166
|
if (obs.type === "feature")
|
|
@@ -801,9 +814,59 @@ function isVecExtensionLoaded(db) {
|
|
|
801
814
|
return false;
|
|
802
815
|
}
|
|
803
816
|
}
|
|
817
|
+
function tableExists(db, name) {
|
|
818
|
+
const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
|
|
819
|
+
return Boolean(row?.name);
|
|
820
|
+
}
|
|
821
|
+
function columnExists(db, table, column) {
|
|
822
|
+
if (!tableExists(db, table))
|
|
823
|
+
return false;
|
|
824
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
825
|
+
return rows.some((row) => row.name === column);
|
|
826
|
+
}
|
|
827
|
+
function ensureLegacyBaseTables(db) {
|
|
828
|
+
db.exec(`
|
|
829
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
830
|
+
key TEXT PRIMARY KEY,
|
|
831
|
+
value TEXT NOT NULL
|
|
832
|
+
);
|
|
833
|
+
`);
|
|
834
|
+
}
|
|
835
|
+
function inferLegacySchemaVersion(db) {
|
|
836
|
+
const hasProjects = tableExists(db, "projects");
|
|
837
|
+
const hasObservations = tableExists(db, "observations");
|
|
838
|
+
const hasSessions = tableExists(db, "sessions");
|
|
839
|
+
if (!(hasProjects && hasObservations && hasSessions))
|
|
840
|
+
return 0;
|
|
841
|
+
ensureLegacyBaseTables(db);
|
|
842
|
+
let version = 1;
|
|
843
|
+
if (columnExists(db, "observations", "superseded_by"))
|
|
844
|
+
version = Math.max(version, 2);
|
|
845
|
+
if (columnExists(db, "observations", "remote_source_id"))
|
|
846
|
+
version = Math.max(version, 3);
|
|
847
|
+
if (tableExists(db, "vec_observations"))
|
|
848
|
+
version = Math.max(version, 4);
|
|
849
|
+
const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
|
|
850
|
+
if (hasSessionMetrics || tableExists(db, "security_findings"))
|
|
851
|
+
version = Math.max(version, 5);
|
|
852
|
+
if (columnExists(db, "sessions", "risk_score"))
|
|
853
|
+
version = Math.max(version, 6);
|
|
854
|
+
if (tableExists(db, "packs_installed"))
|
|
855
|
+
version = Math.max(version, 7);
|
|
856
|
+
if (tableExists(db, "user_prompts"))
|
|
857
|
+
version = Math.max(version, 9);
|
|
858
|
+
if (tableExists(db, "tool_events"))
|
|
859
|
+
version = Math.max(version, 10);
|
|
860
|
+
return version;
|
|
861
|
+
}
|
|
804
862
|
function runMigrations(db) {
|
|
805
863
|
const currentVersion = db.query("PRAGMA user_version").get();
|
|
806
864
|
let version = currentVersion.user_version;
|
|
865
|
+
const inferred = inferLegacySchemaVersion(db);
|
|
866
|
+
if (inferred > version) {
|
|
867
|
+
db.exec(`PRAGMA user_version = ${inferred}`);
|
|
868
|
+
version = inferred;
|
|
869
|
+
}
|
|
807
870
|
for (const migration of MIGRATIONS) {
|
|
808
871
|
if (migration.version <= version)
|
|
809
872
|
continue;
|
|
@@ -1862,8 +1925,13 @@ function buildSummaryVectorDocument(summary, config, project, observations = [],
|
|
|
1862
1925
|
next_step_items: extractSectionItems(summary.next_steps),
|
|
1863
1926
|
prompt_count: captureContext?.prompt_count ?? 0,
|
|
1864
1927
|
tool_event_count: captureContext?.tool_event_count ?? 0,
|
|
1928
|
+
capture_state: captureContext?.capture_state ?? "summary-only",
|
|
1929
|
+
recent_request_prompts: captureContext?.recent_request_prompts ?? [],
|
|
1865
1930
|
latest_request: captureContext?.latest_request ?? null,
|
|
1866
1931
|
recent_tool_names: captureContext?.recent_tool_names ?? [],
|
|
1932
|
+
recent_tool_commands: captureContext?.recent_tool_commands ?? [],
|
|
1933
|
+
hot_files: captureContext?.hot_files ?? [],
|
|
1934
|
+
recent_outcomes: captureContext?.recent_outcomes ?? [],
|
|
1867
1935
|
decisions_count: valueSignals.decisions_count,
|
|
1868
1936
|
lessons_count: valueSignals.lessons_count,
|
|
1869
1937
|
discoveries_count: valueSignals.discoveries_count,
|
|
@@ -1907,7 +1975,7 @@ async function pushOutbox(db, client, config, batchSize = 50) {
|
|
|
1907
1975
|
const doc2 = buildSummaryVectorDocument(summary, config, {
|
|
1908
1976
|
canonical_id: project2.canonical_id,
|
|
1909
1977
|
name: project2.name
|
|
1910
|
-
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents));
|
|
1978
|
+
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents, summaryObservations));
|
|
1911
1979
|
batch.push({ entryId: entry.id, doc: doc2 });
|
|
1912
1980
|
continue;
|
|
1913
1981
|
}
|
|
@@ -1981,16 +2049,39 @@ function extractSectionItems(section) {
|
|
|
1981
2049
|
return section.split(`
|
|
1982
2050
|
`).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "").trim()).filter(Boolean).slice(0, 4);
|
|
1983
2051
|
}
|
|
1984
|
-
function buildSummaryCaptureContext(prompts, toolEvents) {
|
|
2052
|
+
function buildSummaryCaptureContext(prompts, toolEvents, observations) {
|
|
1985
2053
|
const latestRequest = prompts.length > 0 ? prompts[prompts.length - 1]?.prompt ?? null : null;
|
|
2054
|
+
const recentRequestPrompts = prompts.slice(-3).map((prompt) => prompt.prompt.trim()).filter(Boolean);
|
|
1986
2055
|
const recentToolNames = [...new Set(toolEvents.slice(-8).map((tool) => tool.tool_name).filter(Boolean))];
|
|
2056
|
+
const recentToolCommands = [...new Set(toolEvents.slice(-5).map((tool) => (tool.command ?? tool.file_path ?? "").trim()).filter(Boolean))];
|
|
2057
|
+
const hotFiles = [...new Set(observations.flatMap((obs) => [
|
|
2058
|
+
...parseJsonArray2(obs.files_modified),
|
|
2059
|
+
...parseJsonArray2(obs.files_read)
|
|
2060
|
+
]).filter(Boolean))].slice(0, 6);
|
|
2061
|
+
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);
|
|
2062
|
+
const captureState = prompts.length > 0 && toolEvents.length > 0 ? "rich" : prompts.length > 0 || toolEvents.length > 0 ? "partial" : "summary-only";
|
|
1987
2063
|
return {
|
|
1988
2064
|
prompt_count: prompts.length,
|
|
1989
2065
|
tool_event_count: toolEvents.length,
|
|
2066
|
+
recent_request_prompts: recentRequestPrompts,
|
|
1990
2067
|
latest_request: latestRequest,
|
|
1991
|
-
recent_tool_names: recentToolNames
|
|
2068
|
+
recent_tool_names: recentToolNames,
|
|
2069
|
+
recent_tool_commands: recentToolCommands,
|
|
2070
|
+
capture_state: captureState,
|
|
2071
|
+
hot_files: hotFiles,
|
|
2072
|
+
recent_outcomes: recentOutcomes
|
|
1992
2073
|
};
|
|
1993
2074
|
}
|
|
2075
|
+
function parseJsonArray2(value) {
|
|
2076
|
+
if (!value)
|
|
2077
|
+
return [];
|
|
2078
|
+
try {
|
|
2079
|
+
const parsed = JSON.parse(value);
|
|
2080
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
2081
|
+
} catch {
|
|
2082
|
+
return [];
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
1994
2085
|
|
|
1995
2086
|
// src/embeddings/embedder.ts
|
|
1996
2087
|
var _available = null;
|
|
@@ -723,9 +723,59 @@ function isVecExtensionLoaded(db) {
|
|
|
723
723
|
return false;
|
|
724
724
|
}
|
|
725
725
|
}
|
|
726
|
+
function tableExists(db, name) {
|
|
727
|
+
const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
|
|
728
|
+
return Boolean(row?.name);
|
|
729
|
+
}
|
|
730
|
+
function columnExists(db, table, column) {
|
|
731
|
+
if (!tableExists(db, table))
|
|
732
|
+
return false;
|
|
733
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
734
|
+
return rows.some((row) => row.name === column);
|
|
735
|
+
}
|
|
736
|
+
function ensureLegacyBaseTables(db) {
|
|
737
|
+
db.exec(`
|
|
738
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
739
|
+
key TEXT PRIMARY KEY,
|
|
740
|
+
value TEXT NOT NULL
|
|
741
|
+
);
|
|
742
|
+
`);
|
|
743
|
+
}
|
|
744
|
+
function inferLegacySchemaVersion(db) {
|
|
745
|
+
const hasProjects = tableExists(db, "projects");
|
|
746
|
+
const hasObservations = tableExists(db, "observations");
|
|
747
|
+
const hasSessions = tableExists(db, "sessions");
|
|
748
|
+
if (!(hasProjects && hasObservations && hasSessions))
|
|
749
|
+
return 0;
|
|
750
|
+
ensureLegacyBaseTables(db);
|
|
751
|
+
let version = 1;
|
|
752
|
+
if (columnExists(db, "observations", "superseded_by"))
|
|
753
|
+
version = Math.max(version, 2);
|
|
754
|
+
if (columnExists(db, "observations", "remote_source_id"))
|
|
755
|
+
version = Math.max(version, 3);
|
|
756
|
+
if (tableExists(db, "vec_observations"))
|
|
757
|
+
version = Math.max(version, 4);
|
|
758
|
+
const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
|
|
759
|
+
if (hasSessionMetrics || tableExists(db, "security_findings"))
|
|
760
|
+
version = Math.max(version, 5);
|
|
761
|
+
if (columnExists(db, "sessions", "risk_score"))
|
|
762
|
+
version = Math.max(version, 6);
|
|
763
|
+
if (tableExists(db, "packs_installed"))
|
|
764
|
+
version = Math.max(version, 7);
|
|
765
|
+
if (tableExists(db, "user_prompts"))
|
|
766
|
+
version = Math.max(version, 9);
|
|
767
|
+
if (tableExists(db, "tool_events"))
|
|
768
|
+
version = Math.max(version, 10);
|
|
769
|
+
return version;
|
|
770
|
+
}
|
|
726
771
|
function runMigrations(db) {
|
|
727
772
|
const currentVersion = db.query("PRAGMA user_version").get();
|
|
728
773
|
let version = currentVersion.user_version;
|
|
774
|
+
const inferred = inferLegacySchemaVersion(db);
|
|
775
|
+
if (inferred > version) {
|
|
776
|
+
db.exec(`PRAGMA user_version = ${inferred}`);
|
|
777
|
+
version = inferred;
|
|
778
|
+
}
|
|
729
779
|
for (const migration of MIGRATIONS) {
|
|
730
780
|
if (migration.version <= version)
|
|
731
781
|
continue;
|
package/dist/server.js
CHANGED
|
@@ -14129,9 +14129,59 @@ function isVecExtensionLoaded(db) {
|
|
|
14129
14129
|
return false;
|
|
14130
14130
|
}
|
|
14131
14131
|
}
|
|
14132
|
+
function tableExists(db, name) {
|
|
14133
|
+
const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
|
|
14134
|
+
return Boolean(row?.name);
|
|
14135
|
+
}
|
|
14136
|
+
function columnExists(db, table, column) {
|
|
14137
|
+
if (!tableExists(db, table))
|
|
14138
|
+
return false;
|
|
14139
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
14140
|
+
return rows.some((row) => row.name === column);
|
|
14141
|
+
}
|
|
14142
|
+
function ensureLegacyBaseTables(db) {
|
|
14143
|
+
db.exec(`
|
|
14144
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
14145
|
+
key TEXT PRIMARY KEY,
|
|
14146
|
+
value TEXT NOT NULL
|
|
14147
|
+
);
|
|
14148
|
+
`);
|
|
14149
|
+
}
|
|
14150
|
+
function inferLegacySchemaVersion(db) {
|
|
14151
|
+
const hasProjects = tableExists(db, "projects");
|
|
14152
|
+
const hasObservations = tableExists(db, "observations");
|
|
14153
|
+
const hasSessions = tableExists(db, "sessions");
|
|
14154
|
+
if (!(hasProjects && hasObservations && hasSessions))
|
|
14155
|
+
return 0;
|
|
14156
|
+
ensureLegacyBaseTables(db);
|
|
14157
|
+
let version2 = 1;
|
|
14158
|
+
if (columnExists(db, "observations", "superseded_by"))
|
|
14159
|
+
version2 = Math.max(version2, 2);
|
|
14160
|
+
if (columnExists(db, "observations", "remote_source_id"))
|
|
14161
|
+
version2 = Math.max(version2, 3);
|
|
14162
|
+
if (tableExists(db, "vec_observations"))
|
|
14163
|
+
version2 = Math.max(version2, 4);
|
|
14164
|
+
const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
|
|
14165
|
+
if (hasSessionMetrics || tableExists(db, "security_findings"))
|
|
14166
|
+
version2 = Math.max(version2, 5);
|
|
14167
|
+
if (columnExists(db, "sessions", "risk_score"))
|
|
14168
|
+
version2 = Math.max(version2, 6);
|
|
14169
|
+
if (tableExists(db, "packs_installed"))
|
|
14170
|
+
version2 = Math.max(version2, 7);
|
|
14171
|
+
if (tableExists(db, "user_prompts"))
|
|
14172
|
+
version2 = Math.max(version2, 9);
|
|
14173
|
+
if (tableExists(db, "tool_events"))
|
|
14174
|
+
version2 = Math.max(version2, 10);
|
|
14175
|
+
return version2;
|
|
14176
|
+
}
|
|
14132
14177
|
function runMigrations(db) {
|
|
14133
14178
|
const currentVersion = db.query("PRAGMA user_version").get();
|
|
14134
14179
|
let version2 = currentVersion.user_version;
|
|
14180
|
+
const inferred = inferLegacySchemaVersion(db);
|
|
14181
|
+
if (inferred > version2) {
|
|
14182
|
+
db.exec(`PRAGMA user_version = ${inferred}`);
|
|
14183
|
+
version2 = inferred;
|
|
14184
|
+
}
|
|
14135
14185
|
for (const migration of MIGRATIONS) {
|
|
14136
14186
|
if (migration.version <= version2)
|
|
14137
14187
|
continue;
|
|
@@ -17336,18 +17386,44 @@ function getSessionContext(db, input) {
|
|
|
17336
17386
|
});
|
|
17337
17387
|
if (!context)
|
|
17338
17388
|
return null;
|
|
17389
|
+
const recentRequests = context.recentPrompts?.length ?? 0;
|
|
17390
|
+
const recentTools = context.recentToolEvents?.length ?? 0;
|
|
17391
|
+
const captureState = recentRequests > 0 && recentTools > 0 ? "rich" : recentRequests > 0 || recentTools > 0 ? "partial" : "summary-only";
|
|
17392
|
+
const hotFiles = buildHotFiles(context);
|
|
17339
17393
|
return {
|
|
17340
17394
|
project_name: context.project_name,
|
|
17341
17395
|
canonical_id: context.canonical_id,
|
|
17342
17396
|
session_count: context.session_count,
|
|
17343
17397
|
total_active: context.total_active,
|
|
17344
|
-
recent_requests:
|
|
17345
|
-
recent_tools:
|
|
17398
|
+
recent_requests: recentRequests,
|
|
17399
|
+
recent_tools: recentTools,
|
|
17346
17400
|
recent_sessions: context.recentSessions?.length ?? 0,
|
|
17347
|
-
|
|
17401
|
+
recent_outcomes: context.recentOutcomes ?? [],
|
|
17402
|
+
hot_files: hotFiles,
|
|
17403
|
+
capture_state: captureState,
|
|
17404
|
+
raw_capture_active: recentRequests > 0 || recentTools > 0,
|
|
17348
17405
|
preview: formatContextForInjection(context)
|
|
17349
17406
|
};
|
|
17350
17407
|
}
|
|
17408
|
+
function buildHotFiles(context) {
|
|
17409
|
+
const counts = new Map;
|
|
17410
|
+
for (const obs of context.observations) {
|
|
17411
|
+
for (const path of [...parseJsonArray(obs.files_read), ...parseJsonArray(obs.files_modified)]) {
|
|
17412
|
+
counts.set(path, (counts.get(path) ?? 0) + 1);
|
|
17413
|
+
}
|
|
17414
|
+
}
|
|
17415
|
+
return Array.from(counts.entries()).map(([path, count]) => ({ path, count })).sort((a, b) => b.count - a.count || a.path.localeCompare(b.path)).slice(0, 6);
|
|
17416
|
+
}
|
|
17417
|
+
function parseJsonArray(value) {
|
|
17418
|
+
if (!value)
|
|
17419
|
+
return [];
|
|
17420
|
+
try {
|
|
17421
|
+
const parsed = JSON.parse(value);
|
|
17422
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
17423
|
+
} catch {
|
|
17424
|
+
return [];
|
|
17425
|
+
}
|
|
17426
|
+
}
|
|
17351
17427
|
|
|
17352
17428
|
// src/tools/send-message.ts
|
|
17353
17429
|
async function sendMessage(db, config2, input) {
|
|
@@ -17964,8 +18040,13 @@ function buildSummaryVectorDocument(summary, config2, project, observations = []
|
|
|
17964
18040
|
next_step_items: extractSectionItems2(summary.next_steps),
|
|
17965
18041
|
prompt_count: captureContext?.prompt_count ?? 0,
|
|
17966
18042
|
tool_event_count: captureContext?.tool_event_count ?? 0,
|
|
18043
|
+
capture_state: captureContext?.capture_state ?? "summary-only",
|
|
18044
|
+
recent_request_prompts: captureContext?.recent_request_prompts ?? [],
|
|
17967
18045
|
latest_request: captureContext?.latest_request ?? null,
|
|
17968
18046
|
recent_tool_names: captureContext?.recent_tool_names ?? [],
|
|
18047
|
+
recent_tool_commands: captureContext?.recent_tool_commands ?? [],
|
|
18048
|
+
hot_files: captureContext?.hot_files ?? [],
|
|
18049
|
+
recent_outcomes: captureContext?.recent_outcomes ?? [],
|
|
17969
18050
|
decisions_count: valueSignals.decisions_count,
|
|
17970
18051
|
lessons_count: valueSignals.lessons_count,
|
|
17971
18052
|
discoveries_count: valueSignals.discoveries_count,
|
|
@@ -18009,7 +18090,7 @@ async function pushOutbox(db, client, config2, batchSize = 50) {
|
|
|
18009
18090
|
const doc3 = buildSummaryVectorDocument(summary, config2, {
|
|
18010
18091
|
canonical_id: project2.canonical_id,
|
|
18011
18092
|
name: project2.name
|
|
18012
|
-
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents));
|
|
18093
|
+
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents, summaryObservations));
|
|
18013
18094
|
batch.push({ entryId: entry.id, doc: doc3 });
|
|
18014
18095
|
continue;
|
|
18015
18096
|
}
|
|
@@ -18083,16 +18164,39 @@ function extractSectionItems2(section) {
|
|
|
18083
18164
|
return section.split(`
|
|
18084
18165
|
`).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "").trim()).filter(Boolean).slice(0, 4);
|
|
18085
18166
|
}
|
|
18086
|
-
function buildSummaryCaptureContext(prompts, toolEvents) {
|
|
18167
|
+
function buildSummaryCaptureContext(prompts, toolEvents, observations) {
|
|
18087
18168
|
const latestRequest = prompts.length > 0 ? prompts[prompts.length - 1]?.prompt ?? null : null;
|
|
18169
|
+
const recentRequestPrompts = prompts.slice(-3).map((prompt) => prompt.prompt.trim()).filter(Boolean);
|
|
18088
18170
|
const recentToolNames = [...new Set(toolEvents.slice(-8).map((tool) => tool.tool_name).filter(Boolean))];
|
|
18171
|
+
const recentToolCommands = [...new Set(toolEvents.slice(-5).map((tool) => (tool.command ?? tool.file_path ?? "").trim()).filter(Boolean))];
|
|
18172
|
+
const hotFiles = [...new Set(observations.flatMap((obs) => [
|
|
18173
|
+
...parseJsonArray2(obs.files_modified),
|
|
18174
|
+
...parseJsonArray2(obs.files_read)
|
|
18175
|
+
]).filter(Boolean))].slice(0, 6);
|
|
18176
|
+
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);
|
|
18177
|
+
const captureState = prompts.length > 0 && toolEvents.length > 0 ? "rich" : prompts.length > 0 || toolEvents.length > 0 ? "partial" : "summary-only";
|
|
18089
18178
|
return {
|
|
18090
18179
|
prompt_count: prompts.length,
|
|
18091
18180
|
tool_event_count: toolEvents.length,
|
|
18181
|
+
recent_request_prompts: recentRequestPrompts,
|
|
18092
18182
|
latest_request: latestRequest,
|
|
18093
|
-
recent_tool_names: recentToolNames
|
|
18183
|
+
recent_tool_names: recentToolNames,
|
|
18184
|
+
recent_tool_commands: recentToolCommands,
|
|
18185
|
+
capture_state: captureState,
|
|
18186
|
+
hot_files: hotFiles,
|
|
18187
|
+
recent_outcomes: recentOutcomes
|
|
18094
18188
|
};
|
|
18095
18189
|
}
|
|
18190
|
+
function parseJsonArray2(value) {
|
|
18191
|
+
if (!value)
|
|
18192
|
+
return [];
|
|
18193
|
+
try {
|
|
18194
|
+
const parsed = JSON.parse(value);
|
|
18195
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
18196
|
+
} catch {
|
|
18197
|
+
return [];
|
|
18198
|
+
}
|
|
18199
|
+
}
|
|
18096
18200
|
|
|
18097
18201
|
// src/sync/pull.ts
|
|
18098
18202
|
var PULL_CURSOR_KEY = "pull_cursor";
|