memorix 1.7.3 → 1.8.1

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/dist/cli/index.js +1488 -270
  5. package/dist/dashboard/static/app.js +125 -27
  6. package/dist/dashboard/static/index.html +11 -1
  7. package/dist/dashboard/static/style.css +86 -0
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.js +927 -240
  10. package/dist/maintenance-runner.js +607 -68
  11. package/dist/media-audio-runner.js +602 -63
  12. package/dist/media-video-runner.js +607 -68
  13. package/dist/memcode-runtime/CHANGELOG.md +41 -0
  14. package/dist/sdk.js +926 -239
  15. package/dist/types.d.ts +8 -0
  16. package/dist/vector-backfill-runner.js +607 -68
  17. package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +1 -1
  18. package/docs/1.8.0-RELEASE-CANDIDATE-SPEC.md +75 -0
  19. package/docs/API_REFERENCE.md +39 -1
  20. package/docs/ARCHITECTURE.md +3 -1
  21. package/docs/DASHBOARD.md +16 -0
  22. package/docs/DEVELOPMENT.md +3 -2
  23. package/docs/INTEGRATIONS.md +1 -1
  24. package/docs/README.md +15 -2
  25. package/package.json +1 -1
  26. package/plugins/antigravity/memorix/rules/memorix.md +1 -1
  27. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  28. package/plugins/claude/memorix/SKILL.md +1 -1
  29. package/plugins/claude/memorix/skills/memorix-memory/SKILL.md +2 -2
  30. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  31. package/plugins/codebuddy/memorix/skills/memorix-memory/SKILL.md +2 -2
  32. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  33. package/plugins/codex/memorix/skills/memorix-memory/SKILL.md +2 -2
  34. package/plugins/copilot/memorix/plugin.json +1 -1
  35. package/plugins/copilot/memorix/skills/memorix-memory/SKILL.md +2 -2
  36. package/plugins/cursor/memorix/skills/memorix-memory/SKILL.md +2 -2
  37. package/plugins/gemini/memorix/GEMINI.md +1 -1
  38. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  39. package/plugins/omp/memorix/package.json +1 -1
  40. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  41. package/plugins/pi/memorix/package.json +1 -1
  42. package/plugins/pi/memorix/skills/memorix-memory/SKILL.md +2 -2
  43. package/server.json +2 -2
  44. package/src/cli/capability-map.ts +2 -0
  45. package/src/cli/commands/context.ts +11 -0
  46. package/src/cli/commands/dashboard.ts +1 -1
  47. package/src/cli/commands/evidence.ts +56 -0
  48. package/src/cli/commands/feedback.ts +58 -0
  49. package/src/cli/commands/resume.ts +2 -0
  50. package/src/cli/commands/serve-http.ts +229 -13
  51. package/src/cli/fallback-budget.ts +59 -0
  52. package/src/cli/index.ts +5 -1
  53. package/src/compact/engine.ts +26 -1
  54. package/src/config/dotenv-loader.ts +17 -0
  55. package/src/dashboard/config-provenance.ts +17 -0
  56. package/src/dashboard/evidence.ts +63 -0
  57. package/src/dashboard/server.ts +178 -154
  58. package/src/dashboard/static/index.html +11 -1
  59. package/src/dashboard/static/style.css +86 -0
  60. package/src/hooks/installers/index.ts +1 -1
  61. package/src/hooks/official-skills.ts +3 -3
  62. package/src/hooks/rules/memorix-agent-rules.md +1 -1
  63. package/src/memory/feedback-model.ts +4 -0
  64. package/src/memory/feedback.ts +220 -0
  65. package/src/memory/observations.ts +19 -0
  66. package/src/memory/retention.ts +38 -5
  67. package/src/server/mcp-binding-store.ts +79 -0
  68. package/src/server/mcp-diagnostics.ts +40 -0
  69. package/src/server/tool-profile.ts +4 -0
  70. package/src/server.ts +125 -5
  71. package/src/store/evidence-store.ts +261 -0
  72. package/src/store/sqlite-db.ts +104 -0
  73. package/src/types.ts +8 -0
@@ -95,6 +95,8 @@ function loadEnvFile(filePath) {
95
95
  const parsed = parse(readFileSync(filePath, "utf-8"));
96
96
  for (const [key, value] of Object.entries(parsed)) {
97
97
  if (!(key in process.env)) {
98
+ loadedEnvKeys.add(key);
99
+ loadedEnvValues.set(key, value);
98
100
  process.env[key] = value;
99
101
  injectedKeys.add(key);
100
102
  }
@@ -107,6 +109,8 @@ function loadDotenv(projectRoot, options = {}) {
107
109
  resetDotenv();
108
110
  }
109
111
  loadedEnvFiles.length = 0;
112
+ loadedEnvKeys.clear();
113
+ loadedEnvValues.clear();
110
114
  if (projectRoot) {
111
115
  loadEnvFile(getProjectDotenvPath(projectRoot));
112
116
  }
@@ -122,11 +126,12 @@ function resetDotenv() {
122
126
  dotenvLoaded = false;
123
127
  dotenvProjectRoot = null;
124
128
  loadedEnvFiles.length = 0;
129
+ loadedEnvKeys.clear();
125
130
  }
126
131
  function getLoadedEnvFiles() {
127
132
  return loadedEnvFiles;
128
133
  }
129
- var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, injectedKeys;
134
+ var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
130
135
  var init_dotenv_loader = __esm({
131
136
  "src/config/dotenv-loader.ts"() {
132
137
  "use strict";
@@ -135,6 +140,8 @@ var init_dotenv_loader = __esm({
135
140
  dotenvLoaded = false;
136
141
  dotenvProjectRoot = null;
137
142
  loadedEnvFiles = [];
143
+ loadedEnvKeys = /* @__PURE__ */ new Set();
144
+ loadedEnvValues = /* @__PURE__ */ new Map();
138
145
  injectedKeys = /* @__PURE__ */ new Set();
139
146
  }
140
147
  });
@@ -488,7 +495,7 @@ function closeAllDatabases() {
488
495
  _dbCache.delete(key);
489
496
  }
490
497
  }
491
- var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
498
+ var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_EVIDENCE_CARDS_TABLE, CREATE_EVIDENCE_CARD_EVENTS_TABLE, CREATE_MEMORY_FEEDBACK_STATES_TABLE, CREATE_MEMORY_FEEDBACK_EVENTS_TABLE, CREATE_MCP_BINDINGS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
492
499
  var init_sqlite_db = __esm({
493
500
  "src/store/sqlite-db.ts"() {
494
501
  "use strict";
@@ -1134,6 +1141,79 @@ CREATE TABLE IF NOT EXISTS compaction_checkpoints (
1134
1141
  created_at TEXT NOT NULL,
1135
1142
  updated_at TEXT NOT NULL
1136
1143
  );
1144
+ `;
1145
+ CREATE_EVIDENCE_CARDS_TABLE = `
1146
+ CREATE TABLE IF NOT EXISTS evidence_cards (
1147
+ id TEXT PRIMARY KEY,
1148
+ project_id TEXT NOT NULL,
1149
+ candidate_kind TEXT NOT NULL,
1150
+ candidate_id TEXT NOT NULL,
1151
+ title TEXT NOT NULL,
1152
+ summary TEXT NOT NULL DEFAULT '',
1153
+ entity_name TEXT,
1154
+ source_kind TEXT NOT NULL,
1155
+ source_ref TEXT NOT NULL,
1156
+ locator TEXT,
1157
+ captured_hash TEXT,
1158
+ session_id TEXT,
1159
+ status TEXT NOT NULL DEFAULT 'active',
1160
+ verification TEXT NOT NULL DEFAULT 'unverified',
1161
+ freshness TEXT NOT NULL DEFAULT 'current',
1162
+ stale_reason TEXT,
1163
+ files_json TEXT NOT NULL DEFAULT '[]',
1164
+ related_entities_json TEXT NOT NULL DEFAULT '[]',
1165
+ created_at TEXT NOT NULL,
1166
+ updated_at TEXT NOT NULL,
1167
+ UNIQUE(project_id, candidate_kind, candidate_id)
1168
+ );
1169
+ `;
1170
+ CREATE_EVIDENCE_CARD_EVENTS_TABLE = `
1171
+ CREATE TABLE IF NOT EXISTS evidence_card_events (
1172
+ id TEXT PRIMARY KEY,
1173
+ project_id TEXT NOT NULL,
1174
+ card_id TEXT NOT NULL,
1175
+ kind TEXT NOT NULL,
1176
+ detail TEXT,
1177
+ created_at TEXT NOT NULL,
1178
+ FOREIGN KEY (card_id) REFERENCES evidence_cards(id) ON DELETE CASCADE
1179
+ );
1180
+ `;
1181
+ CREATE_MEMORY_FEEDBACK_STATES_TABLE = `
1182
+ CREATE TABLE IF NOT EXISTS memory_feedback_states (
1183
+ project_id TEXT NOT NULL,
1184
+ candidate_kind TEXT NOT NULL,
1185
+ candidate_id TEXT NOT NULL,
1186
+ weight REAL NOT NULL DEFAULT 1.0,
1187
+ lifecycle_status TEXT NOT NULL DEFAULT 'active',
1188
+ updated_at TEXT NOT NULL,
1189
+ PRIMARY KEY (project_id, candidate_kind, candidate_id)
1190
+ );
1191
+ `;
1192
+ CREATE_MEMORY_FEEDBACK_EVENTS_TABLE = `
1193
+ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1194
+ id TEXT PRIMARY KEY,
1195
+ project_id TEXT NOT NULL,
1196
+ candidate_kind TEXT NOT NULL,
1197
+ candidate_id TEXT NOT NULL,
1198
+ signal TEXT NOT NULL,
1199
+ actor TEXT,
1200
+ note TEXT,
1201
+ source_ref TEXT NOT NULL,
1202
+ target_event_id TEXT,
1203
+ delta REAL NOT NULL DEFAULT 0,
1204
+ observed_at TEXT NOT NULL
1205
+ );
1206
+ `;
1207
+ CREATE_MCP_BINDINGS_TABLE = `
1208
+ CREATE TABLE IF NOT EXISTS mcp_bindings (
1209
+ handle_id TEXT PRIMARY KEY,
1210
+ project_id TEXT NOT NULL,
1211
+ project_root TEXT NOT NULL,
1212
+ data_dir TEXT NOT NULL,
1213
+ created_at TEXT NOT NULL,
1214
+ last_used_at TEXT NOT NULL,
1215
+ expires_at TEXT
1216
+ );
1137
1217
  `;
1138
1218
  CREATE_INDEXES = `
1139
1219
  CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
@@ -1196,6 +1276,12 @@ CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_target
1196
1276
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
1197
1277
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
1198
1278
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
1279
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC);
1280
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id);
1281
+ CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1282
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1283
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1284
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1199
1285
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1200
1286
  ON maintenance_jobs(project_id, kind, dedupe_key)
1201
1287
  WHERE status IN ('pending', 'running', 'retry');
@@ -1351,6 +1437,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1351
1437
  }
1352
1438
  db2.exec("CREATE INDEX IF NOT EXISTS idx_media_jobs_source_asset ON media_jobs(project_id, source_asset_id, status)");
1353
1439
  }
1440
+ },
1441
+ {
1442
+ id: "1.8-context-control-plane",
1443
+ apply: (db2) => {
1444
+ db2.exec(CREATE_EVIDENCE_CARDS_TABLE);
1445
+ db2.exec(CREATE_EVIDENCE_CARD_EVENTS_TABLE);
1446
+ db2.exec(CREATE_MEMORY_FEEDBACK_STATES_TABLE);
1447
+ db2.exec(CREATE_MEMORY_FEEDBACK_EVENTS_TABLE);
1448
+ db2.exec(CREATE_MCP_BINDINGS_TABLE);
1449
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC)");
1450
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id)");
1451
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at)");
1452
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1453
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC)");
1454
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1455
+ }
1354
1456
  }
1355
1457
  ];
1356
1458
  _dbCache = /* @__PURE__ */ new Map();
@@ -8463,8 +8565,228 @@ var init_lifecycle = __esm({
8463
8565
  }
8464
8566
  });
8465
8567
 
8568
+ // src/store/evidence-store.ts
8569
+ import { createHash as createHash3, randomUUID as randomUUID4 } from "crypto";
8570
+ function safeJson(value, fallback) {
8571
+ if (typeof value !== "string" || value.length === 0) return fallback;
8572
+ try {
8573
+ return JSON.parse(value);
8574
+ } catch {
8575
+ return fallback;
8576
+ }
8577
+ }
8578
+ function rowToCard(row) {
8579
+ return {
8580
+ id: String(row.id),
8581
+ projectId: String(row.project_id),
8582
+ candidateKind: row.candidate_kind,
8583
+ candidateId: String(row.candidate_id),
8584
+ title: String(row.title),
8585
+ summary: String(row.summary ?? ""),
8586
+ ...row.entity_name ? { entityName: String(row.entity_name) } : {},
8587
+ sourceKind: String(row.source_kind),
8588
+ sourceRef: String(row.source_ref),
8589
+ ...row.locator ? { locator: String(row.locator) } : {},
8590
+ ...row.captured_hash ? { capturedHash: String(row.captured_hash) } : {},
8591
+ ...row.session_id ? { sessionId: String(row.session_id) } : {},
8592
+ status: String(row.status ?? "active"),
8593
+ verification: row.verification,
8594
+ freshness: row.freshness,
8595
+ ...row.stale_reason ? { staleReason: String(row.stale_reason) } : {},
8596
+ files: safeJson(row.files_json, []),
8597
+ relatedEntities: safeJson(row.related_entities_json, []),
8598
+ createdAt: String(row.created_at),
8599
+ updatedAt: String(row.updated_at)
8600
+ };
8601
+ }
8602
+ function observationHash(observation) {
8603
+ return createHash3("sha256").update(JSON.stringify({
8604
+ projectId: observation.projectId,
8605
+ id: observation.id,
8606
+ title: observation.title,
8607
+ narrative: observation.narrative,
8608
+ facts: observation.facts,
8609
+ files: observation.filesModified,
8610
+ relatedEntities: observation.relatedEntities
8611
+ })).digest("hex");
8612
+ }
8613
+ function sourceKind(observation) {
8614
+ if (observation.source === "git" || observation.sourceDetail === "git-ingest") return "git";
8615
+ if (observation.sourceDetail === "hook") return "hook";
8616
+ return observation.source ?? "agent";
8617
+ }
8618
+ var EvidenceCardStore;
8619
+ var init_evidence_store = __esm({
8620
+ "src/store/evidence-store.ts"() {
8621
+ "use strict";
8622
+ init_esm_shims();
8623
+ init_sqlite_db();
8624
+ EvidenceCardStore = class {
8625
+ db = null;
8626
+ async init(dataDir) {
8627
+ this.db = getDatabase(dataDir);
8628
+ }
8629
+ requireDb() {
8630
+ if (!this.db) throw new Error("EvidenceCardStore is not initialized.");
8631
+ return this.db;
8632
+ }
8633
+ upsertObservation(observation) {
8634
+ const db2 = this.requireDb();
8635
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
8636
+ const card = {
8637
+ id: `evidence:observation:${observation.projectId}:${observation.id}`,
8638
+ projectId: observation.projectId,
8639
+ candidateKind: "observation",
8640
+ candidateId: String(observation.id),
8641
+ title: observation.title,
8642
+ summary: observation.narrative.slice(0, 500),
8643
+ entityName: observation.entityName,
8644
+ sourceKind: sourceKind(observation),
8645
+ sourceRef: `observation:${observation.id}`,
8646
+ locator: observation.filesModified?.join(", ") || `observation:${observation.id}`,
8647
+ capturedHash: observationHash(observation),
8648
+ ...observation.sessionId ? { sessionId: observation.sessionId } : {},
8649
+ status: observation.status ?? "active",
8650
+ verification: observation.status === "resolved" ? "verified" : "unverified",
8651
+ freshness: "current",
8652
+ files: observation.filesModified ?? [],
8653
+ relatedEntities: observation.relatedEntities ?? [],
8654
+ createdAt: observation.createdAt,
8655
+ updatedAt: now3
8656
+ };
8657
+ db2.prepare(`
8658
+ INSERT INTO evidence_cards (
8659
+ id, project_id, candidate_kind, candidate_id, title, summary, entity_name,
8660
+ source_kind, source_ref, locator, captured_hash, session_id, status,
8661
+ verification, freshness, stale_reason, files_json, related_entities_json,
8662
+ created_at, updated_at
8663
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?)
8664
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
8665
+ title = excluded.title,
8666
+ summary = excluded.summary,
8667
+ entity_name = excluded.entity_name,
8668
+ source_kind = excluded.source_kind,
8669
+ source_ref = excluded.source_ref,
8670
+ locator = excluded.locator,
8671
+ captured_hash = excluded.captured_hash,
8672
+ session_id = excluded.session_id,
8673
+ status = excluded.status,
8674
+ freshness = 'current',
8675
+ stale_reason = NULL,
8676
+ files_json = excluded.files_json,
8677
+ related_entities_json = excluded.related_entities_json,
8678
+ updated_at = excluded.updated_at
8679
+ `).run(
8680
+ card.id,
8681
+ card.projectId,
8682
+ card.candidateKind,
8683
+ card.candidateId,
8684
+ card.title,
8685
+ card.summary,
8686
+ card.entityName ?? null,
8687
+ card.sourceKind,
8688
+ card.sourceRef,
8689
+ card.locator ?? null,
8690
+ card.capturedHash ?? null,
8691
+ card.sessionId ?? null,
8692
+ card.status,
8693
+ card.verification,
8694
+ card.freshness,
8695
+ JSON.stringify(card.files),
8696
+ JSON.stringify(card.relatedEntities),
8697
+ card.createdAt,
8698
+ card.updatedAt
8699
+ );
8700
+ return this.get(card.projectId, card.candidateKind, card.candidateId) ?? card;
8701
+ }
8702
+ get(projectId, candidateKind, candidateId) {
8703
+ const row = this.requireDb().prepare(`
8704
+ SELECT * FROM evidence_cards WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
8705
+ `).get(projectId, candidateKind, candidateId);
8706
+ return row ? rowToCard(row) : void 0;
8707
+ }
8708
+ list(projectId, options = {}) {
8709
+ const limit2 = Math.min(100, Math.max(1, Math.floor(options.limit ?? 20)));
8710
+ const clauses = ["project_id = ?"];
8711
+ const args = [projectId];
8712
+ if (options.freshness) {
8713
+ clauses.push("freshness = ?");
8714
+ args.push(options.freshness);
8715
+ }
8716
+ if (options.status) {
8717
+ clauses.push("status = ?");
8718
+ args.push(options.status);
8719
+ }
8720
+ args.push(limit2);
8721
+ const rows = this.requireDb().prepare(`
8722
+ SELECT * FROM evidence_cards WHERE ${clauses.join(" AND ")} ORDER BY updated_at DESC, id DESC LIMIT ?
8723
+ `).all(...args);
8724
+ return rows.map(rowToCard);
8725
+ }
8726
+ syncObservations(observations2) {
8727
+ return observations2.map((observation) => this.upsertObservation(observation));
8728
+ }
8729
+ markStaleForPaths(projectId, paths, reason = "source file changed") {
8730
+ const normalized = new Set(paths.map((value) => value.replace(/\\/g, "/").toLowerCase()));
8731
+ if (normalized.size === 0) return 0;
8732
+ const db2 = this.requireDb();
8733
+ const rows = db2.prepare(`SELECT * FROM evidence_cards WHERE project_id = ? AND freshness != 'stale'`).all(projectId);
8734
+ const update2 = db2.prepare(`
8735
+ UPDATE evidence_cards SET freshness = 'stale', stale_reason = ?, updated_at = ?
8736
+ WHERE id = ?
8737
+ `);
8738
+ let changed = 0;
8739
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
8740
+ const run = db2.transaction(() => {
8741
+ for (const row of rows) {
8742
+ const files = safeJson(row.files_json, []);
8743
+ if (!files.some((file) => normalized.has(file.replace(/\\/g, "/").toLowerCase()))) continue;
8744
+ update2.run(reason, now3, row.id);
8745
+ changed++;
8746
+ }
8747
+ });
8748
+ run();
8749
+ return changed;
8750
+ }
8751
+ setVerification(projectId, cardId, verification) {
8752
+ this.requireDb().prepare(`
8753
+ UPDATE evidence_cards SET verification = ?, updated_at = ? WHERE project_id = ? AND id = ?
8754
+ `).run(verification, (/* @__PURE__ */ new Date()).toISOString(), projectId, cardId);
8755
+ }
8756
+ recordEvent(projectId, cardId, kind, detail) {
8757
+ const event = {
8758
+ id: randomUUID4(),
8759
+ projectId,
8760
+ cardId,
8761
+ kind,
8762
+ ...detail ? { detail: detail.slice(0, 2e3) } : {},
8763
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
8764
+ };
8765
+ this.requireDb().prepare(`
8766
+ INSERT INTO evidence_card_events (id, project_id, card_id, kind, detail, created_at)
8767
+ VALUES (?, ?, ?, ?, ?, ?)
8768
+ `).run(event.id, event.projectId, event.cardId, event.kind, event.detail ?? null, event.createdAt);
8769
+ return event;
8770
+ }
8771
+ listEvents(cardId, limit2 = 50) {
8772
+ const rows = this.requireDb().prepare(`
8773
+ SELECT * FROM evidence_card_events WHERE card_id = ? ORDER BY created_at DESC LIMIT ?
8774
+ `).all(cardId, Math.min(200, Math.max(1, Math.floor(limit2))));
8775
+ return rows.map((row) => ({
8776
+ id: String(row.id),
8777
+ projectId: String(row.project_id),
8778
+ cardId: String(row.card_id),
8779
+ kind: String(row.kind),
8780
+ ...row.detail ? { detail: String(row.detail) } : {},
8781
+ createdAt: String(row.created_at)
8782
+ }));
8783
+ }
8784
+ };
8785
+ }
8786
+ });
8787
+
8466
8788
  // src/knowledge/workspace-store.ts
8467
- import { randomUUID as randomUUID4 } from "crypto";
8789
+ import { randomUUID as randomUUID5 } from "crypto";
8468
8790
  function arrayValue(raw) {
8469
8791
  if (typeof raw !== "string" || !raw) return [];
8470
8792
  try {
@@ -8605,7 +8927,7 @@ var init_workspace_store = __esm({
8605
8927
  }
8606
8928
  createProposal(input) {
8607
8929
  const proposal = {
8608
- id: input.id ?? randomUUID4(),
8930
+ id: input.id ?? randomUUID5(),
8609
8931
  ...input,
8610
8932
  status: input.status ?? "pending",
8611
8933
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -8658,13 +8980,13 @@ __export(workspace_exports, {
8658
8980
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
8659
8981
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
8660
8982
  });
8661
- import { createHash as createHash3 } from "crypto";
8983
+ import { createHash as createHash4 } from "crypto";
8662
8984
  import { execFile } from "child_process";
8663
8985
  import { promises as fs6 } from "fs";
8664
8986
  import path9 from "path";
8665
8987
  import { promisify } from "util";
8666
8988
  function hash(value) {
8667
- return createHash3("sha256").update(value).digest("hex");
8989
+ return createHash4("sha256").update(value).digest("hex");
8668
8990
  }
8669
8991
  function workspaceId(projectId, mode) {
8670
8992
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -9271,14 +9593,14 @@ var init_types2 = __esm({
9271
9593
  });
9272
9594
 
9273
9595
  // src/media/asset-store.ts
9274
- import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
9596
+ import { createHash as createHash5, randomUUID as randomUUID6 } from "crypto";
9275
9597
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
9276
9598
  import path10 from "path";
9277
9599
  function mediaRoot(dataDir) {
9278
9600
  return path10.resolve(dataDir, "media");
9279
9601
  }
9280
9602
  function projectStorageKey(projectId) {
9281
- return createHash4("sha256").update(projectId).digest("hex").slice(0, 16);
9603
+ return createHash5("sha256").update(projectId).digest("hex").slice(0, 16);
9282
9604
  }
9283
9605
  function validateUnderRoot(root, candidate) {
9284
9606
  const resolved = path10.resolve(candidate);
@@ -9342,7 +9664,7 @@ async function destinationExists(destination) {
9342
9664
  async function persistBuffer(bytes, destination) {
9343
9665
  await mkdir3(path10.dirname(destination), { recursive: true });
9344
9666
  if (await destinationExists(destination)) return false;
9345
- const temporary = `${destination}.${randomUUID5()}.tmp`;
9667
+ const temporary = `${destination}.${randomUUID6()}.tmp`;
9346
9668
  try {
9347
9669
  await writeFile3(temporary, bytes, { flag: "wx" });
9348
9670
  await rename2(temporary, destination);
@@ -9387,7 +9709,7 @@ async function importMediaBuffer(input) {
9387
9709
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
9388
9710
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
9389
9711
  const detected = detectMedia(input.bytes.subarray(0, 32));
9390
- const sha256 = createHash4("sha256").update(input.bytes).digest("hex");
9712
+ const sha256 = createHash5("sha256").update(input.bytes).digest("hex");
9391
9713
  return importKnownMedia({
9392
9714
  dataDir: input.dataDir,
9393
9715
  projectId: input.projectId,
@@ -9405,7 +9727,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
9405
9727
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
9406
9728
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
9407
9729
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
9408
- const sha256 = createHash4("sha256").update(bytes).digest("hex");
9730
+ const sha256 = createHash5("sha256").update(bytes).digest("hex");
9409
9731
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
9410
9732
  return bytes;
9411
9733
  }
@@ -62156,7 +62478,7 @@ var require_gaxios = __commonJS({
62156
62478
  var retry_js_1 = require_retry3();
62157
62479
  var stream_1 = __require("stream");
62158
62480
  var interceptor_js_1 = require_interceptor();
62159
- var randomUUID14 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
62481
+ var randomUUID16 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
62160
62482
  var HTTP_STATUS_NO_CONTENT = 204;
62161
62483
  var Gaxios = class {
62162
62484
  agentCache = /* @__PURE__ */ new Map();
@@ -62429,7 +62751,7 @@ var require_gaxios = __commonJS({
62429
62751
  */
62430
62752
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
62431
62753
  if (opts.multipart?.length) {
62432
- const boundary = await randomUUID14();
62754
+ const boundary = await randomUUID16();
62433
62755
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
62434
62756
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
62435
62757
  } else if (shouldDirectlyPassData) {
@@ -74656,7 +74978,7 @@ var require_websocket = __commonJS({
74656
74978
  var http3 = __require("http");
74657
74979
  var net = __require("net");
74658
74980
  var tls = __require("tls");
74659
- var { randomBytes, createHash: createHash13 } = __require("crypto");
74981
+ var { randomBytes, createHash: createHash14 } = __require("crypto");
74660
74982
  var { Duplex, Readable: Readable2 } = __require("stream");
74661
74983
  var { URL: URL2 } = __require("url");
74662
74984
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -75324,7 +75646,7 @@ var require_websocket = __commonJS({
75324
75646
  abortHandshake(websocket, socket, "Invalid Upgrade header");
75325
75647
  return;
75326
75648
  }
75327
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
75649
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
75328
75650
  if (res.headers["sec-websocket-accept"] !== digest2) {
75329
75651
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
75330
75652
  return;
@@ -75696,7 +76018,7 @@ var require_websocket_server = __commonJS({
75696
76018
  var EventEmitter = __require("events");
75697
76019
  var http3 = __require("http");
75698
76020
  var { Duplex } = __require("stream");
75699
- var { createHash: createHash13 } = __require("crypto");
76021
+ var { createHash: createHash14 } = __require("crypto");
75700
76022
  var extension3 = require_extension();
75701
76023
  var PerMessageDeflate2 = require_permessage_deflate();
75702
76024
  var subprotocol2 = require_subprotocol();
@@ -76003,7 +76325,7 @@ var require_websocket_server = __commonJS({
76003
76325
  );
76004
76326
  }
76005
76327
  if (this._state > RUNNING) return abortHandshake(socket, 503);
76006
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
76328
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
76007
76329
  const headers = [
76008
76330
  "HTTP/1.1 101 Switching Protocols",
76009
76331
  "Upgrade: websocket",
@@ -142847,6 +143169,186 @@ var init_video_jobs = __esm({
142847
143169
  }
142848
143170
  });
142849
143171
 
143172
+ // src/memory/feedback-model.ts
143173
+ function feedbackWeightMultiplier(weight) {
143174
+ return Math.max(0.35, Math.min(1.4, 0.35 + Math.max(0, Math.min(2, weight)) * 0.525));
143175
+ }
143176
+ var init_feedback_model = __esm({
143177
+ "src/memory/feedback-model.ts"() {
143178
+ "use strict";
143179
+ init_esm_shims();
143180
+ }
143181
+ });
143182
+
143183
+ // src/memory/feedback.ts
143184
+ var feedback_exports = {};
143185
+ __export(feedback_exports, {
143186
+ FEEDBACK_DELTAS: () => FEEDBACK_DELTAS,
143187
+ MemoryFeedbackStore: () => MemoryFeedbackStore,
143188
+ applyMemoryFeedback: () => applyMemoryFeedback,
143189
+ feedbackWeightMultiplier: () => feedbackWeightMultiplier
143190
+ });
143191
+ import { randomUUID as randomUUID7 } from "crypto";
143192
+ function applyMemoryFeedback(current, event) {
143193
+ const delta = event.signal === "revoke" ? 0 : FEEDBACK_DELTAS[event.signal];
143194
+ const weight = Math.max(0, Math.min(2, current.weight + delta));
143195
+ const status = (event.signal === "code-conflict" || event.signal === "user-correction" || event.signal === "weaken") && weight < 0.5 ? "archived" : current.status;
143196
+ return { weight, status, audit: [...current.audit, { ...event }] };
143197
+ }
143198
+ function rowToEvent(row) {
143199
+ return {
143200
+ id: String(row.id),
143201
+ projectId: String(row.project_id),
143202
+ candidateKind: row.candidate_kind,
143203
+ candidateId: String(row.candidate_id),
143204
+ signal: row.signal,
143205
+ ...row.observed_at ? { at: String(row.observed_at) } : { at: (/* @__PURE__ */ new Date()).toISOString() },
143206
+ ...row.actor ? { actor: String(row.actor) } : {},
143207
+ ...row.note ? { note: String(row.note) } : {},
143208
+ ...row.source_ref ? { sourceRef: String(row.source_ref) } : {},
143209
+ ...row.target_event_id ? { targetEventId: String(row.target_event_id) } : {},
143210
+ delta: Number(row.delta ?? 0)
143211
+ };
143212
+ }
143213
+ var FEEDBACK_DELTAS, MemoryFeedbackStore;
143214
+ var init_feedback = __esm({
143215
+ "src/memory/feedback.ts"() {
143216
+ "use strict";
143217
+ init_esm_shims();
143218
+ init_sqlite_db();
143219
+ init_feedback_model();
143220
+ FEEDBACK_DELTAS = {
143221
+ "user-correction": -0.35,
143222
+ "verification-success": 0.25,
143223
+ "verification-failure": -0.25,
143224
+ "code-change": -0.15,
143225
+ used: 0.1,
143226
+ "not-used": -0.05,
143227
+ "code-conflict": -0.4,
143228
+ strengthen: 0.2,
143229
+ weaken: -0.2
143230
+ };
143231
+ MemoryFeedbackStore = class {
143232
+ db = null;
143233
+ async init(dataDir) {
143234
+ this.db = getDatabase(dataDir);
143235
+ }
143236
+ requireDb() {
143237
+ if (!this.db) throw new Error("MemoryFeedbackStore is not initialized.");
143238
+ return this.db;
143239
+ }
143240
+ loadEvents(projectId, candidateKind, candidateId) {
143241
+ return this.requireDb().prepare(`
143242
+ SELECT * FROM memory_feedback_events
143243
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143244
+ ORDER BY observed_at ASC, rowid ASC
143245
+ `).all(projectId, candidateKind, candidateId).map(rowToEvent);
143246
+ }
143247
+ rebuild(projectId, candidateKind, candidateId) {
143248
+ const events = this.loadEvents(projectId, candidateKind, candidateId);
143249
+ const revoked = new Set(events.filter((event) => event.signal === "revoke" && event.targetEventId).map((event) => event.targetEventId));
143250
+ let state = { weight: 1, status: "active", audit: [] };
143251
+ for (const event of events) {
143252
+ if (event.signal === "revoke" || revoked.has(event.id)) {
143253
+ state.audit.push(event);
143254
+ continue;
143255
+ }
143256
+ state = applyMemoryFeedback(state, event);
143257
+ }
143258
+ this.requireDb().prepare(`
143259
+ INSERT INTO memory_feedback_states (project_id, candidate_kind, candidate_id, weight, lifecycle_status, updated_at)
143260
+ VALUES (?, ?, ?, ?, ?, ?)
143261
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
143262
+ weight = excluded.weight, lifecycle_status = excluded.lifecycle_status, updated_at = excluded.updated_at
143263
+ `).run(projectId, candidateKind, candidateId, state.weight, state.status, (/* @__PURE__ */ new Date()).toISOString());
143264
+ return state;
143265
+ }
143266
+ getState(projectId, candidateKind, candidateId) {
143267
+ const row = this.requireDb().prepare(`
143268
+ SELECT weight, lifecycle_status FROM memory_feedback_states
143269
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143270
+ `).get(projectId, candidateKind, candidateId);
143271
+ if (!row) return { weight: 1, status: "active", audit: [] };
143272
+ return {
143273
+ weight: Number(row.weight ?? 1),
143274
+ status: row.lifecycle_status ?? "active",
143275
+ audit: this.loadEvents(projectId, candidateKind, candidateId)
143276
+ };
143277
+ }
143278
+ listStates(projectId, candidateKind = "observation") {
143279
+ return this.requireDb().prepare(`
143280
+ SELECT candidate_id, weight, lifecycle_status, updated_at
143281
+ FROM memory_feedback_states WHERE project_id = ? AND candidate_kind = ?
143282
+ ORDER BY updated_at DESC
143283
+ `).all(projectId, candidateKind).map((row) => ({
143284
+ candidateId: String(row.candidate_id),
143285
+ weight: Number(row.weight),
143286
+ status: row.lifecycle_status,
143287
+ updatedAt: String(row.updated_at)
143288
+ }));
143289
+ }
143290
+ weights(projectId, candidateKind = "observation") {
143291
+ const states = /* @__PURE__ */ new Map();
143292
+ for (const row of this.listStates(projectId, candidateKind)) {
143293
+ states.set(row.candidateId, this.getState(projectId, candidateKind, row.candidateId));
143294
+ }
143295
+ return states;
143296
+ }
143297
+ record(input) {
143298
+ if (input.signal === "revoke" && !input.targetEventId) {
143299
+ throw new Error("A revoke feedback event requires targetEventId.");
143300
+ }
143301
+ if (input.signal === "revoke") {
143302
+ const target = this.requireDb().prepare(`
143303
+ SELECT 1 FROM memory_feedback_events
143304
+ WHERE id = ? AND project_id = ? AND candidate_kind = ? AND candidate_id = ?
143305
+ `).get(input.targetEventId, input.projectId, input.candidateKind, input.candidateId);
143306
+ if (!target) throw new Error("The feedback event to revoke was not found in the same candidate scope.");
143307
+ }
143308
+ const event = {
143309
+ id: randomUUID7(),
143310
+ projectId: input.projectId,
143311
+ candidateKind: input.candidateKind,
143312
+ candidateId: input.candidateId,
143313
+ signal: input.signal,
143314
+ sourceRef: input.sourceRef,
143315
+ ...input.actor ? { actor: input.actor } : {},
143316
+ ...input.note ? { note: input.note.slice(0, 2e3) } : {},
143317
+ ...input.targetEventId ? { targetEventId: input.targetEventId } : {},
143318
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
143319
+ delta: input.signal === "revoke" ? 0 : FEEDBACK_DELTAS[input.signal]
143320
+ };
143321
+ this.requireDb().prepare(`
143322
+ INSERT INTO memory_feedback_events (
143323
+ id, project_id, candidate_kind, candidate_id, signal, actor, note,
143324
+ source_ref, target_event_id, delta, observed_at
143325
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
143326
+ `).run(
143327
+ event.id,
143328
+ event.projectId,
143329
+ event.candidateKind,
143330
+ event.candidateId,
143331
+ event.signal,
143332
+ event.actor ?? null,
143333
+ event.note ?? null,
143334
+ event.sourceRef,
143335
+ event.targetEventId ?? null,
143336
+ event.delta,
143337
+ event.at
143338
+ );
143339
+ return { event, state: this.rebuild(input.projectId, input.candidateKind, input.candidateId) };
143340
+ }
143341
+ audit(projectId, candidateKind, candidateId, limit2 = 100) {
143342
+ return this.requireDb().prepare(`
143343
+ SELECT * FROM memory_feedback_events
143344
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143345
+ ORDER BY observed_at DESC, rowid DESC LIMIT ?
143346
+ `).all(projectId, candidateKind, candidateId, Math.min(200, Math.max(1, Math.floor(limit2)))).map(rowToEvent);
143347
+ }
143348
+ };
143349
+ }
143350
+ });
143351
+
142850
143352
  // src/memory/retention.ts
142851
143353
  var retention_exports = {};
142852
143354
  __export(retention_exports, {
@@ -142888,6 +143390,7 @@ function getEffectiveRetentionDays(doc) {
142888
143390
  }
142889
143391
  function isImmune(doc) {
142890
143392
  if (doc.type === "probe") return false;
143393
+ if (doc.feedbackStatus === "archived") return false;
142891
143394
  if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
142892
143395
  if (doc.valueCategory === "core") return true;
142893
143396
  const importance = getImportanceLevel(doc);
@@ -142920,7 +143423,8 @@ function calculateRelevance(doc, referenceTime) {
142920
143423
  const decayFactor = Math.exp(-ageDays / retention);
142921
143424
  const accessCount = doc.accessCount ?? 0;
142922
143425
  const accessBoost = Math.min(2, 1 + 0.1 * accessCount);
142923
- let totalScore = base * decayFactor * accessBoost;
143426
+ const feedbackMultiplier = doc.feedbackWeight === void 0 ? 1 : feedbackWeightMultiplier(doc.feedbackWeight);
143427
+ let totalScore = base * decayFactor * accessBoost * feedbackMultiplier;
142924
143428
  const immune = isImmune(doc);
142925
143429
  if (immune) {
142926
143430
  totalScore = Math.max(totalScore, 0.5);
@@ -142932,7 +143436,8 @@ function calculateRelevance(doc, referenceTime) {
142932
143436
  decayFactor,
142933
143437
  accessBoost,
142934
143438
  ageDays,
142935
- isImmune: immune
143439
+ isImmune: immune,
143440
+ feedbackMultiplier
142936
143441
  };
142937
143442
  }
142938
143443
  function rankByRelevance(docs, referenceTime) {
@@ -142940,6 +143445,7 @@ function rankByRelevance(docs, referenceTime) {
142940
143445
  }
142941
143446
  function getRetentionZone(doc, referenceTime) {
142942
143447
  const now3 = referenceTime ?? /* @__PURE__ */ new Date();
143448
+ if (doc.feedbackStatus === "archived") return "archive-candidate";
142943
143449
  const importance = getImportanceLevel(doc);
142944
143450
  const retention = getEffectiveRetentionDays(doc);
142945
143451
  const createdAt = new Date(doc.createdAt);
@@ -143011,7 +143517,7 @@ function explainRetention(doc, referenceTime) {
143011
143517
  summary: parts.join(" ; ")
143012
143518
  };
143013
143519
  }
143014
- function toRetentionDocument(obs, accessMap) {
143520
+ function toRetentionDocument(obs, accessMap, feedback) {
143015
143521
  const access2 = accessMap?.get(obs.id);
143016
143522
  return {
143017
143523
  id: `obs-${obs.id}`,
@@ -143033,11 +143539,12 @@ function toRetentionDocument(obs, accessMap) {
143033
143539
  sourceDetail: obs.sourceDetail ?? "",
143034
143540
  valueCategory: obs.valueCategory ?? "",
143035
143541
  admissionState: obs.admissionState ?? "",
143036
- admissionReason: obs.admissionReason ?? ""
143542
+ admissionReason: obs.admissionReason ?? "",
143543
+ ...feedback ? { feedbackWeight: feedback.weight, feedbackStatus: feedback.status } : {}
143037
143544
  };
143038
143545
  }
143039
143546
  function projectObservationRetention(observation, options = {}) {
143040
- const document2 = toRetentionDocument(observation, options.accessMap);
143547
+ const document2 = toRetentionDocument(observation, options.accessMap, options.feedback);
143041
143548
  const relevance = calculateRelevance(document2, options.referenceTime);
143042
143549
  const explanation = explainRetention(document2, options.referenceTime);
143043
143550
  return {
@@ -143062,7 +143569,17 @@ function summarizeRetentionProjections(projections) {
143062
143569
  }
143063
143570
  return { active, stale, archiveCandidates, immune };
143064
143571
  }
143065
- async function archiveExpiredBatch(_projectDir, options) {
143572
+ async function loadFeedbackStore(dataDir) {
143573
+ try {
143574
+ const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
143575
+ const store = new MemoryFeedbackStore2();
143576
+ await store.init(dataDir);
143577
+ return store;
143578
+ } catch {
143579
+ return void 0;
143580
+ }
143581
+ }
143582
+ async function archiveExpiredBatch(projectDir2, options) {
143066
143583
  const store = getObservationStore();
143067
143584
  const limit2 = Math.min(500, Math.max(1, Math.floor(options.limit ?? 100)));
143068
143585
  const afterId = Math.max(0, Math.floor(options.afterId ?? 0));
@@ -143071,11 +143588,13 @@ async function archiveExpiredBatch(_projectDir, options) {
143071
143588
  afterId,
143072
143589
  limit: limit2 + 1
143073
143590
  });
143591
+ const feedback = await loadFeedbackStore(projectDir2);
143074
143592
  const hasMore = page.length > limit2;
143075
143593
  const scanned = hasMore ? page.slice(0, limit2) : page;
143076
143594
  const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => projectObservationRetention(observation, {
143077
143595
  accessMap: options.accessMap,
143078
- referenceTime: options.referenceTime
143596
+ referenceTime: options.referenceTime,
143597
+ ...feedback ? { feedback: feedback.getState(options.projectId, "observation", String(observation.id)) } : {}
143079
143598
  }).zone === "archive-candidate").map((observation) => observation.id);
143080
143599
  const archived = candidateIds.length === 0 ? 0 : await store.atomic(async (tx) => {
143081
143600
  const updates = await Promise.all(
@@ -143106,6 +143625,7 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
143106
143625
  const remaining = reader ? remainingObservations.filter((observation) => canManageObservation(observation, reader)).length : remainingObservations.length;
143107
143626
  return { archived, remaining };
143108
143627
  }
143628
+ const feedback = await loadFeedbackStore(projectDir2);
143109
143629
  return await store.atomic(async (tx) => {
143110
143630
  const allObs = await tx.loadAll();
143111
143631
  const activeObs = allObs.filter(
@@ -143113,7 +143633,11 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
143113
143633
  );
143114
143634
  const archivedIds = [];
143115
143635
  for (const obs of activeObs) {
143116
- const retention = projectObservationRetention(obs, { accessMap, referenceTime });
143636
+ const retention = projectObservationRetention(obs, {
143637
+ accessMap,
143638
+ referenceTime,
143639
+ ...feedback ? { feedback: feedback.getState(obs.projectId, "observation", String(obs.id)) } : {}
143640
+ });
143117
143641
  if (retention.zone === "archive-candidate") {
143118
143642
  archivedIds.push(obs.id);
143119
143643
  }
@@ -143133,6 +143657,7 @@ var init_retention = __esm({
143133
143657
  init_esm_shims();
143134
143658
  init_obs_store();
143135
143659
  init_visibility();
143660
+ init_feedback_model();
143136
143661
  RETENTION_DAYS = {
143137
143662
  critical: 365,
143138
143663
  high: 180,
@@ -143169,7 +143694,7 @@ var init_retention = __esm({
143169
143694
  });
143170
143695
 
143171
143696
  // src/memory/owner.ts
143172
- import { randomUUID as randomUUID6 } from "crypto";
143697
+ import { randomUUID as randomUUID8 } from "crypto";
143173
143698
  import { promises as fs9 } from "fs";
143174
143699
  import path15 from "path";
143175
143700
  function ownerPath(dataDir) {
@@ -143199,7 +143724,7 @@ async function resolveLocalMemoryOwner(dataDir, options = {}) {
143199
143724
  await fs9.mkdir(path15.dirname(filePath), { recursive: true });
143200
143725
  const created = {
143201
143726
  version: 1,
143202
- id: "local-" + randomUUID6(),
143727
+ id: "local-" + randomUUID8(),
143203
143728
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
143204
143729
  };
143205
143730
  try {
@@ -143222,7 +143747,7 @@ var init_owner = __esm({
143222
143747
  });
143223
143748
 
143224
143749
  // src/memory/long-term-store.ts
143225
- import { randomUUID as randomUUID7 } from "crypto";
143750
+ import { randomUUID as randomUUID9 } from "crypto";
143226
143751
  function optionalText2(value) {
143227
143752
  return typeof value === "string" && value.trim() ? value : void 0;
143228
143753
  }
@@ -143273,7 +143798,7 @@ function rowToEvidence(row) {
143273
143798
  createdAt: row.createdAt
143274
143799
  };
143275
143800
  }
143276
- function rowToEvent(row) {
143801
+ function rowToEvent2(row) {
143277
143802
  return {
143278
143803
  id: row.id,
143279
143804
  memoryId: row.memoryId,
@@ -143382,7 +143907,7 @@ var init_long_term_store = __esm({
143382
143907
  }
143383
143908
  insertEvidence(input) {
143384
143909
  const evidence = {
143385
- id: randomUUID7(),
143910
+ id: randomUUID9(),
143386
143911
  ...input,
143387
143912
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
143388
143913
  };
@@ -143409,7 +143934,7 @@ var init_long_term_store = __esm({
143409
143934
  }
143410
143935
  recordEvent(input) {
143411
143936
  const event = {
143412
- id: randomUUID7(),
143937
+ id: randomUUID9(),
143413
143938
  ...input,
143414
143939
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
143415
143940
  };
@@ -143430,7 +143955,7 @@ var init_long_term_store = __esm({
143430
143955
  listEvents(memoryId) {
143431
143956
  return this.db.prepare(`
143432
143957
  SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
143433
- `).all(memoryId).map(rowToEvent);
143958
+ `).all(memoryId).map(rowToEvent2);
143434
143959
  }
143435
143960
  markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
143436
143961
  const unique = [...new Set(ids.filter(Boolean))];
@@ -143499,7 +144024,7 @@ __export(long_term_exports, {
143499
144024
  selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
143500
144025
  supersedeLongTermMemory: () => supersedeLongTermMemory
143501
144026
  });
143502
- import { randomUUID as randomUUID8 } from "crypto";
144027
+ import { randomUUID as randomUUID10 } from "crypto";
143503
144028
  function isOneOf(value, values) {
143504
144029
  return typeof value === "string" && values.includes(value);
143505
144030
  }
@@ -143681,7 +144206,7 @@ async function createManualLongTermMemory(input) {
143681
144206
  const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
143682
144207
  if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
143683
144208
  assertScope(input.scope);
143684
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
144209
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
143685
144210
  return createLongTermMemoryWithDataDir({
143686
144211
  dataDir: input.dataDir,
143687
144212
  originProjectId: input.projectId,
@@ -143736,7 +144261,7 @@ async function createLongTermMemoryWithDataDir(input) {
143736
144261
  assertPortableEvidence(input.scope, portability, evidenceInputs);
143737
144262
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
143738
144263
  const memory = {
143739
- id: randomUUID8(),
144264
+ id: randomUUID10(),
143740
144265
  originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
143741
144266
  ownerId: compactText(input.ownerId, "owner id", 160),
143742
144267
  scope: input.scope,
@@ -144054,9 +144579,9 @@ var init_long_term = __esm({
144054
144579
  });
144055
144580
 
144056
144581
  // src/codegraph/ids.ts
144057
- import { createHash as createHash5 } from "crypto";
144582
+ import { createHash as createHash6 } from "crypto";
144058
144583
  function digest(input) {
144059
- return createHash5("sha256").update(input).digest("hex").slice(0, 16);
144584
+ return createHash6("sha256").update(input).digest("hex").slice(0, 16);
144060
144585
  }
144061
144586
  function normalizeCodePath(path22) {
144062
144587
  return path22.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -144091,7 +144616,7 @@ var store_exports = {};
144091
144616
  __export(store_exports, {
144092
144617
  CodeGraphStore: () => CodeGraphStore
144093
144618
  });
144094
- import { randomUUID as randomUUID9 } from "crypto";
144619
+ import { randomUUID as randomUUID11 } from "crypto";
144095
144620
  import { posix as pathPosix } from "path";
144096
144621
  function rowToFile(row) {
144097
144622
  return {
@@ -144808,7 +145333,7 @@ var init_store = __esm({
144808
145333
  * so an interrupted scan cannot be advertised as complete.
144809
145334
  */
144810
145335
  recordCodeStateSnapshot(input) {
144811
- const id = randomUUID9();
145336
+ const id = randomUUID11();
144812
145337
  let snapshot;
144813
145338
  const tx = this.db.transaction(() => {
144814
145339
  const previous = this.db.prepare(
@@ -144886,7 +145411,7 @@ var init_store = __esm({
144886
145411
 
144887
145412
  // src/codegraph/code-state.ts
144888
145413
  import { execFile as execFile2 } from "child_process";
144889
- import { createHash as createHash6 } from "crypto";
145414
+ import { createHash as createHash7 } from "crypto";
144890
145415
  import { promisify as promisify3 } from "util";
144891
145416
  async function runGit(rootPath, args) {
144892
145417
  try {
@@ -144915,7 +145440,7 @@ function changedPathCount(status) {
144915
145440
  return count2;
144916
145441
  }
144917
145442
  function fingerprint(baseRevision, status) {
144918
- return createHash6("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
145443
+ return createHash7("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
144919
145444
  }
144920
145445
  async function collectCodeStateSnapshot(input) {
144921
145446
  const [revision, status] = await Promise.all([
@@ -145015,11 +145540,11 @@ __export(lite_provider_exports, {
145015
145540
  indexProjectLite: () => indexProjectLite,
145016
145541
  refreshProjectLite: () => refreshProjectLite
145017
145542
  });
145018
- import { createHash as createHash7 } from "crypto";
145543
+ import { createHash as createHash8 } from "crypto";
145019
145544
  import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
145020
145545
  import { join as join4, relative } from "path";
145021
145546
  function hashText(text2) {
145022
- return createHash7("sha256").update(text2).digest("hex");
145547
+ return createHash8("sha256").update(text2).digest("hex");
145023
145548
  }
145024
145549
  function extension2(path22) {
145025
145550
  const index = path22.lastIndexOf(".");
@@ -145586,7 +146111,7 @@ var claim_store_exports = {};
145586
146111
  __export(claim_store_exports, {
145587
146112
  ClaimStore: () => ClaimStore
145588
146113
  });
145589
- import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
146114
+ import { createHash as createHash9, randomUUID as randomUUID12 } from "crypto";
145590
146115
  function optionalText3(value) {
145591
146116
  return typeof value === "string" && value ? value : void 0;
145592
146117
  }
@@ -145625,7 +146150,7 @@ function rowToEvidence2(row) {
145625
146150
  createdAt: row.createdAt
145626
146151
  };
145627
146152
  }
145628
- function rowToEvent2(row) {
146153
+ function rowToEvent3(row) {
145629
146154
  return {
145630
146155
  id: row.id,
145631
146156
  projectId: row.projectId,
@@ -145639,7 +146164,7 @@ function rowToEvent2(row) {
145639
146164
  };
145640
146165
  }
145641
146166
  function evidenceKey(input) {
145642
- return createHash8("sha256").update(JSON.stringify([
146167
+ return createHash9("sha256").update(JSON.stringify([
145643
146168
  input.evidenceKind,
145644
146169
  input.evidenceId,
145645
146170
  input.relation,
@@ -145770,7 +146295,7 @@ var init_claim_store = __esm({
145770
146295
  locator, capturedHash, evidenceKey, createdAt
145771
146296
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
145772
146297
  `).run(
145773
- randomUUID10(),
146298
+ randomUUID12(),
145774
146299
  input.claimId,
145775
146300
  input.evidenceKind,
145776
146301
  input.evidenceId,
@@ -145792,7 +146317,7 @@ var init_claim_store = __esm({
145792
146317
  }
145793
146318
  recordEvent(input) {
145794
146319
  const event = {
145795
- id: randomUUID10(),
146320
+ id: randomUUID12(),
145796
146321
  ...input,
145797
146322
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
145798
146323
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -145820,7 +146345,7 @@ var init_claim_store = __esm({
145820
146345
  SELECT * FROM knowledge_claim_events
145821
146346
  WHERE claimId = ?
145822
146347
  ORDER BY createdAt ASC, id
145823
- `).all(claimId).map(rowToEvent2);
146348
+ `).all(claimId).map(rowToEvent3);
145824
146349
  }
145825
146350
  };
145826
146351
  }
@@ -145837,7 +146362,7 @@ __export(claims_exports, {
145837
146362
  supersedeClaim: () => supersedeClaim,
145838
146363
  writeClaim: () => writeClaim
145839
146364
  });
145840
- import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
146365
+ import { createHash as createHash10, randomUUID as randomUUID13 } from "crypto";
145841
146366
  function compactText2(value, field) {
145842
146367
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
145843
146368
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -145849,7 +146374,7 @@ function normalizedTerm(value) {
145849
146374
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
145850
146375
  }
145851
146376
  function hashIdentity(parts) {
145852
- return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
146377
+ return createHash10("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
145853
146378
  }
145854
146379
  function buildClaimIdentity(input) {
145855
146380
  const subject = normalizedTerm(input.subject);
@@ -145923,7 +146448,7 @@ function normalizeClaimInput(input) {
145923
146448
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
145924
146449
  return {
145925
146450
  claim: {
145926
- id: randomUUID11(),
146451
+ id: randomUUID13(),
145927
146452
  projectId,
145928
146453
  subject,
145929
146454
  predicate,
@@ -146090,7 +146615,7 @@ function supersedeClaim(store, input) {
146090
146615
  });
146091
146616
  }
146092
146617
  function hashObservation(observation) {
146093
- return createHash9("sha256").update(JSON.stringify([
146618
+ return createHash10("sha256").update(JSON.stringify([
146094
146619
  observation.title,
146095
146620
  observation.narrative,
146096
146621
  observation.facts,
@@ -146334,11 +146859,11 @@ var init_frontmatter = __esm({
146334
146859
  });
146335
146860
 
146336
146861
  // src/knowledge/markdown.ts
146337
- import { createHash as createHash10 } from "crypto";
146862
+ import { createHash as createHash11 } from "crypto";
146338
146863
  import { promises as fs10 } from "fs";
146339
146864
  import path16 from "path";
146340
146865
  function hashContent(content) {
146341
- return createHash10("sha256").update(content).digest("hex");
146866
+ return createHash11("sha256").update(content).digest("hex");
146342
146867
  }
146343
146868
  function stringField(data, key) {
146344
146869
  const value = data[key];
@@ -146464,11 +146989,11 @@ __export(wiki_exports, {
146464
146989
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
146465
146990
  readKnowledgePage: () => readKnowledgePage
146466
146991
  });
146467
- import { createHash as createHash11 } from "crypto";
146992
+ import { createHash as createHash12 } from "crypto";
146468
146993
  import { promises as fs11 } from "fs";
146469
146994
  import path17 from "path";
146470
146995
  function hash2(value) {
146471
- return createHash11("sha256").update(value).digest("hex");
146996
+ return createHash12("sha256").update(value).digest("hex");
146472
146997
  }
146473
146998
  function now() {
146474
146999
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -147008,7 +147533,7 @@ var init_workflow_sync = __esm({
147008
147533
  });
147009
147534
 
147010
147535
  // src/knowledge/workflow-store.ts
147011
- import { randomUUID as randomUUID12 } from "crypto";
147536
+ import { randomUUID as randomUUID14 } from "crypto";
147012
147537
  function parseJson(value, fallback) {
147013
147538
  if (typeof value !== "string" || !value) return fallback;
147014
147539
  try {
@@ -147108,7 +147633,7 @@ var init_workflow_store = __esm({
147108
147633
  }
147109
147634
  recordRun(input) {
147110
147635
  const run = {
147111
- id: randomUUID12(),
147636
+ id: randomUUID14(),
147112
147637
  workflowId: input.workflowId,
147113
147638
  projectId: input.projectId,
147114
147639
  task: input.task.trim(),
@@ -147176,7 +147701,7 @@ var init_outcome_types = __esm({
147176
147701
  });
147177
147702
 
147178
147703
  // src/knowledge/outcome-store.ts
147179
- import { randomUUID as randomUUID13 } from "crypto";
147704
+ import { randomUUID as randomUUID15 } from "crypto";
147180
147705
  function optionalText5(value) {
147181
147706
  return typeof value === "string" && value.trim() ? value : void 0;
147182
147707
  }
@@ -147220,7 +147745,7 @@ var init_outcome_store = __esm({
147220
147745
  }
147221
147746
  record(input) {
147222
147747
  const signal = {
147223
- id: randomUUID13(),
147748
+ id: randomUUID15(),
147224
147749
  projectId: text(input.projectId, "project id"),
147225
147750
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
147226
147751
  candidateId: text(input.candidateId, "candidate id"),
@@ -147325,11 +147850,11 @@ __export(workflows_exports, {
147325
147850
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
147326
147851
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
147327
147852
  });
147328
- import { createHash as createHash12 } from "crypto";
147853
+ import { createHash as createHash13 } from "crypto";
147329
147854
  import { promises as fs12 } from "fs";
147330
147855
  import path18 from "path";
147331
147856
  function hash3(value) {
147332
- return createHash12("sha256").update(value).digest("hex");
147857
+ return createHash13("sha256").update(value).digest("hex");
147333
147858
  }
147334
147859
  function now2() {
147335
147860
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -148711,6 +149236,16 @@ async function bindObservationCodeRefsBestEffort(observation) {
148711
149236
  } catch {
148712
149237
  }
148713
149238
  }
149239
+ async function persistEvidenceCard(observation) {
149240
+ if (!projectDir) return;
149241
+ try {
149242
+ const evidence = new EvidenceCardStore();
149243
+ await evidence.init(projectDir);
149244
+ evidence.upsertObservation(observation);
149245
+ } catch (error) {
149246
+ console.error(`[memorix] Evidence card projection failed for obs-${observation.id}: ${error instanceof Error ? error.message : String(error)}`);
149247
+ }
149248
+ }
148714
149249
  function queueClaimDerivation(observation) {
148715
149250
  const dataDir = projectDir;
148716
149251
  const isExplicit = observation.sourceDetail === "explicit";
@@ -149057,6 +149592,7 @@ async function storeObservation(input) {
149057
149592
  }
149058
149593
  if (!persistedCallbackInvoked) await input.onPersisted?.(observation);
149059
149594
  await bindObservationCodeRefsBestEffort(observation);
149595
+ await persistEvidenceCard(observation);
149060
149596
  queueObservationQualification(observation);
149061
149597
  if (resolveObservationVisibility(observation) === "project") {
149062
149598
  queueClaimDerivation(observation);
@@ -149149,6 +149685,7 @@ async function upsertObservation(existing, input, now3) {
149149
149685
  await store.update(existing);
149150
149686
  }
149151
149687
  await bindObservationCodeRefsBestEffort(existing);
149688
+ await persistEvidenceCard(existing);
149152
149689
  queueObservationQualification(existing);
149153
149690
  if (resolveObservationVisibility(existing) === "project") {
149154
149691
  queueClaimDerivation(existing);
@@ -149282,6 +149819,7 @@ async function resolveObservations(ids, status = "resolved") {
149282
149819
  await Promise.all(changed.map((observation) => tx.update(observation)));
149283
149820
  });
149284
149821
  }
149822
+ for (const observation of changed) await persistEvidenceCard(observation);
149285
149823
  return { resolved, notFound };
149286
149824
  }
149287
149825
  function getProjectObservations(projectId) {
@@ -149619,6 +150157,7 @@ var init_observations = __esm({
149619
150157
  init_secret_filter();
149620
150158
  init_lifecycle();
149621
150159
  init_visibility();
150160
+ init_evidence_store();
149622
150161
  observations = [];
149623
150162
  nextId = 1;
149624
150163
  projectDir = null;