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 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;
@@ -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", latest.next_steps, 2]
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
- lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
3045
- for (const item of toolFallbacks) {
3046
- lines.push(` - ${truncateInline(item, 160)}`);
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
- lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
3062
- for (const item of toolFallbacks) {
3063
- lines.push(` - ${truncateInline(item, 160)}`);
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)
@@ -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 unresolved = lastQuarter.filter((o) => o.type === "bugfix" && o.narrative && /error|fail|exception/i.test(o.narrative));
110
- const explicitDecisions = lastQuarter.filter((o) => o.type === "decision").sort((a, b) => scoreNarrativeObservation(b) - scoreNarrativeObservation(a)).slice(0, 2).map((o) => `- Follow through: ${o.title}`);
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: context.recentPrompts?.length ?? 0,
17345
- recent_tools: context.recentToolEvents?.length ?? 0,
17399
+ recent_requests: recentRequests,
17400
+ recent_tools: recentTools,
17346
17401
  recent_sessions: context.recentSessions?.length ?? 0,
17347
- raw_capture_active: (context.recentPrompts?.length ?? 0) > 0 || (context.recentToolEvents?.length ?? 0) > 0,
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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "engrm",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
4
4
  "description": "Shared memory across devices, sessions, and coding agents",
5
5
  "mcpName": "io.github.dr12hes/engrm",
6
6
  "type": "module",