engrm 0.4.9 → 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 +55 -0
- package/dist/hooks/elicitation-result.js +55 -0
- package/dist/hooks/post-tool-use.js +55 -0
- package/dist/hooks/pre-compact.js +55 -0
- package/dist/hooks/sentinel.js +55 -0
- package/dist/hooks/session-start.js +123 -7
- package/dist/hooks/stop.js +103 -7
- package/dist/hooks/user-prompt-submit.js +55 -0
- package/dist/server.js +115 -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;
|
|
@@ -689,6 +739,11 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
689
739
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
690
740
|
function openDatabase(dbPath) {
|
|
691
741
|
if (IS_BUN) {
|
|
742
|
+
if (process.platform === "darwin") {
|
|
743
|
+
try {
|
|
744
|
+
return openNodeDatabase(dbPath);
|
|
745
|
+
} catch {}
|
|
746
|
+
}
|
|
692
747
|
return openBunDatabase(dbPath);
|
|
693
748
|
}
|
|
694
749
|
return openNodeDatabase(dbPath);
|
|
@@ -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;
|
|
@@ -1515,6 +1565,11 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
1515
1565
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
1516
1566
|
function openDatabase(dbPath) {
|
|
1517
1567
|
if (IS_BUN) {
|
|
1568
|
+
if (process.platform === "darwin") {
|
|
1569
|
+
try {
|
|
1570
|
+
return openNodeDatabase(dbPath);
|
|
1571
|
+
} catch {}
|
|
1572
|
+
}
|
|
1518
1573
|
return openBunDatabase(dbPath);
|
|
1519
1574
|
}
|
|
1520
1575
|
return openNodeDatabase(dbPath);
|
|
@@ -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;
|
|
@@ -863,6 +913,11 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
863
913
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
864
914
|
function openDatabase(dbPath) {
|
|
865
915
|
if (IS_BUN) {
|
|
916
|
+
if (process.platform === "darwin") {
|
|
917
|
+
try {
|
|
918
|
+
return openNodeDatabase(dbPath);
|
|
919
|
+
} catch {}
|
|
920
|
+
}
|
|
866
921
|
return openBunDatabase(dbPath);
|
|
867
922
|
}
|
|
868
923
|
return openNodeDatabase(dbPath);
|
|
@@ -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;
|
|
@@ -657,6 +707,11 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
657
707
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
658
708
|
function openDatabase(dbPath) {
|
|
659
709
|
if (IS_BUN) {
|
|
710
|
+
if (process.platform === "darwin") {
|
|
711
|
+
try {
|
|
712
|
+
return openNodeDatabase(dbPath);
|
|
713
|
+
} catch {}
|
|
714
|
+
}
|
|
660
715
|
return openBunDatabase(dbPath);
|
|
661
716
|
}
|
|
662
717
|
return openNodeDatabase(dbPath);
|
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;
|
|
@@ -733,6 +783,11 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
733
783
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
734
784
|
function openDatabase(dbPath) {
|
|
735
785
|
if (IS_BUN) {
|
|
786
|
+
if (process.platform === "darwin") {
|
|
787
|
+
try {
|
|
788
|
+
return openNodeDatabase(dbPath);
|
|
789
|
+
} catch {}
|
|
790
|
+
}
|
|
736
791
|
return openBunDatabase(dbPath);
|
|
737
792
|
}
|
|
738
793
|
return openNodeDatabase(dbPath);
|
|
@@ -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;
|
|
@@ -2244,6 +2294,11 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
2244
2294
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
2245
2295
|
function openDatabase(dbPath) {
|
|
2246
2296
|
if (IS_BUN) {
|
|
2297
|
+
if (process.platform === "darwin") {
|
|
2298
|
+
try {
|
|
2299
|
+
return openNodeDatabase(dbPath);
|
|
2300
|
+
} catch {}
|
|
2301
|
+
}
|
|
2247
2302
|
return openBunDatabase(dbPath);
|
|
2248
2303
|
}
|
|
2249
2304
|
return openNodeDatabase(dbPath);
|
|
@@ -3009,19 +3064,28 @@ function formatVisibleStartupBrief(context) {
|
|
|
3009
3064
|
const sessionFallbacks = sessionFallbacksFromContext(context);
|
|
3010
3065
|
const recentOutcomeLines = buildRecentOutcomeLines(context, latest);
|
|
3011
3066
|
const projectSignals = buildProjectSignalLine(context);
|
|
3067
|
+
const shownItems = new Set;
|
|
3012
3068
|
if (promptLines.length > 0) {
|
|
3013
3069
|
lines.push(`${c2.cyan}Recent Requests:${c2.reset}`);
|
|
3014
3070
|
for (const item of promptLines) {
|
|
3015
3071
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3072
|
+
rememberShownItem(shownItems, item);
|
|
3016
3073
|
}
|
|
3017
3074
|
}
|
|
3018
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
|
+
});
|
|
3019
3083
|
const sections = [
|
|
3020
3084
|
["Request", currentRequest, 1],
|
|
3021
3085
|
["Investigated", chooseSection(latest.investigated, observationFallbacks.investigated, "Investigated"), 2],
|
|
3022
3086
|
["Learned", latest.learned, 2],
|
|
3023
3087
|
["Completed", chooseSection(latest.completed, observationFallbacks.completed, "Completed"), 2],
|
|
3024
|
-
["Next Steps",
|
|
3088
|
+
["Next Steps", sanitizedNextSteps, 2]
|
|
3025
3089
|
];
|
|
3026
3090
|
for (const [label, value, maxItems] of sections) {
|
|
3027
3091
|
const formatted = toSplashLines(value, maxItems ?? 2);
|
|
@@ -3029,33 +3093,45 @@ function formatVisibleStartupBrief(context) {
|
|
|
3029
3093
|
lines.push(`${c2.cyan}${label}:${c2.reset}`);
|
|
3030
3094
|
for (const item of formatted) {
|
|
3031
3095
|
lines.push(` ${item}`);
|
|
3096
|
+
rememberShownItem(shownItems, item);
|
|
3032
3097
|
}
|
|
3033
3098
|
}
|
|
3034
3099
|
}
|
|
3035
3100
|
} else if (currentRequest && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
|
|
3036
3101
|
lines.push(`${c2.cyan}Current Request:${c2.reset}`);
|
|
3037
3102
|
lines.push(` - ${truncateInline(currentRequest, 160)}`);
|
|
3103
|
+
rememberShownItem(shownItems, currentRequest);
|
|
3038
3104
|
if (toolFallbacks.length > 0) {
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
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
|
+
}
|
|
3042
3112
|
}
|
|
3043
3113
|
}
|
|
3044
3114
|
}
|
|
3045
3115
|
if (latest && currentRequest && !hasRequestSection(lines) && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
|
|
3046
3116
|
lines.push(`${c2.cyan}Current Request:${c2.reset}`);
|
|
3047
3117
|
lines.push(` - ${truncateInline(currentRequest, 160)}`);
|
|
3118
|
+
rememberShownItem(shownItems, currentRequest);
|
|
3048
3119
|
}
|
|
3049
3120
|
if (recentOutcomeLines.length > 0) {
|
|
3050
3121
|
lines.push(`${c2.cyan}Recent Work:${c2.reset}`);
|
|
3051
3122
|
for (const item of recentOutcomeLines) {
|
|
3052
3123
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3124
|
+
rememberShownItem(shownItems, item);
|
|
3053
3125
|
}
|
|
3054
3126
|
}
|
|
3055
3127
|
if (toolFallbacks.length > 0 && latest) {
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
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
|
+
}
|
|
3059
3135
|
}
|
|
3060
3136
|
}
|
|
3061
3137
|
if (sessionFallbacks.length > 0) {
|
|
@@ -3080,6 +3156,46 @@ function formatVisibleStartupBrief(context) {
|
|
|
3080
3156
|
}
|
|
3081
3157
|
return lines.slice(0, 14);
|
|
3082
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
|
+
}
|
|
3083
3199
|
function buildPromptFallback(context) {
|
|
3084
3200
|
const latest = (context.recentPrompts ?? []).find((prompt) => isMeaningfulPrompt2(prompt.prompt));
|
|
3085
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;
|
|
@@ -879,6 +942,11 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
879
942
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
880
943
|
function openDatabase(dbPath) {
|
|
881
944
|
if (IS_BUN) {
|
|
945
|
+
if (process.platform === "darwin") {
|
|
946
|
+
try {
|
|
947
|
+
return openNodeDatabase(dbPath);
|
|
948
|
+
} catch {}
|
|
949
|
+
}
|
|
882
950
|
return openBunDatabase(dbPath);
|
|
883
951
|
}
|
|
884
952
|
return openNodeDatabase(dbPath);
|
|
@@ -1857,8 +1925,13 @@ function buildSummaryVectorDocument(summary, config, project, observations = [],
|
|
|
1857
1925
|
next_step_items: extractSectionItems(summary.next_steps),
|
|
1858
1926
|
prompt_count: captureContext?.prompt_count ?? 0,
|
|
1859
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 ?? [],
|
|
1860
1930
|
latest_request: captureContext?.latest_request ?? null,
|
|
1861
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 ?? [],
|
|
1862
1935
|
decisions_count: valueSignals.decisions_count,
|
|
1863
1936
|
lessons_count: valueSignals.lessons_count,
|
|
1864
1937
|
discoveries_count: valueSignals.discoveries_count,
|
|
@@ -1902,7 +1975,7 @@ async function pushOutbox(db, client, config, batchSize = 50) {
|
|
|
1902
1975
|
const doc2 = buildSummaryVectorDocument(summary, config, {
|
|
1903
1976
|
canonical_id: project2.canonical_id,
|
|
1904
1977
|
name: project2.name
|
|
1905
|
-
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents));
|
|
1978
|
+
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents, summaryObservations));
|
|
1906
1979
|
batch.push({ entryId: entry.id, doc: doc2 });
|
|
1907
1980
|
continue;
|
|
1908
1981
|
}
|
|
@@ -1976,16 +2049,39 @@ function extractSectionItems(section) {
|
|
|
1976
2049
|
return section.split(`
|
|
1977
2050
|
`).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "").trim()).filter(Boolean).slice(0, 4);
|
|
1978
2051
|
}
|
|
1979
|
-
function buildSummaryCaptureContext(prompts, toolEvents) {
|
|
2052
|
+
function buildSummaryCaptureContext(prompts, toolEvents, observations) {
|
|
1980
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);
|
|
1981
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";
|
|
1982
2063
|
return {
|
|
1983
2064
|
prompt_count: prompts.length,
|
|
1984
2065
|
tool_event_count: toolEvents.length,
|
|
2066
|
+
recent_request_prompts: recentRequestPrompts,
|
|
1985
2067
|
latest_request: latestRequest,
|
|
1986
|
-
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
|
|
1987
2073
|
};
|
|
1988
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
|
+
}
|
|
1989
2085
|
|
|
1990
2086
|
// src/embeddings/embedder.ts
|
|
1991
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;
|
|
@@ -801,6 +851,11 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
801
851
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
802
852
|
function openDatabase(dbPath) {
|
|
803
853
|
if (IS_BUN) {
|
|
854
|
+
if (process.platform === "darwin") {
|
|
855
|
+
try {
|
|
856
|
+
return openNodeDatabase(dbPath);
|
|
857
|
+
} catch {}
|
|
858
|
+
}
|
|
804
859
|
return openBunDatabase(dbPath);
|
|
805
860
|
}
|
|
806
861
|
return openNodeDatabase(dbPath);
|
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;
|
|
@@ -14211,6 +14261,11 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
14211
14261
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
14212
14262
|
function openDatabase(dbPath) {
|
|
14213
14263
|
if (IS_BUN) {
|
|
14264
|
+
if (process.platform === "darwin") {
|
|
14265
|
+
try {
|
|
14266
|
+
return openNodeDatabase(dbPath);
|
|
14267
|
+
} catch {}
|
|
14268
|
+
}
|
|
14214
14269
|
return openBunDatabase(dbPath);
|
|
14215
14270
|
}
|
|
14216
14271
|
return openNodeDatabase(dbPath);
|
|
@@ -17331,18 +17386,44 @@ function getSessionContext(db, input) {
|
|
|
17331
17386
|
});
|
|
17332
17387
|
if (!context)
|
|
17333
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);
|
|
17334
17393
|
return {
|
|
17335
17394
|
project_name: context.project_name,
|
|
17336
17395
|
canonical_id: context.canonical_id,
|
|
17337
17396
|
session_count: context.session_count,
|
|
17338
17397
|
total_active: context.total_active,
|
|
17339
|
-
recent_requests:
|
|
17340
|
-
recent_tools:
|
|
17398
|
+
recent_requests: recentRequests,
|
|
17399
|
+
recent_tools: recentTools,
|
|
17341
17400
|
recent_sessions: context.recentSessions?.length ?? 0,
|
|
17342
|
-
|
|
17401
|
+
recent_outcomes: context.recentOutcomes ?? [],
|
|
17402
|
+
hot_files: hotFiles,
|
|
17403
|
+
capture_state: captureState,
|
|
17404
|
+
raw_capture_active: recentRequests > 0 || recentTools > 0,
|
|
17343
17405
|
preview: formatContextForInjection(context)
|
|
17344
17406
|
};
|
|
17345
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
|
+
}
|
|
17346
17427
|
|
|
17347
17428
|
// src/tools/send-message.ts
|
|
17348
17429
|
async function sendMessage(db, config2, input) {
|
|
@@ -17959,8 +18040,13 @@ function buildSummaryVectorDocument(summary, config2, project, observations = []
|
|
|
17959
18040
|
next_step_items: extractSectionItems2(summary.next_steps),
|
|
17960
18041
|
prompt_count: captureContext?.prompt_count ?? 0,
|
|
17961
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 ?? [],
|
|
17962
18045
|
latest_request: captureContext?.latest_request ?? null,
|
|
17963
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 ?? [],
|
|
17964
18050
|
decisions_count: valueSignals.decisions_count,
|
|
17965
18051
|
lessons_count: valueSignals.lessons_count,
|
|
17966
18052
|
discoveries_count: valueSignals.discoveries_count,
|
|
@@ -18004,7 +18090,7 @@ async function pushOutbox(db, client, config2, batchSize = 50) {
|
|
|
18004
18090
|
const doc3 = buildSummaryVectorDocument(summary, config2, {
|
|
18005
18091
|
canonical_id: project2.canonical_id,
|
|
18006
18092
|
name: project2.name
|
|
18007
|
-
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents));
|
|
18093
|
+
}, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents, summaryObservations));
|
|
18008
18094
|
batch.push({ entryId: entry.id, doc: doc3 });
|
|
18009
18095
|
continue;
|
|
18010
18096
|
}
|
|
@@ -18078,16 +18164,39 @@ function extractSectionItems2(section) {
|
|
|
18078
18164
|
return section.split(`
|
|
18079
18165
|
`).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "").trim()).filter(Boolean).slice(0, 4);
|
|
18080
18166
|
}
|
|
18081
|
-
function buildSummaryCaptureContext(prompts, toolEvents) {
|
|
18167
|
+
function buildSummaryCaptureContext(prompts, toolEvents, observations) {
|
|
18082
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);
|
|
18083
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";
|
|
18084
18178
|
return {
|
|
18085
18179
|
prompt_count: prompts.length,
|
|
18086
18180
|
tool_event_count: toolEvents.length,
|
|
18181
|
+
recent_request_prompts: recentRequestPrompts,
|
|
18087
18182
|
latest_request: latestRequest,
|
|
18088
|
-
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
|
|
18089
18188
|
};
|
|
18090
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
|
+
}
|
|
18091
18200
|
|
|
18092
18201
|
// src/sync/pull.ts
|
|
18093
18202
|
var PULL_CURSOR_KEY = "pull_cursor";
|