@massu/core 1.10.1 → 1.10.3

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
@@ -796,7 +796,7 @@ function initMemorySchema(db) {
796
796
  response_tokens INTEGER,
797
797
  created_at TEXT DEFAULT (datetime('now')),
798
798
  created_at_epoch INTEGER DEFAULT (unixepoch()),
799
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
799
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
800
800
  );
801
801
 
802
802
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -817,7 +817,7 @@ function initMemorySchema(db) {
817
817
  files_involved TEXT,
818
818
  created_at TEXT DEFAULT (datetime('now')),
819
819
  created_at_epoch INTEGER DEFAULT (unixepoch()),
820
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
820
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
821
821
  );
822
822
 
823
823
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -871,7 +871,7 @@ function initMemorySchema(db) {
871
871
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
872
872
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
873
873
  created_at TEXT DEFAULT (datetime('now')),
874
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
874
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
875
875
  );
876
876
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
877
877
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -891,7 +891,7 @@ function initMemorySchema(db) {
891
891
  duration_minutes REAL NOT NULL DEFAULT 0.0,
892
892
  tool_calls INTEGER NOT NULL DEFAULT 0,
893
893
  created_at TEXT DEFAULT (datetime('now')),
894
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
894
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
895
895
  );
896
896
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
897
897
  `);
@@ -904,7 +904,7 @@ function initMemorySchema(db) {
904
904
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
905
905
  commit_hash TEXT,
906
906
  created_at TEXT DEFAULT (datetime('now')),
907
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
907
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
908
908
  );
909
909
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
910
910
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -921,7 +921,7 @@ function initMemorySchema(db) {
921
921
  corrections_needed INTEGER NOT NULL DEFAULT 0,
922
922
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
923
923
  created_at TEXT DEFAULT (datetime('now')),
924
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
924
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
925
925
  );
926
926
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
927
927
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -940,7 +940,7 @@ function initMemorySchema(db) {
940
940
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
941
941
  evidence TEXT,
942
942
  metadata TEXT,
943
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
943
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
944
944
  );
945
945
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
946
946
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -957,7 +957,7 @@ function initMemorySchema(db) {
957
957
  details TEXT,
958
958
  rules_violated TEXT,
959
959
  created_at TEXT DEFAULT (datetime('now')),
960
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
960
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
961
961
  );
962
962
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
963
963
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -975,7 +975,7 @@ function initMemorySchema(db) {
975
975
  affected_files TEXT,
976
976
  commit_hash TEXT,
977
977
  created_at TEXT DEFAULT (datetime('now')),
978
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
978
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
979
979
  );
980
980
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
981
981
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -988,7 +988,7 @@ function initMemorySchema(db) {
988
988
  risk_score INTEGER NOT NULL DEFAULT 0,
989
989
  findings TEXT,
990
990
  created_at TEXT DEFAULT (datetime('now')),
991
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
991
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
992
992
  );
993
993
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
994
994
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -1141,11 +1141,29 @@ function enqueueSyncPayload(db, payload) {
1141
1141
  }
1142
1142
  function dequeuePendingSync(db, limit = 10) {
1143
1143
  const stale = db.prepare(
1144
- "SELECT id FROM pending_sync WHERE retry_count >= 10"
1144
+ "SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10"
1145
1145
  ).all();
1146
1146
  if (stale.length > 0) {
1147
1147
  const ids = stale.map((s) => s.id);
1148
1148
  db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
1149
+ const lastErrors = [...new Set(stale.map((s) => s.last_error).filter(Boolean))];
1150
+ process.stderr.write(
1151
+ `[massu] WARNING: ${stale.length} cloud-sync queue item(s) discarded after 10+ retries. Likely cause: invalid API key or unreachable endpoint. Recent errors: ${lastErrors.slice(0, 3).join("; ") || "(none recorded)"}
1152
+ `
1153
+ );
1154
+ try {
1155
+ db.prepare(`
1156
+ INSERT INTO analytics_events (event_type, event_data, created_at)
1157
+ VALUES (?, ?, datetime('now'))
1158
+ `).run(
1159
+ "cloud_sync_giveup",
1160
+ JSON.stringify({
1161
+ discarded_count: stale.length,
1162
+ recent_errors: lastErrors.slice(0, 3)
1163
+ })
1164
+ );
1165
+ } catch {
1166
+ }
1149
1167
  }
1150
1168
  return db.prepare(
1151
1169
  "SELECT id, payload, retry_count FROM pending_sync ORDER BY created_at ASC LIMIT ?"
@@ -25525,8 +25543,9 @@ function indexAllKnowledge(db) {
25525
25543
  "INSERT OR IGNORE INTO knowledge_incidents (incident_num, date, type, gap_found, prevention) VALUES (?, ?, ?, ?, ?)"
25526
25544
  );
25527
25545
  const insertMismatch = db.prepare(
25528
- "INSERT INTO knowledge_schema_mismatches (table_name, wrong_column, correct_column) VALUES (?, ?, ?)"
25546
+ "INSERT INTO knowledge_schema_mismatches (table_name, wrong_column, correct_column, source) VALUES (?, ?, ?, ?)"
25529
25547
  );
25548
+ const defaultSource = getConfig().conventions?.knowledgeSourceFiles?.[0] ?? "CLAUDE.md";
25530
25549
  const files = discoverMarkdownFiles(paths.claudeDir);
25531
25550
  try {
25532
25551
  const memFiles = discoverMarkdownFiles(paths.memoryDir);
@@ -25614,7 +25633,7 @@ function indexAllKnowledge(db) {
25614
25633
  }
25615
25634
  const mismatches = parseSchemaMismatches(content);
25616
25635
  for (const m3 of mismatches) {
25617
- insertMismatch.run(m3.table_name, m3.wrong_column, m3.correct_column);
25636
+ insertMismatch.run(m3.table_name, m3.wrong_column, m3.correct_column, defaultSource);
25618
25637
  insertChunk.run(docId, "mismatch", m3.table_name, `${m3.table_name}: ${m3.wrong_column} -> ${m3.correct_column}`, null, null, JSON.stringify({ table: m3.table_name }));
25619
25638
  stats.chunksCreated++;
25620
25639
  }
@@ -26916,12 +26935,19 @@ function initKnowledgeSchema(db) {
26916
26935
  CREATE INDEX IF NOT EXISTS idx_ki_cr ON knowledge_incidents(cr_added);
26917
26936
 
26918
26937
  -- Schema mismatch quick lookup
26938
+ -- P-H013 (plan-stage-c-high-batch): the source column no longer has a
26939
+ -- SQL DEFAULT. Previously the value was a JS template-literal
26940
+ -- interpolation baked into the customer SQLite at schema creation
26941
+ -- time, so later config changes were ignored. Default is now applied
26942
+ -- at INSERT time via getConfig().conventions.knowledgeSourceFiles[0]
26943
+ -- in knowledge-indexer.ts -- a runtime config lookup that reflects
26944
+ -- the customers current config.
26919
26945
  CREATE TABLE IF NOT EXISTS knowledge_schema_mismatches (
26920
26946
  id INTEGER PRIMARY KEY AUTOINCREMENT,
26921
26947
  table_name TEXT NOT NULL,
26922
26948
  wrong_column TEXT NOT NULL,
26923
26949
  correct_column TEXT NOT NULL,
26924
- source TEXT DEFAULT '${getConfig().conventions?.knowledgeSourceFiles?.[0] ?? "CLAUDE.md"}'
26950
+ source TEXT NOT NULL
26925
26951
  );
26926
26952
 
26927
26953
  CREATE INDEX IF NOT EXISTS idx_ksm_table ON knowledge_schema_mismatches(table_name);
@@ -701,7 +701,7 @@ function initMemorySchema(db) {
701
701
  response_tokens INTEGER,
702
702
  created_at TEXT DEFAULT (datetime('now')),
703
703
  created_at_epoch INTEGER DEFAULT (unixepoch()),
704
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
704
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
705
705
  );
706
706
 
707
707
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -722,7 +722,7 @@ function initMemorySchema(db) {
722
722
  files_involved TEXT,
723
723
  created_at TEXT DEFAULT (datetime('now')),
724
724
  created_at_epoch INTEGER DEFAULT (unixepoch()),
725
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
725
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
726
726
  );
727
727
 
728
728
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -776,7 +776,7 @@ function initMemorySchema(db) {
776
776
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
777
777
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
778
778
  created_at TEXT DEFAULT (datetime('now')),
779
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
779
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
780
780
  );
781
781
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
782
782
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -796,7 +796,7 @@ function initMemorySchema(db) {
796
796
  duration_minutes REAL NOT NULL DEFAULT 0.0,
797
797
  tool_calls INTEGER NOT NULL DEFAULT 0,
798
798
  created_at TEXT DEFAULT (datetime('now')),
799
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
799
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
800
800
  );
801
801
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
802
802
  `);
@@ -809,7 +809,7 @@ function initMemorySchema(db) {
809
809
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
810
810
  commit_hash TEXT,
811
811
  created_at TEXT DEFAULT (datetime('now')),
812
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
812
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
813
813
  );
814
814
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
815
815
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -826,7 +826,7 @@ function initMemorySchema(db) {
826
826
  corrections_needed INTEGER NOT NULL DEFAULT 0,
827
827
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
828
828
  created_at TEXT DEFAULT (datetime('now')),
829
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
829
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
830
830
  );
831
831
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
832
832
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -845,7 +845,7 @@ function initMemorySchema(db) {
845
845
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
846
846
  evidence TEXT,
847
847
  metadata TEXT,
848
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
848
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
849
849
  );
850
850
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
851
851
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -862,7 +862,7 @@ function initMemorySchema(db) {
862
862
  details TEXT,
863
863
  rules_violated TEXT,
864
864
  created_at TEXT DEFAULT (datetime('now')),
865
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
865
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
866
866
  );
867
867
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
868
868
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -880,7 +880,7 @@ function initMemorySchema(db) {
880
880
  affected_files TEXT,
881
881
  commit_hash TEXT,
882
882
  created_at TEXT DEFAULT (datetime('now')),
883
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
883
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
884
884
  );
885
885
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
886
886
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -893,7 +893,7 @@ function initMemorySchema(db) {
893
893
  risk_score INTEGER NOT NULL DEFAULT 0,
894
894
  findings TEXT,
895
895
  created_at TEXT DEFAULT (datetime('now')),
896
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
896
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
897
897
  );
898
898
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
899
899
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -698,7 +698,7 @@ function initMemorySchema(db) {
698
698
  response_tokens INTEGER,
699
699
  created_at TEXT DEFAULT (datetime('now')),
700
700
  created_at_epoch INTEGER DEFAULT (unixepoch()),
701
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
701
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
702
702
  );
703
703
 
704
704
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -719,7 +719,7 @@ function initMemorySchema(db) {
719
719
  files_involved TEXT,
720
720
  created_at TEXT DEFAULT (datetime('now')),
721
721
  created_at_epoch INTEGER DEFAULT (unixepoch()),
722
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
722
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
723
723
  );
724
724
 
725
725
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -773,7 +773,7 @@ function initMemorySchema(db) {
773
773
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
774
774
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
775
775
  created_at TEXT DEFAULT (datetime('now')),
776
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
776
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
777
777
  );
778
778
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
779
779
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -793,7 +793,7 @@ function initMemorySchema(db) {
793
793
  duration_minutes REAL NOT NULL DEFAULT 0.0,
794
794
  tool_calls INTEGER NOT NULL DEFAULT 0,
795
795
  created_at TEXT DEFAULT (datetime('now')),
796
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
796
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
797
797
  );
798
798
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
799
799
  `);
@@ -806,7 +806,7 @@ function initMemorySchema(db) {
806
806
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
807
807
  commit_hash TEXT,
808
808
  created_at TEXT DEFAULT (datetime('now')),
809
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
809
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
810
810
  );
811
811
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
812
812
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -823,7 +823,7 @@ function initMemorySchema(db) {
823
823
  corrections_needed INTEGER NOT NULL DEFAULT 0,
824
824
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
825
825
  created_at TEXT DEFAULT (datetime('now')),
826
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
826
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
827
827
  );
828
828
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
829
829
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -842,7 +842,7 @@ function initMemorySchema(db) {
842
842
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
843
843
  evidence TEXT,
844
844
  metadata TEXT,
845
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
845
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
846
846
  );
847
847
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
848
848
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -859,7 +859,7 @@ function initMemorySchema(db) {
859
859
  details TEXT,
860
860
  rules_violated TEXT,
861
861
  created_at TEXT DEFAULT (datetime('now')),
862
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
862
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
863
863
  );
864
864
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
865
865
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -877,7 +877,7 @@ function initMemorySchema(db) {
877
877
  affected_files TEXT,
878
878
  commit_hash TEXT,
879
879
  created_at TEXT DEFAULT (datetime('now')),
880
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
880
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
881
881
  );
882
882
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
883
883
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -890,7 +890,7 @@ function initMemorySchema(db) {
890
890
  risk_score INTEGER NOT NULL DEFAULT 0,
891
891
  findings TEXT,
892
892
  created_at TEXT DEFAULT (datetime('now')),
893
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
893
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
894
894
  );
895
895
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
896
896
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -700,7 +700,7 @@ function initMemorySchema(db) {
700
700
  response_tokens INTEGER,
701
701
  created_at TEXT DEFAULT (datetime('now')),
702
702
  created_at_epoch INTEGER DEFAULT (unixepoch()),
703
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
703
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
704
704
  );
705
705
 
706
706
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -721,7 +721,7 @@ function initMemorySchema(db) {
721
721
  files_involved TEXT,
722
722
  created_at TEXT DEFAULT (datetime('now')),
723
723
  created_at_epoch INTEGER DEFAULT (unixepoch()),
724
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
724
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
725
725
  );
726
726
 
727
727
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -775,7 +775,7 @@ function initMemorySchema(db) {
775
775
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
776
776
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
777
777
  created_at TEXT DEFAULT (datetime('now')),
778
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
778
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
779
779
  );
780
780
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
781
781
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -795,7 +795,7 @@ function initMemorySchema(db) {
795
795
  duration_minutes REAL NOT NULL DEFAULT 0.0,
796
796
  tool_calls INTEGER NOT NULL DEFAULT 0,
797
797
  created_at TEXT DEFAULT (datetime('now')),
798
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
798
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
799
799
  );
800
800
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
801
801
  `);
@@ -808,7 +808,7 @@ function initMemorySchema(db) {
808
808
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
809
809
  commit_hash TEXT,
810
810
  created_at TEXT DEFAULT (datetime('now')),
811
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
811
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
812
812
  );
813
813
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
814
814
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -825,7 +825,7 @@ function initMemorySchema(db) {
825
825
  corrections_needed INTEGER NOT NULL DEFAULT 0,
826
826
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
827
827
  created_at TEXT DEFAULT (datetime('now')),
828
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
828
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
829
829
  );
830
830
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
831
831
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -844,7 +844,7 @@ function initMemorySchema(db) {
844
844
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
845
845
  evidence TEXT,
846
846
  metadata TEXT,
847
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
847
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
848
848
  );
849
849
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
850
850
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -861,7 +861,7 @@ function initMemorySchema(db) {
861
861
  details TEXT,
862
862
  rules_violated TEXT,
863
863
  created_at TEXT DEFAULT (datetime('now')),
864
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
864
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
865
865
  );
866
866
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
867
867
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -879,7 +879,7 @@ function initMemorySchema(db) {
879
879
  affected_files TEXT,
880
880
  commit_hash TEXT,
881
881
  created_at TEXT DEFAULT (datetime('now')),
882
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
882
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
883
883
  );
884
884
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
885
885
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -892,7 +892,7 @@ function initMemorySchema(db) {
892
892
  risk_score INTEGER NOT NULL DEFAULT 0,
893
893
  findings TEXT,
894
894
  created_at TEXT DEFAULT (datetime('now')),
895
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
895
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
896
896
  );
897
897
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
898
898
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -698,7 +698,7 @@ function initMemorySchema(db) {
698
698
  response_tokens INTEGER,
699
699
  created_at TEXT DEFAULT (datetime('now')),
700
700
  created_at_epoch INTEGER DEFAULT (unixepoch()),
701
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
701
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
702
702
  );
703
703
 
704
704
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -719,7 +719,7 @@ function initMemorySchema(db) {
719
719
  files_involved TEXT,
720
720
  created_at TEXT DEFAULT (datetime('now')),
721
721
  created_at_epoch INTEGER DEFAULT (unixepoch()),
722
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
722
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
723
723
  );
724
724
 
725
725
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -773,7 +773,7 @@ function initMemorySchema(db) {
773
773
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
774
774
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
775
775
  created_at TEXT DEFAULT (datetime('now')),
776
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
776
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
777
777
  );
778
778
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
779
779
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -793,7 +793,7 @@ function initMemorySchema(db) {
793
793
  duration_minutes REAL NOT NULL DEFAULT 0.0,
794
794
  tool_calls INTEGER NOT NULL DEFAULT 0,
795
795
  created_at TEXT DEFAULT (datetime('now')),
796
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
796
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
797
797
  );
798
798
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
799
799
  `);
@@ -806,7 +806,7 @@ function initMemorySchema(db) {
806
806
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
807
807
  commit_hash TEXT,
808
808
  created_at TEXT DEFAULT (datetime('now')),
809
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
809
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
810
810
  );
811
811
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
812
812
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -823,7 +823,7 @@ function initMemorySchema(db) {
823
823
  corrections_needed INTEGER NOT NULL DEFAULT 0,
824
824
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
825
825
  created_at TEXT DEFAULT (datetime('now')),
826
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
826
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
827
827
  );
828
828
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
829
829
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -842,7 +842,7 @@ function initMemorySchema(db) {
842
842
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
843
843
  evidence TEXT,
844
844
  metadata TEXT,
845
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
845
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
846
846
  );
847
847
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
848
848
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -859,7 +859,7 @@ function initMemorySchema(db) {
859
859
  details TEXT,
860
860
  rules_violated TEXT,
861
861
  created_at TEXT DEFAULT (datetime('now')),
862
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
862
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
863
863
  );
864
864
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
865
865
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -877,7 +877,7 @@ function initMemorySchema(db) {
877
877
  affected_files TEXT,
878
878
  commit_hash TEXT,
879
879
  created_at TEXT DEFAULT (datetime('now')),
880
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
880
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
881
881
  );
882
882
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
883
883
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -890,7 +890,7 @@ function initMemorySchema(db) {
890
890
  risk_score INTEGER NOT NULL DEFAULT 0,
891
891
  findings TEXT,
892
892
  created_at TEXT DEFAULT (datetime('now')),
893
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
893
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
894
894
  );
895
895
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
896
896
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -698,7 +698,7 @@ function initMemorySchema(db) {
698
698
  response_tokens INTEGER,
699
699
  created_at TEXT DEFAULT (datetime('now')),
700
700
  created_at_epoch INTEGER DEFAULT (unixepoch()),
701
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
701
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
702
702
  );
703
703
 
704
704
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -719,7 +719,7 @@ function initMemorySchema(db) {
719
719
  files_involved TEXT,
720
720
  created_at TEXT DEFAULT (datetime('now')),
721
721
  created_at_epoch INTEGER DEFAULT (unixepoch()),
722
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
722
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
723
723
  );
724
724
 
725
725
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -773,7 +773,7 @@ function initMemorySchema(db) {
773
773
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
774
774
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
775
775
  created_at TEXT DEFAULT (datetime('now')),
776
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
776
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
777
777
  );
778
778
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
779
779
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -793,7 +793,7 @@ function initMemorySchema(db) {
793
793
  duration_minutes REAL NOT NULL DEFAULT 0.0,
794
794
  tool_calls INTEGER NOT NULL DEFAULT 0,
795
795
  created_at TEXT DEFAULT (datetime('now')),
796
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
796
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
797
797
  );
798
798
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
799
799
  `);
@@ -806,7 +806,7 @@ function initMemorySchema(db) {
806
806
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
807
807
  commit_hash TEXT,
808
808
  created_at TEXT DEFAULT (datetime('now')),
809
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
809
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
810
810
  );
811
811
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
812
812
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -823,7 +823,7 @@ function initMemorySchema(db) {
823
823
  corrections_needed INTEGER NOT NULL DEFAULT 0,
824
824
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
825
825
  created_at TEXT DEFAULT (datetime('now')),
826
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
826
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
827
827
  );
828
828
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
829
829
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -842,7 +842,7 @@ function initMemorySchema(db) {
842
842
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
843
843
  evidence TEXT,
844
844
  metadata TEXT,
845
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
845
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
846
846
  );
847
847
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
848
848
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -859,7 +859,7 @@ function initMemorySchema(db) {
859
859
  details TEXT,
860
860
  rules_violated TEXT,
861
861
  created_at TEXT DEFAULT (datetime('now')),
862
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
862
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
863
863
  );
864
864
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
865
865
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -877,7 +877,7 @@ function initMemorySchema(db) {
877
877
  affected_files TEXT,
878
878
  commit_hash TEXT,
879
879
  created_at TEXT DEFAULT (datetime('now')),
880
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
880
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
881
881
  );
882
882
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
883
883
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -890,7 +890,7 @@ function initMemorySchema(db) {
890
890
  risk_score INTEGER NOT NULL DEFAULT 0,
891
891
  findings TEXT,
892
892
  created_at TEXT DEFAULT (datetime('now')),
893
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
893
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
894
894
  );
895
895
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
896
896
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -698,7 +698,7 @@ function initMemorySchema(db) {
698
698
  response_tokens INTEGER,
699
699
  created_at TEXT DEFAULT (datetime('now')),
700
700
  created_at_epoch INTEGER DEFAULT (unixepoch()),
701
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
701
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
702
702
  );
703
703
 
704
704
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -719,7 +719,7 @@ function initMemorySchema(db) {
719
719
  files_involved TEXT,
720
720
  created_at TEXT DEFAULT (datetime('now')),
721
721
  created_at_epoch INTEGER DEFAULT (unixepoch()),
722
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
722
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
723
723
  );
724
724
 
725
725
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -773,7 +773,7 @@ function initMemorySchema(db) {
773
773
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
774
774
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
775
775
  created_at TEXT DEFAULT (datetime('now')),
776
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
776
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
777
777
  );
778
778
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
779
779
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -793,7 +793,7 @@ function initMemorySchema(db) {
793
793
  duration_minutes REAL NOT NULL DEFAULT 0.0,
794
794
  tool_calls INTEGER NOT NULL DEFAULT 0,
795
795
  created_at TEXT DEFAULT (datetime('now')),
796
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
796
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
797
797
  );
798
798
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
799
799
  `);
@@ -806,7 +806,7 @@ function initMemorySchema(db) {
806
806
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
807
807
  commit_hash TEXT,
808
808
  created_at TEXT DEFAULT (datetime('now')),
809
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
809
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
810
810
  );
811
811
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
812
812
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -823,7 +823,7 @@ function initMemorySchema(db) {
823
823
  corrections_needed INTEGER NOT NULL DEFAULT 0,
824
824
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
825
825
  created_at TEXT DEFAULT (datetime('now')),
826
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
826
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
827
827
  );
828
828
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
829
829
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -842,7 +842,7 @@ function initMemorySchema(db) {
842
842
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
843
843
  evidence TEXT,
844
844
  metadata TEXT,
845
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
845
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
846
846
  );
847
847
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
848
848
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -859,7 +859,7 @@ function initMemorySchema(db) {
859
859
  details TEXT,
860
860
  rules_violated TEXT,
861
861
  created_at TEXT DEFAULT (datetime('now')),
862
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
862
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
863
863
  );
864
864
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
865
865
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -877,7 +877,7 @@ function initMemorySchema(db) {
877
877
  affected_files TEXT,
878
878
  commit_hash TEXT,
879
879
  created_at TEXT DEFAULT (datetime('now')),
880
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
880
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
881
881
  );
882
882
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
883
883
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -890,7 +890,7 @@ function initMemorySchema(db) {
890
890
  risk_score INTEGER NOT NULL DEFAULT 0,
891
891
  findings TEXT,
892
892
  created_at TEXT DEFAULT (datetime('now')),
893
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
893
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
894
894
  );
895
895
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
896
896
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -698,7 +698,7 @@ function initMemorySchema(db) {
698
698
  response_tokens INTEGER,
699
699
  created_at TEXT DEFAULT (datetime('now')),
700
700
  created_at_epoch INTEGER DEFAULT (unixepoch()),
701
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
701
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
702
702
  );
703
703
 
704
704
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -719,7 +719,7 @@ function initMemorySchema(db) {
719
719
  files_involved TEXT,
720
720
  created_at TEXT DEFAULT (datetime('now')),
721
721
  created_at_epoch INTEGER DEFAULT (unixepoch()),
722
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
722
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
723
723
  );
724
724
 
725
725
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -773,7 +773,7 @@ function initMemorySchema(db) {
773
773
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
774
774
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
775
775
  created_at TEXT DEFAULT (datetime('now')),
776
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
776
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
777
777
  );
778
778
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
779
779
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -793,7 +793,7 @@ function initMemorySchema(db) {
793
793
  duration_minutes REAL NOT NULL DEFAULT 0.0,
794
794
  tool_calls INTEGER NOT NULL DEFAULT 0,
795
795
  created_at TEXT DEFAULT (datetime('now')),
796
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
796
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
797
797
  );
798
798
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
799
799
  `);
@@ -806,7 +806,7 @@ function initMemorySchema(db) {
806
806
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
807
807
  commit_hash TEXT,
808
808
  created_at TEXT DEFAULT (datetime('now')),
809
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
809
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
810
810
  );
811
811
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
812
812
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -823,7 +823,7 @@ function initMemorySchema(db) {
823
823
  corrections_needed INTEGER NOT NULL DEFAULT 0,
824
824
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
825
825
  created_at TEXT DEFAULT (datetime('now')),
826
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
826
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
827
827
  );
828
828
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
829
829
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -842,7 +842,7 @@ function initMemorySchema(db) {
842
842
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
843
843
  evidence TEXT,
844
844
  metadata TEXT,
845
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
845
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
846
846
  );
847
847
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
848
848
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -859,7 +859,7 @@ function initMemorySchema(db) {
859
859
  details TEXT,
860
860
  rules_violated TEXT,
861
861
  created_at TEXT DEFAULT (datetime('now')),
862
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
862
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
863
863
  );
864
864
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
865
865
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -877,7 +877,7 @@ function initMemorySchema(db) {
877
877
  affected_files TEXT,
878
878
  commit_hash TEXT,
879
879
  created_at TEXT DEFAULT (datetime('now')),
880
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
880
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
881
881
  );
882
882
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
883
883
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -890,7 +890,7 @@ function initMemorySchema(db) {
890
890
  risk_score INTEGER NOT NULL DEFAULT 0,
891
891
  findings TEXT,
892
892
  created_at TEXT DEFAULT (datetime('now')),
893
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
893
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
894
894
  );
895
895
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
896
896
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -1043,11 +1043,29 @@ function enqueueSyncPayload(db, payload) {
1043
1043
  }
1044
1044
  function dequeuePendingSync(db, limit = 10) {
1045
1045
  const stale = db.prepare(
1046
- "SELECT id FROM pending_sync WHERE retry_count >= 10"
1046
+ "SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10"
1047
1047
  ).all();
1048
1048
  if (stale.length > 0) {
1049
1049
  const ids = stale.map((s) => s.id);
1050
1050
  db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
1051
+ const lastErrors = [...new Set(stale.map((s) => s.last_error).filter(Boolean))];
1052
+ process.stderr.write(
1053
+ `[massu] WARNING: ${stale.length} cloud-sync queue item(s) discarded after 10+ retries. Likely cause: invalid API key or unreachable endpoint. Recent errors: ${lastErrors.slice(0, 3).join("; ") || "(none recorded)"}
1054
+ `
1055
+ );
1056
+ try {
1057
+ db.prepare(`
1058
+ INSERT INTO analytics_events (event_type, event_data, created_at)
1059
+ VALUES (?, ?, datetime('now'))
1060
+ `).run(
1061
+ "cloud_sync_giveup",
1062
+ JSON.stringify({
1063
+ discarded_count: stale.length,
1064
+ recent_errors: lastErrors.slice(0, 3)
1065
+ })
1066
+ );
1067
+ } catch {
1068
+ }
1051
1069
  }
1052
1070
  return db.prepare(
1053
1071
  "SELECT id, payload, retry_count FROM pending_sync ORDER BY created_at ASC LIMIT ?"
@@ -1415,6 +1433,33 @@ function estimateTokens(text) {
1415
1433
  return Math.ceil(text.length / 4);
1416
1434
  }
1417
1435
 
1436
+ // src/observation-extractor.ts
1437
+ var PRIVATE_PATTERNS = [
1438
+ /\/Users\/\w+/,
1439
+ // Absolute macOS paths
1440
+ /\/home\/\w+/,
1441
+ // Absolute Linux paths
1442
+ /[A-Z]:\\/,
1443
+ // Windows paths
1444
+ /\b(api[_-]?key|secret|token|password|credential|dsn)\b/i,
1445
+ // Secrets
1446
+ /\b(STRIPE_|SUPABASE_|SENTRY_|AWS_|DATABASE_URL)\b/,
1447
+ // Env var names
1448
+ /\.(env|pem|key|cert)\b/,
1449
+ // Sensitive file extensions
1450
+ /Bearer\s+\S+/,
1451
+ // Auth tokens
1452
+ /sk_live_|sk_test_|whsec_/
1453
+ // Stripe keys
1454
+ ];
1455
+ function classifyVisibility(title, detail) {
1456
+ const text = `${title} ${detail ?? ""}`;
1457
+ for (const pattern of PRIVATE_PATTERNS) {
1458
+ if (pattern.test(text)) return "private";
1459
+ }
1460
+ return "public";
1461
+ }
1462
+
1418
1463
  // src/cloud-sync.ts
1419
1464
  var MAX_RETRIES = 3;
1420
1465
  var RETRY_DELAYS = [1e3, 2e3, 4e3];
@@ -1435,7 +1480,26 @@ async function syncToCloud(db, payload) {
1435
1480
  const filteredPayload = {};
1436
1481
  if (cloud.sync?.memory !== false) {
1437
1482
  filteredPayload.sessions = payload.sessions;
1438
- filteredPayload.observations = payload.observations;
1483
+ if (payload.observations) {
1484
+ let droppedPrivate = 0;
1485
+ filteredPayload.observations = payload.observations.filter((obs) => {
1486
+ if (classifyVisibility(obs.content ?? "", obs.content ?? "") === "private") {
1487
+ droppedPrivate += 1;
1488
+ return false;
1489
+ }
1490
+ if (obs.file_path && classifyVisibility(obs.file_path, obs.file_path) === "private") {
1491
+ droppedPrivate += 1;
1492
+ return false;
1493
+ }
1494
+ return true;
1495
+ });
1496
+ if (droppedPrivate > 0) {
1497
+ process.stderr.write(
1498
+ `[massu] cloud-sync: dropped ${droppedPrivate} private observation(s) (PRIVATE_PATTERNS match)
1499
+ `
1500
+ );
1501
+ }
1502
+ }
1439
1503
  }
1440
1504
  if (cloud.sync?.analytics !== false) {
1441
1505
  filteredPayload.analytics = payload.analytics;
@@ -6496,7 +6496,7 @@ function initMemorySchema(db) {
6496
6496
  response_tokens INTEGER,
6497
6497
  created_at TEXT DEFAULT (datetime('now')),
6498
6498
  created_at_epoch INTEGER DEFAULT (unixepoch()),
6499
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6499
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6500
6500
  );
6501
6501
 
6502
6502
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -6517,7 +6517,7 @@ function initMemorySchema(db) {
6517
6517
  files_involved TEXT,
6518
6518
  created_at TEXT DEFAULT (datetime('now')),
6519
6519
  created_at_epoch INTEGER DEFAULT (unixepoch()),
6520
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6520
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6521
6521
  );
6522
6522
 
6523
6523
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -6571,7 +6571,7 @@ function initMemorySchema(db) {
6571
6571
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
6572
6572
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
6573
6573
  created_at TEXT DEFAULT (datetime('now')),
6574
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6574
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6575
6575
  );
6576
6576
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
6577
6577
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -6591,7 +6591,7 @@ function initMemorySchema(db) {
6591
6591
  duration_minutes REAL NOT NULL DEFAULT 0.0,
6592
6592
  tool_calls INTEGER NOT NULL DEFAULT 0,
6593
6593
  created_at TEXT DEFAULT (datetime('now')),
6594
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6594
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6595
6595
  );
6596
6596
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
6597
6597
  `);
@@ -6604,7 +6604,7 @@ function initMemorySchema(db) {
6604
6604
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
6605
6605
  commit_hash TEXT,
6606
6606
  created_at TEXT DEFAULT (datetime('now')),
6607
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6607
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6608
6608
  );
6609
6609
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
6610
6610
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -6621,7 +6621,7 @@ function initMemorySchema(db) {
6621
6621
  corrections_needed INTEGER NOT NULL DEFAULT 0,
6622
6622
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
6623
6623
  created_at TEXT DEFAULT (datetime('now')),
6624
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6624
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6625
6625
  );
6626
6626
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
6627
6627
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -6640,7 +6640,7 @@ function initMemorySchema(db) {
6640
6640
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
6641
6641
  evidence TEXT,
6642
6642
  metadata TEXT,
6643
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6643
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6644
6644
  );
6645
6645
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
6646
6646
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -6657,7 +6657,7 @@ function initMemorySchema(db) {
6657
6657
  details TEXT,
6658
6658
  rules_violated TEXT,
6659
6659
  created_at TEXT DEFAULT (datetime('now')),
6660
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6660
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6661
6661
  );
6662
6662
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
6663
6663
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -6675,7 +6675,7 @@ function initMemorySchema(db) {
6675
6675
  affected_files TEXT,
6676
6676
  commit_hash TEXT,
6677
6677
  created_at TEXT DEFAULT (datetime('now')),
6678
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6678
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6679
6679
  );
6680
6680
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
6681
6681
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -6688,7 +6688,7 @@ function initMemorySchema(db) {
6688
6688
  risk_score INTEGER NOT NULL DEFAULT 0,
6689
6689
  findings TEXT,
6690
6690
  created_at TEXT DEFAULT (datetime('now')),
6691
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
6691
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
6692
6692
  );
6693
6693
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
6694
6694
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -698,7 +698,7 @@ function initMemorySchema(db) {
698
698
  response_tokens INTEGER,
699
699
  created_at TEXT DEFAULT (datetime('now')),
700
700
  created_at_epoch INTEGER DEFAULT (unixepoch()),
701
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
701
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
702
702
  );
703
703
 
704
704
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -719,7 +719,7 @@ function initMemorySchema(db) {
719
719
  files_involved TEXT,
720
720
  created_at TEXT DEFAULT (datetime('now')),
721
721
  created_at_epoch INTEGER DEFAULT (unixepoch()),
722
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
722
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
723
723
  );
724
724
 
725
725
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -773,7 +773,7 @@ function initMemorySchema(db) {
773
773
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
774
774
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
775
775
  created_at TEXT DEFAULT (datetime('now')),
776
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
776
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
777
777
  );
778
778
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
779
779
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -793,7 +793,7 @@ function initMemorySchema(db) {
793
793
  duration_minutes REAL NOT NULL DEFAULT 0.0,
794
794
  tool_calls INTEGER NOT NULL DEFAULT 0,
795
795
  created_at TEXT DEFAULT (datetime('now')),
796
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
796
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
797
797
  );
798
798
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
799
799
  `);
@@ -806,7 +806,7 @@ function initMemorySchema(db) {
806
806
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
807
807
  commit_hash TEXT,
808
808
  created_at TEXT DEFAULT (datetime('now')),
809
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
809
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
810
810
  );
811
811
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
812
812
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -823,7 +823,7 @@ function initMemorySchema(db) {
823
823
  corrections_needed INTEGER NOT NULL DEFAULT 0,
824
824
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
825
825
  created_at TEXT DEFAULT (datetime('now')),
826
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
826
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
827
827
  );
828
828
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
829
829
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -842,7 +842,7 @@ function initMemorySchema(db) {
842
842
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
843
843
  evidence TEXT,
844
844
  metadata TEXT,
845
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
845
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
846
846
  );
847
847
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
848
848
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -859,7 +859,7 @@ function initMemorySchema(db) {
859
859
  details TEXT,
860
860
  rules_violated TEXT,
861
861
  created_at TEXT DEFAULT (datetime('now')),
862
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
862
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
863
863
  );
864
864
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
865
865
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -877,7 +877,7 @@ function initMemorySchema(db) {
877
877
  affected_files TEXT,
878
878
  commit_hash TEXT,
879
879
  created_at TEXT DEFAULT (datetime('now')),
880
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
880
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
881
881
  );
882
882
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
883
883
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -890,7 +890,7 @@ function initMemorySchema(db) {
890
890
  risk_score INTEGER NOT NULL DEFAULT 0,
891
891
  findings TEXT,
892
892
  created_at TEXT DEFAULT (datetime('now')),
893
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
893
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
894
894
  );
895
895
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
896
896
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massu/core",
3
- "version": "1.10.1",
3
+ "version": "1.10.3",
4
4
  "type": "module",
5
5
  "description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement, tiered tooling (12 free / 73 total), 59 workflow commands, 11 agents, 20+ patterns",
6
6
  "main": "src/server.ts",
package/src/cloud-sync.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  removePendingSync,
10
10
  incrementRetryCount,
11
11
  } from './memory-db.ts';
12
+ import { classifyVisibility } from './observation-extractor.ts';
12
13
 
13
14
  // ============================================================
14
15
  // Cloud Sync Module
@@ -98,7 +99,32 @@ export async function syncToCloud(
98
99
  const filteredPayload: SyncPayload = {};
99
100
  if (cloud.sync?.memory !== false) {
100
101
  filteredPayload.sessions = payload.sessions;
101
- filteredPayload.observations = payload.observations;
102
+ // P-H020 (plan-stage-c-high-batch): consume classifyVisibility() to drop
103
+ // observations whose title/detail matches PRIVATE_PATTERNS (Stripe keys,
104
+ // env var names, file paths, Bearer tokens, etc.). Pre-fix: cloud-sync
105
+ // transmitted EVERY observation to Massu's Supabase, leaking customer
106
+ // secrets and absolute file paths.
107
+ if (payload.observations) {
108
+ let droppedPrivate = 0;
109
+ filteredPayload.observations = payload.observations.filter((obs) => {
110
+ if (classifyVisibility(obs.content ?? '', obs.content ?? '') === 'private') {
111
+ droppedPrivate += 1;
112
+ return false;
113
+ }
114
+ // Belt-and-suspenders: also drop if file_path matches private patterns.
115
+ if (obs.file_path && classifyVisibility(obs.file_path, obs.file_path) === 'private') {
116
+ droppedPrivate += 1;
117
+ return false;
118
+ }
119
+ return true;
120
+ });
121
+ if (droppedPrivate > 0) {
122
+ // Surface to the customer's stderr so they can audit what got filtered.
123
+ process.stderr.write(
124
+ `[massu] cloud-sync: dropped ${droppedPrivate} private observation(s) (PRIVATE_PATTERNS match)\n`,
125
+ );
126
+ }
127
+ }
102
128
  }
103
129
  if (cloud.sync?.analytics !== false) {
104
130
  filteredPayload.analytics = payload.analytics;
@@ -104,12 +104,19 @@ export function initKnowledgeSchema(db: Database.Database): void {
104
104
  CREATE INDEX IF NOT EXISTS idx_ki_cr ON knowledge_incidents(cr_added);
105
105
 
106
106
  -- Schema mismatch quick lookup
107
+ -- P-H013 (plan-stage-c-high-batch): the source column no longer has a
108
+ -- SQL DEFAULT. Previously the value was a JS template-literal
109
+ -- interpolation baked into the customer SQLite at schema creation
110
+ -- time, so later config changes were ignored. Default is now applied
111
+ -- at INSERT time via getConfig().conventions.knowledgeSourceFiles[0]
112
+ -- in knowledge-indexer.ts -- a runtime config lookup that reflects
113
+ -- the customers current config.
107
114
  CREATE TABLE IF NOT EXISTS knowledge_schema_mismatches (
108
115
  id INTEGER PRIMARY KEY AUTOINCREMENT,
109
116
  table_name TEXT NOT NULL,
110
117
  wrong_column TEXT NOT NULL,
111
118
  correct_column TEXT NOT NULL,
112
- source TEXT DEFAULT '${getConfig().conventions?.knowledgeSourceFiles?.[0] ?? 'CLAUDE.md'}'
119
+ source TEXT NOT NULL
113
120
  );
114
121
 
115
122
  CREATE INDEX IF NOT EXISTS idx_ksm_table ON knowledge_schema_mismatches(table_name);
@@ -440,9 +440,13 @@ export function indexAllKnowledge(db: Database.Database): IndexStats {
440
440
  const insertIncident = db.prepare(
441
441
  'INSERT OR IGNORE INTO knowledge_incidents (incident_num, date, type, gap_found, prevention) VALUES (?, ?, ?, ?, ?)'
442
442
  );
443
+ // P-H013 (plan-stage-c-high-batch): source column now bound at insert time
444
+ // (was a SQL-DEFAULT template-literal interpolation baked into the customer's
445
+ // SQLite at schema creation time, ignoring later config changes).
443
446
  const insertMismatch = db.prepare(
444
- 'INSERT INTO knowledge_schema_mismatches (table_name, wrong_column, correct_column) VALUES (?, ?, ?)'
447
+ 'INSERT INTO knowledge_schema_mismatches (table_name, wrong_column, correct_column, source) VALUES (?, ?, ?, ?)'
445
448
  );
449
+ const defaultSource = getConfig().conventions?.knowledgeSourceFiles?.[0] ?? 'CLAUDE.md';
446
450
 
447
451
  // Discover all .claude/ markdown files
448
452
  const files = discoverMarkdownFiles(paths.claudeDir);
@@ -567,7 +571,7 @@ export function indexAllKnowledge(db: Database.Database): IndexStats {
567
571
  // Extract schema mismatches
568
572
  const mismatches = parseSchemaMismatches(content);
569
573
  for (const m of mismatches) {
570
- insertMismatch.run(m.table_name, m.wrong_column, m.correct_column);
574
+ insertMismatch.run(m.table_name, m.wrong_column, m.correct_column, defaultSource);
571
575
  insertChunk.run(docId, 'mismatch', m.table_name, `${m.table_name}: ${m.wrong_column} -> ${m.correct_column}`, null, null, JSON.stringify({ table: m.table_name }));
572
576
  stats.chunksCreated++;
573
577
  }
package/src/memory-db.ts CHANGED
@@ -214,7 +214,7 @@ export function initMemorySchema(db: Database.Database): void {
214
214
  response_tokens INTEGER,
215
215
  created_at TEXT DEFAULT (datetime('now')),
216
216
  created_at_epoch INTEGER DEFAULT (unixepoch()),
217
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
217
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
218
218
  );
219
219
 
220
220
  CREATE INDEX IF NOT EXISTS idx_ct_session ON conversation_turns(session_id);
@@ -237,7 +237,7 @@ export function initMemorySchema(db: Database.Database): void {
237
237
  files_involved TEXT,
238
238
  created_at TEXT DEFAULT (datetime('now')),
239
239
  created_at_epoch INTEGER DEFAULT (unixepoch()),
240
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
240
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
241
241
  );
242
242
 
243
243
  CREATE INDEX IF NOT EXISTS idx_tcd_session ON tool_call_details(session_id);
@@ -302,7 +302,7 @@ export function initMemorySchema(db: Database.Database): void {
302
302
  vr_checks_failed INTEGER NOT NULL DEFAULT 0,
303
303
  incidents_triggered INTEGER NOT NULL DEFAULT 0,
304
304
  created_at TEXT DEFAULT (datetime('now')),
305
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
305
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
306
306
  );
307
307
  CREATE INDEX IF NOT EXISTS idx_sqs_session ON session_quality_scores(session_id);
308
308
  CREATE INDEX IF NOT EXISTS idx_sqs_project ON session_quality_scores(project);
@@ -324,7 +324,7 @@ export function initMemorySchema(db: Database.Database): void {
324
324
  duration_minutes REAL NOT NULL DEFAULT 0.0,
325
325
  tool_calls INTEGER NOT NULL DEFAULT 0,
326
326
  created_at TEXT DEFAULT (datetime('now')),
327
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
327
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
328
328
  );
329
329
  CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
330
330
  `);
@@ -339,7 +339,7 @@ export function initMemorySchema(db: Database.Database): void {
339
339
  estimated_cost_usd REAL NOT NULL DEFAULT 0.0,
340
340
  commit_hash TEXT,
341
341
  created_at TEXT DEFAULT (datetime('now')),
342
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
342
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
343
343
  );
344
344
  CREATE INDEX IF NOT EXISTS idx_fc_feature ON feature_costs(feature_key);
345
345
  CREATE INDEX IF NOT EXISTS idx_fc_session ON feature_costs(session_id);
@@ -358,7 +358,7 @@ export function initMemorySchema(db: Database.Database): void {
358
358
  corrections_needed INTEGER NOT NULL DEFAULT 0,
359
359
  follow_up_prompts INTEGER NOT NULL DEFAULT 0,
360
360
  created_at TEXT DEFAULT (datetime('now')),
361
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
361
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
362
362
  );
363
363
  CREATE INDEX IF NOT EXISTS idx_po_session ON prompt_outcomes(session_id);
364
364
  CREATE INDEX IF NOT EXISTS idx_po_category ON prompt_outcomes(prompt_category);
@@ -379,7 +379,7 @@ export function initMemorySchema(db: Database.Database): void {
379
379
  approval_status TEXT CHECK(approval_status IN ('auto_approved', 'human_approved', 'pending', 'denied')),
380
380
  evidence TEXT,
381
381
  metadata TEXT,
382
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
382
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
383
383
  );
384
384
  CREATE INDEX IF NOT EXISTS idx_al_session ON audit_log(session_id);
385
385
  CREATE INDEX IF NOT EXISTS idx_al_file ON audit_log(file_path);
@@ -398,7 +398,7 @@ export function initMemorySchema(db: Database.Database): void {
398
398
  details TEXT,
399
399
  rules_violated TEXT,
400
400
  created_at TEXT DEFAULT (datetime('now')),
401
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
401
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
402
402
  );
403
403
  CREATE INDEX IF NOT EXISTS idx_vr_session ON validation_results(session_id);
404
404
  CREATE INDEX IF NOT EXISTS idx_vr_file ON validation_results(file_path);
@@ -418,7 +418,7 @@ export function initMemorySchema(db: Database.Database): void {
418
418
  affected_files TEXT,
419
419
  commit_hash TEXT,
420
420
  created_at TEXT DEFAULT (datetime('now')),
421
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
421
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
422
422
  );
423
423
  CREATE INDEX IF NOT EXISTS idx_ad_session ON architecture_decisions(session_id);
424
424
  CREATE INDEX IF NOT EXISTS idx_ad_status ON architecture_decisions(status);
@@ -433,7 +433,7 @@ export function initMemorySchema(db: Database.Database): void {
433
433
  risk_score INTEGER NOT NULL DEFAULT 0,
434
434
  findings TEXT,
435
435
  created_at TEXT DEFAULT (datetime('now')),
436
- FOREIGN KEY (session_id) REFERENCES sessions(session_id)
436
+ FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
437
437
  );
438
438
  CREATE INDEX IF NOT EXISTS idx_ss_session ON security_scores(session_id);
439
439
  CREATE INDEX IF NOT EXISTS idx_ss_file ON security_scores(file_path);
@@ -625,7 +625,13 @@ export function enqueueSyncPayload(db: Database.Database, payload: string): void
625
625
 
626
626
  /**
627
627
  * Dequeue pending sync items (oldest first).
628
- * Items with retry_count >= 10 are silently discarded to prevent infinite accumulation.
628
+ *
629
+ * P-H012 (plan-stage-c-high-batch): when items exceed retry_count >= 10
630
+ * they are discarded — but the discard now emits a stderr warning AND
631
+ * inserts an analytics_events telemetry row so the customer can detect
632
+ * silent cloud-sync failure (e.g., invalid API key for >10 sync cycles).
633
+ * Previously this was a silent DELETE; customers lost all queued
634
+ * observations with no visibility.
629
635
  */
630
636
  export function dequeuePendingSync(
631
637
  db: Database.Database,
@@ -633,11 +639,34 @@ export function dequeuePendingSync(
633
639
  ): Array<{ id: number; payload: string; retry_count: number }> {
634
640
  // First, discard items that have exceeded max retries
635
641
  const stale = db.prepare(
636
- 'SELECT id FROM pending_sync WHERE retry_count >= 10'
637
- ).all() as Array<{ id: number }>;
642
+ 'SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10'
643
+ ).all() as Array<{ id: number; retry_count: number; last_error: string | null }>;
638
644
  if (stale.length > 0) {
639
645
  const ids = stale.map(s => s.id);
640
646
  db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => '?').join(',')})`).run(...ids);
647
+ // P-H012: stderr warning so the customer's terminal sees what happened.
648
+ const lastErrors = [...new Set(stale.map(s => s.last_error).filter(Boolean))];
649
+ process.stderr.write(
650
+ `[massu] WARNING: ${stale.length} cloud-sync queue item(s) discarded after 10+ retries. ` +
651
+ `Likely cause: invalid API key or unreachable endpoint. ` +
652
+ `Recent errors: ${lastErrors.slice(0, 3).join('; ') || '(none recorded)'}\n`,
653
+ );
654
+ // P-H012: telemetry event for dashboard surfacing. Use the analytics_events
655
+ // sink that already exists in memory-db (see addObservation pattern).
656
+ try {
657
+ db.prepare(`
658
+ INSERT INTO analytics_events (event_type, event_data, created_at)
659
+ VALUES (?, ?, datetime('now'))
660
+ `).run(
661
+ 'cloud_sync_giveup',
662
+ JSON.stringify({
663
+ discarded_count: stale.length,
664
+ recent_errors: lastErrors.slice(0, 3),
665
+ }),
666
+ );
667
+ } catch {
668
+ // analytics_events may not exist in older schemas — best-effort.
669
+ }
641
670
  }
642
671
 
643
672
  return db.prepare(
@@ -1,4 +1,4 @@
1
- // AUTO-GENERATED by scripts/bundle-pubkey.mjs at 2026-05-17T05:11:08.118Z.
1
+ // AUTO-GENERATED by scripts/bundle-pubkey.mjs at 2026-05-17T05:43:58.152Z.
2
2
  // Source pem: packages/core/security/registry-pubkey.pem
3
3
  // RAW-bytes sha256: 3b6226d036c472e533110d11a7d0cd2773ce1d7d4f1003517d5bd69c5418ed4c
4
4
  // DO NOT EDIT — regenerate via `node scripts/bundle-pubkey.mjs` or