engrm 0.4.10 → 0.4.12
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 +52 -1
- package/dist/hooks/elicitation-result.js +52 -1
- package/dist/hooks/post-tool-use.js +52 -1
- package/dist/hooks/pre-compact.js +52 -1
- package/dist/hooks/sentinel.js +52 -1
- package/dist/hooks/session-start.js +120 -8
- package/dist/hooks/stop.js +100 -8
- package/dist/hooks/user-prompt-submit.js +52 -1
- package/dist/server.js +112 -7
- 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;
|
|
@@ -722,6 +772,7 @@ function openNodeDatabase(dbPath) {
|
|
|
722
772
|
const BetterSqlite3 = __require("better-sqlite3");
|
|
723
773
|
const raw = new BetterSqlite3(dbPath);
|
|
724
774
|
return {
|
|
775
|
+
__raw: raw,
|
|
725
776
|
query(sql) {
|
|
726
777
|
const stmt = raw.prepare(sql);
|
|
727
778
|
return {
|
|
@@ -759,7 +810,7 @@ class MemDatabase {
|
|
|
759
810
|
loadVecExtension() {
|
|
760
811
|
try {
|
|
761
812
|
const sqliteVec = __require("sqlite-vec");
|
|
762
|
-
sqliteVec.load(this.db);
|
|
813
|
+
sqliteVec.load(this.db.__raw ?? this.db);
|
|
763
814
|
return true;
|
|
764
815
|
} catch {
|
|
765
816
|
return false;
|
|
@@ -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;
|
|
@@ -1548,6 +1598,7 @@ function openNodeDatabase(dbPath) {
|
|
|
1548
1598
|
const BetterSqlite3 = __require("better-sqlite3");
|
|
1549
1599
|
const raw = new BetterSqlite3(dbPath);
|
|
1550
1600
|
return {
|
|
1601
|
+
__raw: raw,
|
|
1551
1602
|
query(sql) {
|
|
1552
1603
|
const stmt = raw.prepare(sql);
|
|
1553
1604
|
return {
|
|
@@ -1585,7 +1636,7 @@ class MemDatabase {
|
|
|
1585
1636
|
loadVecExtension() {
|
|
1586
1637
|
try {
|
|
1587
1638
|
const sqliteVec = __require("sqlite-vec");
|
|
1588
|
-
sqliteVec.load(this.db);
|
|
1639
|
+
sqliteVec.load(this.db.__raw ?? this.db);
|
|
1589
1640
|
return true;
|
|
1590
1641
|
} catch {
|
|
1591
1642
|
return false;
|
|
@@ -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;
|
|
@@ -896,6 +946,7 @@ function openNodeDatabase(dbPath) {
|
|
|
896
946
|
const BetterSqlite3 = __require("better-sqlite3");
|
|
897
947
|
const raw = new BetterSqlite3(dbPath);
|
|
898
948
|
return {
|
|
949
|
+
__raw: raw,
|
|
899
950
|
query(sql) {
|
|
900
951
|
const stmt = raw.prepare(sql);
|
|
901
952
|
return {
|
|
@@ -933,7 +984,7 @@ class MemDatabase {
|
|
|
933
984
|
loadVecExtension() {
|
|
934
985
|
try {
|
|
935
986
|
const sqliteVec = __require("sqlite-vec");
|
|
936
|
-
sqliteVec.load(this.db);
|
|
987
|
+
sqliteVec.load(this.db.__raw ?? this.db);
|
|
937
988
|
return true;
|
|
938
989
|
} catch {
|
|
939
990
|
return false;
|
|
@@ -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;
|
|
@@ -690,6 +740,7 @@ function openNodeDatabase(dbPath) {
|
|
|
690
740
|
const BetterSqlite3 = __require("better-sqlite3");
|
|
691
741
|
const raw = new BetterSqlite3(dbPath);
|
|
692
742
|
return {
|
|
743
|
+
__raw: raw,
|
|
693
744
|
query(sql) {
|
|
694
745
|
const stmt = raw.prepare(sql);
|
|
695
746
|
return {
|
|
@@ -727,7 +778,7 @@ class MemDatabase {
|
|
|
727
778
|
loadVecExtension() {
|
|
728
779
|
try {
|
|
729
780
|
const sqliteVec = __require("sqlite-vec");
|
|
730
|
-
sqliteVec.load(this.db);
|
|
781
|
+
sqliteVec.load(this.db.__raw ?? this.db);
|
|
731
782
|
return true;
|
|
732
783
|
} catch {
|
|
733
784
|
return false;
|
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;
|
|
@@ -766,6 +816,7 @@ function openNodeDatabase(dbPath) {
|
|
|
766
816
|
const BetterSqlite3 = __require("better-sqlite3");
|
|
767
817
|
const raw = new BetterSqlite3(dbPath);
|
|
768
818
|
return {
|
|
819
|
+
__raw: raw,
|
|
769
820
|
query(sql) {
|
|
770
821
|
const stmt = raw.prepare(sql);
|
|
771
822
|
return {
|
|
@@ -803,7 +854,7 @@ class MemDatabase {
|
|
|
803
854
|
loadVecExtension() {
|
|
804
855
|
try {
|
|
805
856
|
const sqliteVec = __require("sqlite-vec");
|
|
806
|
-
sqliteVec.load(this.db);
|
|
857
|
+
sqliteVec.load(this.db.__raw ?? this.db);
|
|
807
858
|
return true;
|
|
808
859
|
} catch {
|
|
809
860
|
return false;
|
|
@@ -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;
|
|
@@ -2277,6 +2327,7 @@ function openNodeDatabase(dbPath) {
|
|
|
2277
2327
|
const BetterSqlite3 = __require("better-sqlite3");
|
|
2278
2328
|
const raw = new BetterSqlite3(dbPath);
|
|
2279
2329
|
return {
|
|
2330
|
+
__raw: raw,
|
|
2280
2331
|
query(sql) {
|
|
2281
2332
|
const stmt = raw.prepare(sql);
|
|
2282
2333
|
return {
|
|
@@ -2314,7 +2365,7 @@ class MemDatabase {
|
|
|
2314
2365
|
loadVecExtension() {
|
|
2315
2366
|
try {
|
|
2316
2367
|
const sqliteVec = __require("sqlite-vec");
|
|
2317
|
-
sqliteVec.load(this.db);
|
|
2368
|
+
sqliteVec.load(this.db.__raw ?? this.db);
|
|
2318
2369
|
return true;
|
|
2319
2370
|
} catch {
|
|
2320
2371
|
return false;
|
|
@@ -3014,19 +3065,28 @@ function formatVisibleStartupBrief(context) {
|
|
|
3014
3065
|
const sessionFallbacks = sessionFallbacksFromContext(context);
|
|
3015
3066
|
const recentOutcomeLines = buildRecentOutcomeLines(context, latest);
|
|
3016
3067
|
const projectSignals = buildProjectSignalLine(context);
|
|
3068
|
+
const shownItems = new Set;
|
|
3017
3069
|
if (promptLines.length > 0) {
|
|
3018
3070
|
lines.push(`${c2.cyan}Recent Requests:${c2.reset}`);
|
|
3019
3071
|
for (const item of promptLines) {
|
|
3020
3072
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3073
|
+
rememberShownItem(shownItems, item);
|
|
3021
3074
|
}
|
|
3022
3075
|
}
|
|
3023
3076
|
if (latest) {
|
|
3077
|
+
const sanitizedNextSteps = sanitizeNextSteps(latest.next_steps, {
|
|
3078
|
+
request: currentRequest,
|
|
3079
|
+
investigated: chooseSection(latest.investigated, observationFallbacks.investigated, "Investigated"),
|
|
3080
|
+
learned: latest.learned,
|
|
3081
|
+
completed: chooseSection(latest.completed, observationFallbacks.completed, "Completed"),
|
|
3082
|
+
recentOutcomes: recentOutcomeLines
|
|
3083
|
+
});
|
|
3024
3084
|
const sections = [
|
|
3025
3085
|
["Request", currentRequest, 1],
|
|
3026
3086
|
["Investigated", chooseSection(latest.investigated, observationFallbacks.investigated, "Investigated"), 2],
|
|
3027
3087
|
["Learned", latest.learned, 2],
|
|
3028
3088
|
["Completed", chooseSection(latest.completed, observationFallbacks.completed, "Completed"), 2],
|
|
3029
|
-
["Next Steps",
|
|
3089
|
+
["Next Steps", sanitizedNextSteps, 2]
|
|
3030
3090
|
];
|
|
3031
3091
|
for (const [label, value, maxItems] of sections) {
|
|
3032
3092
|
const formatted = toSplashLines(value, maxItems ?? 2);
|
|
@@ -3034,33 +3094,45 @@ function formatVisibleStartupBrief(context) {
|
|
|
3034
3094
|
lines.push(`${c2.cyan}${label}:${c2.reset}`);
|
|
3035
3095
|
for (const item of formatted) {
|
|
3036
3096
|
lines.push(` ${item}`);
|
|
3097
|
+
rememberShownItem(shownItems, item);
|
|
3037
3098
|
}
|
|
3038
3099
|
}
|
|
3039
3100
|
}
|
|
3040
3101
|
} else if (currentRequest && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
|
|
3041
3102
|
lines.push(`${c2.cyan}Current Request:${c2.reset}`);
|
|
3042
3103
|
lines.push(` - ${truncateInline(currentRequest, 160)}`);
|
|
3104
|
+
rememberShownItem(shownItems, currentRequest);
|
|
3043
3105
|
if (toolFallbacks.length > 0) {
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
lines.push(
|
|
3106
|
+
const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
|
|
3107
|
+
if (additiveTools.length > 0) {
|
|
3108
|
+
lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
|
|
3109
|
+
for (const item of additiveTools) {
|
|
3110
|
+
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3111
|
+
rememberShownItem(shownItems, item);
|
|
3112
|
+
}
|
|
3047
3113
|
}
|
|
3048
3114
|
}
|
|
3049
3115
|
}
|
|
3050
3116
|
if (latest && currentRequest && !hasRequestSection(lines) && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
|
|
3051
3117
|
lines.push(`${c2.cyan}Current Request:${c2.reset}`);
|
|
3052
3118
|
lines.push(` - ${truncateInline(currentRequest, 160)}`);
|
|
3119
|
+
rememberShownItem(shownItems, currentRequest);
|
|
3053
3120
|
}
|
|
3054
3121
|
if (recentOutcomeLines.length > 0) {
|
|
3055
3122
|
lines.push(`${c2.cyan}Recent Work:${c2.reset}`);
|
|
3056
3123
|
for (const item of recentOutcomeLines) {
|
|
3057
3124
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3125
|
+
rememberShownItem(shownItems, item);
|
|
3058
3126
|
}
|
|
3059
3127
|
}
|
|
3060
3128
|
if (toolFallbacks.length > 0 && latest) {
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
lines.push(
|
|
3129
|
+
const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
|
|
3130
|
+
if (additiveTools.length > 0) {
|
|
3131
|
+
lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
|
|
3132
|
+
for (const item of additiveTools) {
|
|
3133
|
+
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3134
|
+
rememberShownItem(shownItems, item);
|
|
3135
|
+
}
|
|
3064
3136
|
}
|
|
3065
3137
|
}
|
|
3066
3138
|
if (sessionFallbacks.length > 0) {
|
|
@@ -3085,6 +3157,46 @@ function formatVisibleStartupBrief(context) {
|
|
|
3085
3157
|
}
|
|
3086
3158
|
return lines.slice(0, 14);
|
|
3087
3159
|
}
|
|
3160
|
+
function rememberShownItem(shown, value) {
|
|
3161
|
+
if (!value)
|
|
3162
|
+
return;
|
|
3163
|
+
for (const item of value.split(`
|
|
3164
|
+
`)) {
|
|
3165
|
+
const normalized = normalizeStartupItem(item);
|
|
3166
|
+
if (normalized)
|
|
3167
|
+
shown.add(normalized);
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
function extractNormalizedSplashItems(value) {
|
|
3171
|
+
if (!value)
|
|
3172
|
+
return [];
|
|
3173
|
+
return value.split(`
|
|
3174
|
+
`).map((line) => normalizeStartupItem(line)).filter(Boolean);
|
|
3175
|
+
}
|
|
3176
|
+
function sanitizeNextSteps(nextSteps, context) {
|
|
3177
|
+
if (!nextSteps)
|
|
3178
|
+
return null;
|
|
3179
|
+
const covered = new Set([
|
|
3180
|
+
normalizeStartupItem(context.request ?? ""),
|
|
3181
|
+
...extractNormalizedSplashItems(context.investigated),
|
|
3182
|
+
...extractNormalizedSplashItems(context.learned),
|
|
3183
|
+
...extractNormalizedSplashItems(context.completed),
|
|
3184
|
+
...context.recentOutcomes.map((item) => normalizeStartupItem(item))
|
|
3185
|
+
].filter(Boolean));
|
|
3186
|
+
const kept = nextSteps.split(`
|
|
3187
|
+
`).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "")).filter((line) => {
|
|
3188
|
+
const normalized = normalizeStartupItem(line.replace(/^(investigate|follow through):\s*/i, ""));
|
|
3189
|
+
return normalized && !covered.has(normalized);
|
|
3190
|
+
});
|
|
3191
|
+
return kept.length > 0 ? kept.map((line) => `- ${line}`).join(`
|
|
3192
|
+
`) : null;
|
|
3193
|
+
}
|
|
3194
|
+
function filterAdditiveToolFallbacks(toolFallbacks, shownItems) {
|
|
3195
|
+
return toolFallbacks.filter((item) => {
|
|
3196
|
+
const normalized = normalizeStartupItem(item);
|
|
3197
|
+
return normalized && !shownItems.has(normalized);
|
|
3198
|
+
});
|
|
3199
|
+
}
|
|
3088
3200
|
function buildPromptFallback(context) {
|
|
3089
3201
|
const latest = (context.recentPrompts ?? []).find((prompt) => isMeaningfulPrompt2(prompt.prompt));
|
|
3090
3202
|
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;
|
|
@@ -912,6 +975,7 @@ function openNodeDatabase(dbPath) {
|
|
|
912
975
|
const BetterSqlite3 = __require("better-sqlite3");
|
|
913
976
|
const raw = new BetterSqlite3(dbPath);
|
|
914
977
|
return {
|
|
978
|
+
__raw: raw,
|
|
915
979
|
query(sql) {
|
|
916
980
|
const stmt = raw.prepare(sql);
|
|
917
981
|
return {
|
|
@@ -949,7 +1013,7 @@ class MemDatabase {
|
|
|
949
1013
|
loadVecExtension() {
|
|
950
1014
|
try {
|
|
951
1015
|
const sqliteVec = __require("sqlite-vec");
|
|
952
|
-
sqliteVec.load(this.db);
|
|
1016
|
+
sqliteVec.load(this.db.__raw ?? this.db);
|
|
953
1017
|
return true;
|
|
954
1018
|
} catch {
|
|
955
1019
|
return false;
|
|
@@ -1862,8 +1926,13 @@ function buildSummaryVectorDocument(summary, config, project, observations = [],
|
|
|
1862
1926
|
next_step_items: extractSectionItems(summary.next_steps),
|
|
1863
1927
|
prompt_count: captureContext?.prompt_count ?? 0,
|
|
1864
1928
|
tool_event_count: captureContext?.tool_event_count ?? 0,
|
|
1929
|
+
capture_state: captureContext?.capture_state ?? "summary-only",
|
|
1930
|
+
recent_request_prompts: captureContext?.recent_request_prompts ?? [],
|
|
1865
1931
|
latest_request: captureContext?.latest_request ?? null,
|
|
1866
1932
|
recent_tool_names: captureContext?.recent_tool_names ?? [],
|
|
1933
|
+
recent_tool_commands: captureContext?.recent_tool_commands ?? [],
|
|
1934
|
+
hot_files: captureContext?.hot_files ?? [],
|
|
1935
|
+
recent_outcomes: captureContext?.recent_outcomes ?? [],
|
|
1867
1936
|
decisions_count: valueSignals.decisions_count,
|
|
1868
1937
|
lessons_count: valueSignals.lessons_count,
|
|
1869
1938
|
discoveries_count: valueSignals.discoveries_count,
|
|
@@ -1907,7 +1976,7 @@ async function pushOutbox(db, client, config, batchSize = 50) {
|
|
|
1907
1976
|
const doc2 = buildSummaryVectorDocument(summary, config, {
|
|
1908
1977
|
canonical_id: project2.canonical_id,
|
|
1909
1978
|
name: project2.name
|
|
1910
|
-
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents));
|
|
1979
|
+
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents, summaryObservations));
|
|
1911
1980
|
batch.push({ entryId: entry.id, doc: doc2 });
|
|
1912
1981
|
continue;
|
|
1913
1982
|
}
|
|
@@ -1981,16 +2050,39 @@ function extractSectionItems(section) {
|
|
|
1981
2050
|
return section.split(`
|
|
1982
2051
|
`).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "").trim()).filter(Boolean).slice(0, 4);
|
|
1983
2052
|
}
|
|
1984
|
-
function buildSummaryCaptureContext(prompts, toolEvents) {
|
|
2053
|
+
function buildSummaryCaptureContext(prompts, toolEvents, observations) {
|
|
1985
2054
|
const latestRequest = prompts.length > 0 ? prompts[prompts.length - 1]?.prompt ?? null : null;
|
|
2055
|
+
const recentRequestPrompts = prompts.slice(-3).map((prompt) => prompt.prompt.trim()).filter(Boolean);
|
|
1986
2056
|
const recentToolNames = [...new Set(toolEvents.slice(-8).map((tool) => tool.tool_name).filter(Boolean))];
|
|
2057
|
+
const recentToolCommands = [...new Set(toolEvents.slice(-5).map((tool) => (tool.command ?? tool.file_path ?? "").trim()).filter(Boolean))];
|
|
2058
|
+
const hotFiles = [...new Set(observations.flatMap((obs) => [
|
|
2059
|
+
...parseJsonArray2(obs.files_modified),
|
|
2060
|
+
...parseJsonArray2(obs.files_read)
|
|
2061
|
+
]).filter(Boolean))].slice(0, 6);
|
|
2062
|
+
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);
|
|
2063
|
+
const captureState = prompts.length > 0 && toolEvents.length > 0 ? "rich" : prompts.length > 0 || toolEvents.length > 0 ? "partial" : "summary-only";
|
|
1987
2064
|
return {
|
|
1988
2065
|
prompt_count: prompts.length,
|
|
1989
2066
|
tool_event_count: toolEvents.length,
|
|
2067
|
+
recent_request_prompts: recentRequestPrompts,
|
|
1990
2068
|
latest_request: latestRequest,
|
|
1991
|
-
recent_tool_names: recentToolNames
|
|
2069
|
+
recent_tool_names: recentToolNames,
|
|
2070
|
+
recent_tool_commands: recentToolCommands,
|
|
2071
|
+
capture_state: captureState,
|
|
2072
|
+
hot_files: hotFiles,
|
|
2073
|
+
recent_outcomes: recentOutcomes
|
|
1992
2074
|
};
|
|
1993
2075
|
}
|
|
2076
|
+
function parseJsonArray2(value) {
|
|
2077
|
+
if (!value)
|
|
2078
|
+
return [];
|
|
2079
|
+
try {
|
|
2080
|
+
const parsed = JSON.parse(value);
|
|
2081
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
2082
|
+
} catch {
|
|
2083
|
+
return [];
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
1994
2086
|
|
|
1995
2087
|
// src/embeddings/embedder.ts
|
|
1996
2088
|
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;
|
|
@@ -834,6 +884,7 @@ function openNodeDatabase(dbPath) {
|
|
|
834
884
|
const BetterSqlite3 = __require("better-sqlite3");
|
|
835
885
|
const raw = new BetterSqlite3(dbPath);
|
|
836
886
|
return {
|
|
887
|
+
__raw: raw,
|
|
837
888
|
query(sql) {
|
|
838
889
|
const stmt = raw.prepare(sql);
|
|
839
890
|
return {
|
|
@@ -871,7 +922,7 @@ class MemDatabase {
|
|
|
871
922
|
loadVecExtension() {
|
|
872
923
|
try {
|
|
873
924
|
const sqliteVec = __require("sqlite-vec");
|
|
874
|
-
sqliteVec.load(this.db);
|
|
925
|
+
sqliteVec.load(this.db.__raw ?? this.db);
|
|
875
926
|
return true;
|
|
876
927
|
} catch {
|
|
877
928
|
return false;
|
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;
|
|
@@ -14244,6 +14294,7 @@ function openNodeDatabase(dbPath) {
|
|
|
14244
14294
|
const BetterSqlite3 = __require("better-sqlite3");
|
|
14245
14295
|
const raw = new BetterSqlite3(dbPath);
|
|
14246
14296
|
return {
|
|
14297
|
+
__raw: raw,
|
|
14247
14298
|
query(sql) {
|
|
14248
14299
|
const stmt = raw.prepare(sql);
|
|
14249
14300
|
return {
|
|
@@ -14281,7 +14332,7 @@ class MemDatabase {
|
|
|
14281
14332
|
loadVecExtension() {
|
|
14282
14333
|
try {
|
|
14283
14334
|
const sqliteVec = __require("sqlite-vec");
|
|
14284
|
-
sqliteVec.load(this.db);
|
|
14335
|
+
sqliteVec.load(this.db.__raw ?? this.db);
|
|
14285
14336
|
return true;
|
|
14286
14337
|
} catch {
|
|
14287
14338
|
return false;
|
|
@@ -17336,18 +17387,44 @@ function getSessionContext(db, input) {
|
|
|
17336
17387
|
});
|
|
17337
17388
|
if (!context)
|
|
17338
17389
|
return null;
|
|
17390
|
+
const recentRequests = context.recentPrompts?.length ?? 0;
|
|
17391
|
+
const recentTools = context.recentToolEvents?.length ?? 0;
|
|
17392
|
+
const captureState = recentRequests > 0 && recentTools > 0 ? "rich" : recentRequests > 0 || recentTools > 0 ? "partial" : "summary-only";
|
|
17393
|
+
const hotFiles = buildHotFiles(context);
|
|
17339
17394
|
return {
|
|
17340
17395
|
project_name: context.project_name,
|
|
17341
17396
|
canonical_id: context.canonical_id,
|
|
17342
17397
|
session_count: context.session_count,
|
|
17343
17398
|
total_active: context.total_active,
|
|
17344
|
-
recent_requests:
|
|
17345
|
-
recent_tools:
|
|
17399
|
+
recent_requests: recentRequests,
|
|
17400
|
+
recent_tools: recentTools,
|
|
17346
17401
|
recent_sessions: context.recentSessions?.length ?? 0,
|
|
17347
|
-
|
|
17402
|
+
recent_outcomes: context.recentOutcomes ?? [],
|
|
17403
|
+
hot_files: hotFiles,
|
|
17404
|
+
capture_state: captureState,
|
|
17405
|
+
raw_capture_active: recentRequests > 0 || recentTools > 0,
|
|
17348
17406
|
preview: formatContextForInjection(context)
|
|
17349
17407
|
};
|
|
17350
17408
|
}
|
|
17409
|
+
function buildHotFiles(context) {
|
|
17410
|
+
const counts = new Map;
|
|
17411
|
+
for (const obs of context.observations) {
|
|
17412
|
+
for (const path of [...parseJsonArray(obs.files_read), ...parseJsonArray(obs.files_modified)]) {
|
|
17413
|
+
counts.set(path, (counts.get(path) ?? 0) + 1);
|
|
17414
|
+
}
|
|
17415
|
+
}
|
|
17416
|
+
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);
|
|
17417
|
+
}
|
|
17418
|
+
function parseJsonArray(value) {
|
|
17419
|
+
if (!value)
|
|
17420
|
+
return [];
|
|
17421
|
+
try {
|
|
17422
|
+
const parsed = JSON.parse(value);
|
|
17423
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
17424
|
+
} catch {
|
|
17425
|
+
return [];
|
|
17426
|
+
}
|
|
17427
|
+
}
|
|
17351
17428
|
|
|
17352
17429
|
// src/tools/send-message.ts
|
|
17353
17430
|
async function sendMessage(db, config2, input) {
|
|
@@ -17964,8 +18041,13 @@ function buildSummaryVectorDocument(summary, config2, project, observations = []
|
|
|
17964
18041
|
next_step_items: extractSectionItems2(summary.next_steps),
|
|
17965
18042
|
prompt_count: captureContext?.prompt_count ?? 0,
|
|
17966
18043
|
tool_event_count: captureContext?.tool_event_count ?? 0,
|
|
18044
|
+
capture_state: captureContext?.capture_state ?? "summary-only",
|
|
18045
|
+
recent_request_prompts: captureContext?.recent_request_prompts ?? [],
|
|
17967
18046
|
latest_request: captureContext?.latest_request ?? null,
|
|
17968
18047
|
recent_tool_names: captureContext?.recent_tool_names ?? [],
|
|
18048
|
+
recent_tool_commands: captureContext?.recent_tool_commands ?? [],
|
|
18049
|
+
hot_files: captureContext?.hot_files ?? [],
|
|
18050
|
+
recent_outcomes: captureContext?.recent_outcomes ?? [],
|
|
17969
18051
|
decisions_count: valueSignals.decisions_count,
|
|
17970
18052
|
lessons_count: valueSignals.lessons_count,
|
|
17971
18053
|
discoveries_count: valueSignals.discoveries_count,
|
|
@@ -18009,7 +18091,7 @@ async function pushOutbox(db, client, config2, batchSize = 50) {
|
|
|
18009
18091
|
const doc3 = buildSummaryVectorDocument(summary, config2, {
|
|
18010
18092
|
canonical_id: project2.canonical_id,
|
|
18011
18093
|
name: project2.name
|
|
18012
|
-
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents));
|
|
18094
|
+
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents, summaryObservations));
|
|
18013
18095
|
batch.push({ entryId: entry.id, doc: doc3 });
|
|
18014
18096
|
continue;
|
|
18015
18097
|
}
|
|
@@ -18083,16 +18165,39 @@ function extractSectionItems2(section) {
|
|
|
18083
18165
|
return section.split(`
|
|
18084
18166
|
`).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "").trim()).filter(Boolean).slice(0, 4);
|
|
18085
18167
|
}
|
|
18086
|
-
function buildSummaryCaptureContext(prompts, toolEvents) {
|
|
18168
|
+
function buildSummaryCaptureContext(prompts, toolEvents, observations) {
|
|
18087
18169
|
const latestRequest = prompts.length > 0 ? prompts[prompts.length - 1]?.prompt ?? null : null;
|
|
18170
|
+
const recentRequestPrompts = prompts.slice(-3).map((prompt) => prompt.prompt.trim()).filter(Boolean);
|
|
18088
18171
|
const recentToolNames = [...new Set(toolEvents.slice(-8).map((tool) => tool.tool_name).filter(Boolean))];
|
|
18172
|
+
const recentToolCommands = [...new Set(toolEvents.slice(-5).map((tool) => (tool.command ?? tool.file_path ?? "").trim()).filter(Boolean))];
|
|
18173
|
+
const hotFiles = [...new Set(observations.flatMap((obs) => [
|
|
18174
|
+
...parseJsonArray2(obs.files_modified),
|
|
18175
|
+
...parseJsonArray2(obs.files_read)
|
|
18176
|
+
]).filter(Boolean))].slice(0, 6);
|
|
18177
|
+
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);
|
|
18178
|
+
const captureState = prompts.length > 0 && toolEvents.length > 0 ? "rich" : prompts.length > 0 || toolEvents.length > 0 ? "partial" : "summary-only";
|
|
18089
18179
|
return {
|
|
18090
18180
|
prompt_count: prompts.length,
|
|
18091
18181
|
tool_event_count: toolEvents.length,
|
|
18182
|
+
recent_request_prompts: recentRequestPrompts,
|
|
18092
18183
|
latest_request: latestRequest,
|
|
18093
|
-
recent_tool_names: recentToolNames
|
|
18184
|
+
recent_tool_names: recentToolNames,
|
|
18185
|
+
recent_tool_commands: recentToolCommands,
|
|
18186
|
+
capture_state: captureState,
|
|
18187
|
+
hot_files: hotFiles,
|
|
18188
|
+
recent_outcomes: recentOutcomes
|
|
18094
18189
|
};
|
|
18095
18190
|
}
|
|
18191
|
+
function parseJsonArray2(value) {
|
|
18192
|
+
if (!value)
|
|
18193
|
+
return [];
|
|
18194
|
+
try {
|
|
18195
|
+
const parsed = JSON.parse(value);
|
|
18196
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
18197
|
+
} catch {
|
|
18198
|
+
return [];
|
|
18199
|
+
}
|
|
18200
|
+
}
|
|
18096
18201
|
|
|
18097
18202
|
// src/sync/pull.ts
|
|
18098
18203
|
var PULL_CURSOR_KEY = "pull_cursor";
|