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
@@ -338,7 +338,7 @@ function closeAllDatabases() {
338
338
  _dbCache.delete(key);
339
339
  }
340
340
  }
341
- 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;
341
+ 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;
342
342
  var init_sqlite_db = __esm({
343
343
  "src/store/sqlite-db.ts"() {
344
344
  "use strict";
@@ -984,6 +984,79 @@ CREATE TABLE IF NOT EXISTS compaction_checkpoints (
984
984
  created_at TEXT NOT NULL,
985
985
  updated_at TEXT NOT NULL
986
986
  );
987
+ `;
988
+ CREATE_EVIDENCE_CARDS_TABLE = `
989
+ CREATE TABLE IF NOT EXISTS evidence_cards (
990
+ id TEXT PRIMARY KEY,
991
+ project_id TEXT NOT NULL,
992
+ candidate_kind TEXT NOT NULL,
993
+ candidate_id TEXT NOT NULL,
994
+ title TEXT NOT NULL,
995
+ summary TEXT NOT NULL DEFAULT '',
996
+ entity_name TEXT,
997
+ source_kind TEXT NOT NULL,
998
+ source_ref TEXT NOT NULL,
999
+ locator TEXT,
1000
+ captured_hash TEXT,
1001
+ session_id TEXT,
1002
+ status TEXT NOT NULL DEFAULT 'active',
1003
+ verification TEXT NOT NULL DEFAULT 'unverified',
1004
+ freshness TEXT NOT NULL DEFAULT 'current',
1005
+ stale_reason TEXT,
1006
+ files_json TEXT NOT NULL DEFAULT '[]',
1007
+ related_entities_json TEXT NOT NULL DEFAULT '[]',
1008
+ created_at TEXT NOT NULL,
1009
+ updated_at TEXT NOT NULL,
1010
+ UNIQUE(project_id, candidate_kind, candidate_id)
1011
+ );
1012
+ `;
1013
+ CREATE_EVIDENCE_CARD_EVENTS_TABLE = `
1014
+ CREATE TABLE IF NOT EXISTS evidence_card_events (
1015
+ id TEXT PRIMARY KEY,
1016
+ project_id TEXT NOT NULL,
1017
+ card_id TEXT NOT NULL,
1018
+ kind TEXT NOT NULL,
1019
+ detail TEXT,
1020
+ created_at TEXT NOT NULL,
1021
+ FOREIGN KEY (card_id) REFERENCES evidence_cards(id) ON DELETE CASCADE
1022
+ );
1023
+ `;
1024
+ CREATE_MEMORY_FEEDBACK_STATES_TABLE = `
1025
+ CREATE TABLE IF NOT EXISTS memory_feedback_states (
1026
+ project_id TEXT NOT NULL,
1027
+ candidate_kind TEXT NOT NULL,
1028
+ candidate_id TEXT NOT NULL,
1029
+ weight REAL NOT NULL DEFAULT 1.0,
1030
+ lifecycle_status TEXT NOT NULL DEFAULT 'active',
1031
+ updated_at TEXT NOT NULL,
1032
+ PRIMARY KEY (project_id, candidate_kind, candidate_id)
1033
+ );
1034
+ `;
1035
+ CREATE_MEMORY_FEEDBACK_EVENTS_TABLE = `
1036
+ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1037
+ id TEXT PRIMARY KEY,
1038
+ project_id TEXT NOT NULL,
1039
+ candidate_kind TEXT NOT NULL,
1040
+ candidate_id TEXT NOT NULL,
1041
+ signal TEXT NOT NULL,
1042
+ actor TEXT,
1043
+ note TEXT,
1044
+ source_ref TEXT NOT NULL,
1045
+ target_event_id TEXT,
1046
+ delta REAL NOT NULL DEFAULT 0,
1047
+ observed_at TEXT NOT NULL
1048
+ );
1049
+ `;
1050
+ CREATE_MCP_BINDINGS_TABLE = `
1051
+ CREATE TABLE IF NOT EXISTS mcp_bindings (
1052
+ handle_id TEXT PRIMARY KEY,
1053
+ project_id TEXT NOT NULL,
1054
+ project_root TEXT NOT NULL,
1055
+ data_dir TEXT NOT NULL,
1056
+ created_at TEXT NOT NULL,
1057
+ last_used_at TEXT NOT NULL,
1058
+ expires_at TEXT
1059
+ );
987
1060
  `;
988
1061
  CREATE_INDEXES = `
989
1062
  CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
@@ -1046,6 +1119,12 @@ CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_target
1046
1119
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
1047
1120
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
1048
1121
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
1122
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC);
1123
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id);
1124
+ CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1125
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1126
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1127
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1049
1128
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1050
1129
  ON maintenance_jobs(project_id, kind, dedupe_key)
1051
1130
  WHERE status IN ('pending', 'running', 'retry');
@@ -1201,6 +1280,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1201
1280
  }
1202
1281
  db2.exec("CREATE INDEX IF NOT EXISTS idx_media_jobs_source_asset ON media_jobs(project_id, source_asset_id, status)");
1203
1282
  }
1283
+ },
1284
+ {
1285
+ id: "1.8-context-control-plane",
1286
+ apply: (db2) => {
1287
+ db2.exec(CREATE_EVIDENCE_CARDS_TABLE);
1288
+ db2.exec(CREATE_EVIDENCE_CARD_EVENTS_TABLE);
1289
+ db2.exec(CREATE_MEMORY_FEEDBACK_STATES_TABLE);
1290
+ db2.exec(CREATE_MEMORY_FEEDBACK_EVENTS_TABLE);
1291
+ db2.exec(CREATE_MCP_BINDINGS_TABLE);
1292
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC)");
1293
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id)");
1294
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at)");
1295
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1296
+ 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)");
1297
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1298
+ }
1204
1299
  }
1205
1300
  ];
1206
1301
  _dbCache = /* @__PURE__ */ new Map();
@@ -1246,6 +1341,8 @@ function loadEnvFile(filePath) {
1246
1341
  const parsed = parse(readFileSync2(filePath, "utf-8"));
1247
1342
  for (const [key, value] of Object.entries(parsed)) {
1248
1343
  if (!(key in process.env)) {
1344
+ loadedEnvKeys.add(key);
1345
+ loadedEnvValues.set(key, value);
1249
1346
  process.env[key] = value;
1250
1347
  injectedKeys.add(key);
1251
1348
  }
@@ -1258,6 +1355,8 @@ function loadDotenv(projectRoot, options = {}) {
1258
1355
  resetDotenv();
1259
1356
  }
1260
1357
  loadedEnvFiles.length = 0;
1358
+ loadedEnvKeys.clear();
1359
+ loadedEnvValues.clear();
1261
1360
  if (projectRoot) {
1262
1361
  loadEnvFile(getProjectDotenvPath(projectRoot));
1263
1362
  }
@@ -1273,11 +1372,12 @@ function resetDotenv() {
1273
1372
  dotenvLoaded = false;
1274
1373
  dotenvProjectRoot = null;
1275
1374
  loadedEnvFiles.length = 0;
1375
+ loadedEnvKeys.clear();
1276
1376
  }
1277
1377
  function getLoadedEnvFiles() {
1278
1378
  return loadedEnvFiles;
1279
1379
  }
1280
- var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, injectedKeys;
1380
+ var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
1281
1381
  var init_dotenv_loader = __esm({
1282
1382
  "src/config/dotenv-loader.ts"() {
1283
1383
  "use strict";
@@ -1286,6 +1386,8 @@ var init_dotenv_loader = __esm({
1286
1386
  dotenvLoaded = false;
1287
1387
  dotenvProjectRoot = null;
1288
1388
  loadedEnvFiles = [];
1389
+ loadedEnvKeys = /* @__PURE__ */ new Set();
1390
+ loadedEnvValues = /* @__PURE__ */ new Map();
1289
1391
  injectedKeys = /* @__PURE__ */ new Set();
1290
1392
  }
1291
1393
  });
@@ -7981,6 +8083,226 @@ var init_lifecycle = __esm({
7981
8083
  }
7982
8084
  });
7983
8085
 
8086
+ // src/store/evidence-store.ts
8087
+ import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
8088
+ function safeJson(value, fallback) {
8089
+ if (typeof value !== "string" || value.length === 0) return fallback;
8090
+ try {
8091
+ return JSON.parse(value);
8092
+ } catch {
8093
+ return fallback;
8094
+ }
8095
+ }
8096
+ function rowToCard(row) {
8097
+ return {
8098
+ id: String(row.id),
8099
+ projectId: String(row.project_id),
8100
+ candidateKind: row.candidate_kind,
8101
+ candidateId: String(row.candidate_id),
8102
+ title: String(row.title),
8103
+ summary: String(row.summary ?? ""),
8104
+ ...row.entity_name ? { entityName: String(row.entity_name) } : {},
8105
+ sourceKind: String(row.source_kind),
8106
+ sourceRef: String(row.source_ref),
8107
+ ...row.locator ? { locator: String(row.locator) } : {},
8108
+ ...row.captured_hash ? { capturedHash: String(row.captured_hash) } : {},
8109
+ ...row.session_id ? { sessionId: String(row.session_id) } : {},
8110
+ status: String(row.status ?? "active"),
8111
+ verification: row.verification,
8112
+ freshness: row.freshness,
8113
+ ...row.stale_reason ? { staleReason: String(row.stale_reason) } : {},
8114
+ files: safeJson(row.files_json, []),
8115
+ relatedEntities: safeJson(row.related_entities_json, []),
8116
+ createdAt: String(row.created_at),
8117
+ updatedAt: String(row.updated_at)
8118
+ };
8119
+ }
8120
+ function observationHash(observation) {
8121
+ return createHash3("sha256").update(JSON.stringify({
8122
+ projectId: observation.projectId,
8123
+ id: observation.id,
8124
+ title: observation.title,
8125
+ narrative: observation.narrative,
8126
+ facts: observation.facts,
8127
+ files: observation.filesModified,
8128
+ relatedEntities: observation.relatedEntities
8129
+ })).digest("hex");
8130
+ }
8131
+ function sourceKind(observation) {
8132
+ if (observation.source === "git" || observation.sourceDetail === "git-ingest") return "git";
8133
+ if (observation.sourceDetail === "hook") return "hook";
8134
+ return observation.source ?? "agent";
8135
+ }
8136
+ var EvidenceCardStore;
8137
+ var init_evidence_store = __esm({
8138
+ "src/store/evidence-store.ts"() {
8139
+ "use strict";
8140
+ init_esm_shims();
8141
+ init_sqlite_db();
8142
+ EvidenceCardStore = class {
8143
+ db = null;
8144
+ async init(dataDir) {
8145
+ this.db = getDatabase(dataDir);
8146
+ }
8147
+ requireDb() {
8148
+ if (!this.db) throw new Error("EvidenceCardStore is not initialized.");
8149
+ return this.db;
8150
+ }
8151
+ upsertObservation(observation) {
8152
+ const db2 = this.requireDb();
8153
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
8154
+ const card = {
8155
+ id: `evidence:observation:${observation.projectId}:${observation.id}`,
8156
+ projectId: observation.projectId,
8157
+ candidateKind: "observation",
8158
+ candidateId: String(observation.id),
8159
+ title: observation.title,
8160
+ summary: observation.narrative.slice(0, 500),
8161
+ entityName: observation.entityName,
8162
+ sourceKind: sourceKind(observation),
8163
+ sourceRef: `observation:${observation.id}`,
8164
+ locator: observation.filesModified?.join(", ") || `observation:${observation.id}`,
8165
+ capturedHash: observationHash(observation),
8166
+ ...observation.sessionId ? { sessionId: observation.sessionId } : {},
8167
+ status: observation.status ?? "active",
8168
+ verification: observation.status === "resolved" ? "verified" : "unverified",
8169
+ freshness: "current",
8170
+ files: observation.filesModified ?? [],
8171
+ relatedEntities: observation.relatedEntities ?? [],
8172
+ createdAt: observation.createdAt,
8173
+ updatedAt: now3
8174
+ };
8175
+ db2.prepare(`
8176
+ INSERT INTO evidence_cards (
8177
+ id, project_id, candidate_kind, candidate_id, title, summary, entity_name,
8178
+ source_kind, source_ref, locator, captured_hash, session_id, status,
8179
+ verification, freshness, stale_reason, files_json, related_entities_json,
8180
+ created_at, updated_at
8181
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?)
8182
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
8183
+ title = excluded.title,
8184
+ summary = excluded.summary,
8185
+ entity_name = excluded.entity_name,
8186
+ source_kind = excluded.source_kind,
8187
+ source_ref = excluded.source_ref,
8188
+ locator = excluded.locator,
8189
+ captured_hash = excluded.captured_hash,
8190
+ session_id = excluded.session_id,
8191
+ status = excluded.status,
8192
+ freshness = 'current',
8193
+ stale_reason = NULL,
8194
+ files_json = excluded.files_json,
8195
+ related_entities_json = excluded.related_entities_json,
8196
+ updated_at = excluded.updated_at
8197
+ `).run(
8198
+ card.id,
8199
+ card.projectId,
8200
+ card.candidateKind,
8201
+ card.candidateId,
8202
+ card.title,
8203
+ card.summary,
8204
+ card.entityName ?? null,
8205
+ card.sourceKind,
8206
+ card.sourceRef,
8207
+ card.locator ?? null,
8208
+ card.capturedHash ?? null,
8209
+ card.sessionId ?? null,
8210
+ card.status,
8211
+ card.verification,
8212
+ card.freshness,
8213
+ JSON.stringify(card.files),
8214
+ JSON.stringify(card.relatedEntities),
8215
+ card.createdAt,
8216
+ card.updatedAt
8217
+ );
8218
+ return this.get(card.projectId, card.candidateKind, card.candidateId) ?? card;
8219
+ }
8220
+ get(projectId, candidateKind, candidateId) {
8221
+ const row = this.requireDb().prepare(`
8222
+ SELECT * FROM evidence_cards WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
8223
+ `).get(projectId, candidateKind, candidateId);
8224
+ return row ? rowToCard(row) : void 0;
8225
+ }
8226
+ list(projectId, options = {}) {
8227
+ const limit2 = Math.min(100, Math.max(1, Math.floor(options.limit ?? 20)));
8228
+ const clauses = ["project_id = ?"];
8229
+ const args = [projectId];
8230
+ if (options.freshness) {
8231
+ clauses.push("freshness = ?");
8232
+ args.push(options.freshness);
8233
+ }
8234
+ if (options.status) {
8235
+ clauses.push("status = ?");
8236
+ args.push(options.status);
8237
+ }
8238
+ args.push(limit2);
8239
+ const rows = this.requireDb().prepare(`
8240
+ SELECT * FROM evidence_cards WHERE ${clauses.join(" AND ")} ORDER BY updated_at DESC, id DESC LIMIT ?
8241
+ `).all(...args);
8242
+ return rows.map(rowToCard);
8243
+ }
8244
+ syncObservations(observations2) {
8245
+ return observations2.map((observation) => this.upsertObservation(observation));
8246
+ }
8247
+ markStaleForPaths(projectId, paths, reason = "source file changed") {
8248
+ const normalized = new Set(paths.map((value) => value.replace(/\\/g, "/").toLowerCase()));
8249
+ if (normalized.size === 0) return 0;
8250
+ const db2 = this.requireDb();
8251
+ const rows = db2.prepare(`SELECT * FROM evidence_cards WHERE project_id = ? AND freshness != 'stale'`).all(projectId);
8252
+ const update2 = db2.prepare(`
8253
+ UPDATE evidence_cards SET freshness = 'stale', stale_reason = ?, updated_at = ?
8254
+ WHERE id = ?
8255
+ `);
8256
+ let changed = 0;
8257
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
8258
+ const run = db2.transaction(() => {
8259
+ for (const row of rows) {
8260
+ const files = safeJson(row.files_json, []);
8261
+ if (!files.some((file) => normalized.has(file.replace(/\\/g, "/").toLowerCase()))) continue;
8262
+ update2.run(reason, now3, row.id);
8263
+ changed++;
8264
+ }
8265
+ });
8266
+ run();
8267
+ return changed;
8268
+ }
8269
+ setVerification(projectId, cardId, verification) {
8270
+ this.requireDb().prepare(`
8271
+ UPDATE evidence_cards SET verification = ?, updated_at = ? WHERE project_id = ? AND id = ?
8272
+ `).run(verification, (/* @__PURE__ */ new Date()).toISOString(), projectId, cardId);
8273
+ }
8274
+ recordEvent(projectId, cardId, kind, detail) {
8275
+ const event = {
8276
+ id: randomUUID3(),
8277
+ projectId,
8278
+ cardId,
8279
+ kind,
8280
+ ...detail ? { detail: detail.slice(0, 2e3) } : {},
8281
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
8282
+ };
8283
+ this.requireDb().prepare(`
8284
+ INSERT INTO evidence_card_events (id, project_id, card_id, kind, detail, created_at)
8285
+ VALUES (?, ?, ?, ?, ?, ?)
8286
+ `).run(event.id, event.projectId, event.cardId, event.kind, event.detail ?? null, event.createdAt);
8287
+ return event;
8288
+ }
8289
+ listEvents(cardId, limit2 = 50) {
8290
+ const rows = this.requireDb().prepare(`
8291
+ SELECT * FROM evidence_card_events WHERE card_id = ? ORDER BY created_at DESC LIMIT ?
8292
+ `).all(cardId, Math.min(200, Math.max(1, Math.floor(limit2))));
8293
+ return rows.map((row) => ({
8294
+ id: String(row.id),
8295
+ projectId: String(row.project_id),
8296
+ cardId: String(row.card_id),
8297
+ kind: String(row.kind),
8298
+ ...row.detail ? { detail: String(row.detail) } : {},
8299
+ createdAt: String(row.created_at)
8300
+ }));
8301
+ }
8302
+ };
8303
+ }
8304
+ });
8305
+
7984
8306
  // src/runtime/isolated-maintenance.ts
7985
8307
  import { spawn } from "child_process";
7986
8308
  import { existsSync as existsSync8 } from "fs";
@@ -8012,7 +8334,7 @@ var init_isolated_maintenance = __esm({
8012
8334
  });
8013
8335
 
8014
8336
  // src/knowledge/workspace-store.ts
8015
- import { randomUUID as randomUUID3 } from "crypto";
8337
+ import { randomUUID as randomUUID4 } from "crypto";
8016
8338
  function arrayValue(raw) {
8017
8339
  if (typeof raw !== "string" || !raw) return [];
8018
8340
  try {
@@ -8153,7 +8475,7 @@ var init_workspace_store = __esm({
8153
8475
  }
8154
8476
  createProposal(input) {
8155
8477
  const proposal = {
8156
- id: input.id ?? randomUUID3(),
8478
+ id: input.id ?? randomUUID4(),
8157
8479
  ...input,
8158
8480
  status: input.status ?? "pending",
8159
8481
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -8206,13 +8528,13 @@ __export(workspace_exports, {
8206
8528
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
8207
8529
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
8208
8530
  });
8209
- import { createHash as createHash3 } from "crypto";
8531
+ import { createHash as createHash4 } from "crypto";
8210
8532
  import { execFile } from "child_process";
8211
8533
  import { promises as fs6 } from "fs";
8212
8534
  import path10 from "path";
8213
8535
  import { promisify } from "util";
8214
8536
  function hash(value) {
8215
- return createHash3("sha256").update(value).digest("hex");
8537
+ return createHash4("sha256").update(value).digest("hex");
8216
8538
  }
8217
8539
  function workspaceId(projectId, mode) {
8218
8540
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -8808,7 +9130,7 @@ Respond in JSON only:
8808
9130
  });
8809
9131
 
8810
9132
  // src/media/media-store.ts
8811
- import { randomUUID as randomUUID4 } from "crypto";
9133
+ import { randomUUID as randomUUID5 } from "crypto";
8812
9134
  function asOptionalText(value) {
8813
9135
  return typeof value === "string" && value.length > 0 ? value : void 0;
8814
9136
  }
@@ -8978,7 +9300,7 @@ var init_media_store = __esm({
8978
9300
  return { asset: this.getAsset(input.projectId, existing.id), deduplicated: true };
8979
9301
  }
8980
9302
  const asset = {
8981
- id: randomUUID4(),
9303
+ id: randomUUID5(),
8982
9304
  ...input,
8983
9305
  createdAt: now3,
8984
9306
  updatedAt: now3
@@ -9058,7 +9380,7 @@ var init_media_store = __esm({
9058
9380
  `).get(input.assetId, input.observationId ?? null, input.role);
9059
9381
  if (existing) return rowToLink(existing);
9060
9382
  const link = {
9061
- id: randomUUID4(),
9383
+ id: randomUUID5(),
9062
9384
  assetId: input.assetId,
9063
9385
  projectId: input.projectId,
9064
9386
  ...input.observationId !== void 0 ? { observationId: input.observationId } : {},
@@ -9097,7 +9419,7 @@ var init_media_store = __esm({
9097
9419
  return rowToDerivation(this.db.prepare("SELECT * FROM media_derivations WHERE id = ?").get(existing.id));
9098
9420
  }
9099
9421
  const derivation = {
9100
- id: randomUUID4(),
9422
+ id: randomUUID5(),
9101
9423
  ...input,
9102
9424
  content,
9103
9425
  ...error ? { error } : {},
@@ -9184,7 +9506,7 @@ var init_media_store = __esm({
9184
9506
  createJob(input) {
9185
9507
  const now3 = Date.now();
9186
9508
  const job = {
9187
- id: randomUUID4(),
9509
+ id: randomUUID5(),
9188
9510
  projectId: input.projectId,
9189
9511
  kind: input.kind,
9190
9512
  status: "queued",
@@ -9346,14 +9668,14 @@ var init_types2 = __esm({
9346
9668
  });
9347
9669
 
9348
9670
  // src/media/asset-store.ts
9349
- import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
9671
+ import { createHash as createHash5, randomUUID as randomUUID6 } from "crypto";
9350
9672
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
9351
9673
  import path11 from "path";
9352
9674
  function mediaRoot(dataDir) {
9353
9675
  return path11.resolve(dataDir, "media");
9354
9676
  }
9355
9677
  function projectStorageKey(projectId) {
9356
- return createHash4("sha256").update(projectId).digest("hex").slice(0, 16);
9678
+ return createHash5("sha256").update(projectId).digest("hex").slice(0, 16);
9357
9679
  }
9358
9680
  function validateUnderRoot(root, candidate) {
9359
9681
  const resolved = path11.resolve(candidate);
@@ -9417,7 +9739,7 @@ async function destinationExists(destination) {
9417
9739
  async function persistBuffer(bytes, destination) {
9418
9740
  await mkdir3(path11.dirname(destination), { recursive: true });
9419
9741
  if (await destinationExists(destination)) return false;
9420
- const temporary = `${destination}.${randomUUID5()}.tmp`;
9742
+ const temporary = `${destination}.${randomUUID6()}.tmp`;
9421
9743
  try {
9422
9744
  await writeFile3(temporary, bytes, { flag: "wx" });
9423
9745
  await rename2(temporary, destination);
@@ -9462,7 +9784,7 @@ async function importMediaBuffer(input) {
9462
9784
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
9463
9785
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
9464
9786
  const detected = detectMedia(input.bytes.subarray(0, 32));
9465
- const sha256 = createHash4("sha256").update(input.bytes).digest("hex");
9787
+ const sha256 = createHash5("sha256").update(input.bytes).digest("hex");
9466
9788
  return importKnownMedia({
9467
9789
  dataDir: input.dataDir,
9468
9790
  projectId: input.projectId,
@@ -9480,7 +9802,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
9480
9802
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
9481
9803
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
9482
9804
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
9483
- const sha256 = createHash4("sha256").update(bytes).digest("hex");
9805
+ const sha256 = createHash5("sha256").update(bytes).digest("hex");
9484
9806
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
9485
9807
  return bytes;
9486
9808
  }
@@ -62231,7 +62553,7 @@ var require_gaxios = __commonJS({
62231
62553
  var retry_js_1 = require_retry3();
62232
62554
  var stream_1 = __require("stream");
62233
62555
  var interceptor_js_1 = require_interceptor();
62234
- var randomUUID14 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
62556
+ var randomUUID16 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
62235
62557
  var HTTP_STATUS_NO_CONTENT = 204;
62236
62558
  var Gaxios = class {
62237
62559
  agentCache = /* @__PURE__ */ new Map();
@@ -62504,7 +62826,7 @@ var require_gaxios = __commonJS({
62504
62826
  */
62505
62827
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
62506
62828
  if (opts.multipart?.length) {
62507
- const boundary = await randomUUID14();
62829
+ const boundary = await randomUUID16();
62508
62830
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
62509
62831
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
62510
62832
  } else if (shouldDirectlyPassData) {
@@ -74731,7 +75053,7 @@ var require_websocket = __commonJS({
74731
75053
  var http3 = __require("http");
74732
75054
  var net = __require("net");
74733
75055
  var tls = __require("tls");
74734
- var { randomBytes, createHash: createHash13 } = __require("crypto");
75056
+ var { randomBytes, createHash: createHash14 } = __require("crypto");
74735
75057
  var { Duplex, Readable: Readable2 } = __require("stream");
74736
75058
  var { URL: URL2 } = __require("url");
74737
75059
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -75399,7 +75721,7 @@ var require_websocket = __commonJS({
75399
75721
  abortHandshake(websocket, socket, "Invalid Upgrade header");
75400
75722
  return;
75401
75723
  }
75402
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
75724
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
75403
75725
  if (res.headers["sec-websocket-accept"] !== digest2) {
75404
75726
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
75405
75727
  return;
@@ -75771,7 +76093,7 @@ var require_websocket_server = __commonJS({
75771
76093
  var EventEmitter = __require("events");
75772
76094
  var http3 = __require("http");
75773
76095
  var { Duplex } = __require("stream");
75774
- var { createHash: createHash13 } = __require("crypto");
76096
+ var { createHash: createHash14 } = __require("crypto");
75775
76097
  var extension3 = require_extension();
75776
76098
  var PerMessageDeflate2 = require_permessage_deflate();
75777
76099
  var subprotocol2 = require_subprotocol();
@@ -76078,7 +76400,7 @@ var require_websocket_server = __commonJS({
76078
76400
  );
76079
76401
  }
76080
76402
  if (this._state > RUNNING) return abortHandshake(socket, 503);
76081
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
76403
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
76082
76404
  const headers = [
76083
76405
  "HTTP/1.1 101 Switching Protocols",
76084
76406
  "Upgrade: websocket",
@@ -143216,6 +143538,186 @@ var init_audio_jobs = __esm({
143216
143538
  }
143217
143539
  });
143218
143540
 
143541
+ // src/memory/feedback-model.ts
143542
+ function feedbackWeightMultiplier(weight) {
143543
+ return Math.max(0.35, Math.min(1.4, 0.35 + Math.max(0, Math.min(2, weight)) * 0.525));
143544
+ }
143545
+ var init_feedback_model = __esm({
143546
+ "src/memory/feedback-model.ts"() {
143547
+ "use strict";
143548
+ init_esm_shims();
143549
+ }
143550
+ });
143551
+
143552
+ // src/memory/feedback.ts
143553
+ var feedback_exports = {};
143554
+ __export(feedback_exports, {
143555
+ FEEDBACK_DELTAS: () => FEEDBACK_DELTAS,
143556
+ MemoryFeedbackStore: () => MemoryFeedbackStore,
143557
+ applyMemoryFeedback: () => applyMemoryFeedback,
143558
+ feedbackWeightMultiplier: () => feedbackWeightMultiplier
143559
+ });
143560
+ import { randomUUID as randomUUID7 } from "crypto";
143561
+ function applyMemoryFeedback(current, event) {
143562
+ const delta = event.signal === "revoke" ? 0 : FEEDBACK_DELTAS[event.signal];
143563
+ const weight = Math.max(0, Math.min(2, current.weight + delta));
143564
+ const status = (event.signal === "code-conflict" || event.signal === "user-correction" || event.signal === "weaken") && weight < 0.5 ? "archived" : current.status;
143565
+ return { weight, status, audit: [...current.audit, { ...event }] };
143566
+ }
143567
+ function rowToEvent(row) {
143568
+ return {
143569
+ id: String(row.id),
143570
+ projectId: String(row.project_id),
143571
+ candidateKind: row.candidate_kind,
143572
+ candidateId: String(row.candidate_id),
143573
+ signal: row.signal,
143574
+ ...row.observed_at ? { at: String(row.observed_at) } : { at: (/* @__PURE__ */ new Date()).toISOString() },
143575
+ ...row.actor ? { actor: String(row.actor) } : {},
143576
+ ...row.note ? { note: String(row.note) } : {},
143577
+ ...row.source_ref ? { sourceRef: String(row.source_ref) } : {},
143578
+ ...row.target_event_id ? { targetEventId: String(row.target_event_id) } : {},
143579
+ delta: Number(row.delta ?? 0)
143580
+ };
143581
+ }
143582
+ var FEEDBACK_DELTAS, MemoryFeedbackStore;
143583
+ var init_feedback = __esm({
143584
+ "src/memory/feedback.ts"() {
143585
+ "use strict";
143586
+ init_esm_shims();
143587
+ init_sqlite_db();
143588
+ init_feedback_model();
143589
+ FEEDBACK_DELTAS = {
143590
+ "user-correction": -0.35,
143591
+ "verification-success": 0.25,
143592
+ "verification-failure": -0.25,
143593
+ "code-change": -0.15,
143594
+ used: 0.1,
143595
+ "not-used": -0.05,
143596
+ "code-conflict": -0.4,
143597
+ strengthen: 0.2,
143598
+ weaken: -0.2
143599
+ };
143600
+ MemoryFeedbackStore = class {
143601
+ db = null;
143602
+ async init(dataDir) {
143603
+ this.db = getDatabase(dataDir);
143604
+ }
143605
+ requireDb() {
143606
+ if (!this.db) throw new Error("MemoryFeedbackStore is not initialized.");
143607
+ return this.db;
143608
+ }
143609
+ loadEvents(projectId, candidateKind, candidateId) {
143610
+ return this.requireDb().prepare(`
143611
+ SELECT * FROM memory_feedback_events
143612
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143613
+ ORDER BY observed_at ASC, rowid ASC
143614
+ `).all(projectId, candidateKind, candidateId).map(rowToEvent);
143615
+ }
143616
+ rebuild(projectId, candidateKind, candidateId) {
143617
+ const events = this.loadEvents(projectId, candidateKind, candidateId);
143618
+ const revoked = new Set(events.filter((event) => event.signal === "revoke" && event.targetEventId).map((event) => event.targetEventId));
143619
+ let state = { weight: 1, status: "active", audit: [] };
143620
+ for (const event of events) {
143621
+ if (event.signal === "revoke" || revoked.has(event.id)) {
143622
+ state.audit.push(event);
143623
+ continue;
143624
+ }
143625
+ state = applyMemoryFeedback(state, event);
143626
+ }
143627
+ this.requireDb().prepare(`
143628
+ INSERT INTO memory_feedback_states (project_id, candidate_kind, candidate_id, weight, lifecycle_status, updated_at)
143629
+ VALUES (?, ?, ?, ?, ?, ?)
143630
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
143631
+ weight = excluded.weight, lifecycle_status = excluded.lifecycle_status, updated_at = excluded.updated_at
143632
+ `).run(projectId, candidateKind, candidateId, state.weight, state.status, (/* @__PURE__ */ new Date()).toISOString());
143633
+ return state;
143634
+ }
143635
+ getState(projectId, candidateKind, candidateId) {
143636
+ const row = this.requireDb().prepare(`
143637
+ SELECT weight, lifecycle_status FROM memory_feedback_states
143638
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143639
+ `).get(projectId, candidateKind, candidateId);
143640
+ if (!row) return { weight: 1, status: "active", audit: [] };
143641
+ return {
143642
+ weight: Number(row.weight ?? 1),
143643
+ status: row.lifecycle_status ?? "active",
143644
+ audit: this.loadEvents(projectId, candidateKind, candidateId)
143645
+ };
143646
+ }
143647
+ listStates(projectId, candidateKind = "observation") {
143648
+ return this.requireDb().prepare(`
143649
+ SELECT candidate_id, weight, lifecycle_status, updated_at
143650
+ FROM memory_feedback_states WHERE project_id = ? AND candidate_kind = ?
143651
+ ORDER BY updated_at DESC
143652
+ `).all(projectId, candidateKind).map((row) => ({
143653
+ candidateId: String(row.candidate_id),
143654
+ weight: Number(row.weight),
143655
+ status: row.lifecycle_status,
143656
+ updatedAt: String(row.updated_at)
143657
+ }));
143658
+ }
143659
+ weights(projectId, candidateKind = "observation") {
143660
+ const states = /* @__PURE__ */ new Map();
143661
+ for (const row of this.listStates(projectId, candidateKind)) {
143662
+ states.set(row.candidateId, this.getState(projectId, candidateKind, row.candidateId));
143663
+ }
143664
+ return states;
143665
+ }
143666
+ record(input) {
143667
+ if (input.signal === "revoke" && !input.targetEventId) {
143668
+ throw new Error("A revoke feedback event requires targetEventId.");
143669
+ }
143670
+ if (input.signal === "revoke") {
143671
+ const target = this.requireDb().prepare(`
143672
+ SELECT 1 FROM memory_feedback_events
143673
+ WHERE id = ? AND project_id = ? AND candidate_kind = ? AND candidate_id = ?
143674
+ `).get(input.targetEventId, input.projectId, input.candidateKind, input.candidateId);
143675
+ if (!target) throw new Error("The feedback event to revoke was not found in the same candidate scope.");
143676
+ }
143677
+ const event = {
143678
+ id: randomUUID7(),
143679
+ projectId: input.projectId,
143680
+ candidateKind: input.candidateKind,
143681
+ candidateId: input.candidateId,
143682
+ signal: input.signal,
143683
+ sourceRef: input.sourceRef,
143684
+ ...input.actor ? { actor: input.actor } : {},
143685
+ ...input.note ? { note: input.note.slice(0, 2e3) } : {},
143686
+ ...input.targetEventId ? { targetEventId: input.targetEventId } : {},
143687
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
143688
+ delta: input.signal === "revoke" ? 0 : FEEDBACK_DELTAS[input.signal]
143689
+ };
143690
+ this.requireDb().prepare(`
143691
+ INSERT INTO memory_feedback_events (
143692
+ id, project_id, candidate_kind, candidate_id, signal, actor, note,
143693
+ source_ref, target_event_id, delta, observed_at
143694
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
143695
+ `).run(
143696
+ event.id,
143697
+ event.projectId,
143698
+ event.candidateKind,
143699
+ event.candidateId,
143700
+ event.signal,
143701
+ event.actor ?? null,
143702
+ event.note ?? null,
143703
+ event.sourceRef,
143704
+ event.targetEventId ?? null,
143705
+ event.delta,
143706
+ event.at
143707
+ );
143708
+ return { event, state: this.rebuild(input.projectId, input.candidateKind, input.candidateId) };
143709
+ }
143710
+ audit(projectId, candidateKind, candidateId, limit2 = 100) {
143711
+ return this.requireDb().prepare(`
143712
+ SELECT * FROM memory_feedback_events
143713
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143714
+ ORDER BY observed_at DESC, rowid DESC LIMIT ?
143715
+ `).all(projectId, candidateKind, candidateId, Math.min(200, Math.max(1, Math.floor(limit2)))).map(rowToEvent);
143716
+ }
143717
+ };
143718
+ }
143719
+ });
143720
+
143219
143721
  // src/memory/retention.ts
143220
143722
  var retention_exports = {};
143221
143723
  __export(retention_exports, {
@@ -143257,6 +143759,7 @@ function getEffectiveRetentionDays(doc) {
143257
143759
  }
143258
143760
  function isImmune(doc) {
143259
143761
  if (doc.type === "probe") return false;
143762
+ if (doc.feedbackStatus === "archived") return false;
143260
143763
  if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
143261
143764
  if (doc.valueCategory === "core") return true;
143262
143765
  const importance = getImportanceLevel(doc);
@@ -143289,7 +143792,8 @@ function calculateRelevance(doc, referenceTime) {
143289
143792
  const decayFactor = Math.exp(-ageDays / retention);
143290
143793
  const accessCount = doc.accessCount ?? 0;
143291
143794
  const accessBoost = Math.min(2, 1 + 0.1 * accessCount);
143292
- let totalScore = base * decayFactor * accessBoost;
143795
+ const feedbackMultiplier = doc.feedbackWeight === void 0 ? 1 : feedbackWeightMultiplier(doc.feedbackWeight);
143796
+ let totalScore = base * decayFactor * accessBoost * feedbackMultiplier;
143293
143797
  const immune = isImmune(doc);
143294
143798
  if (immune) {
143295
143799
  totalScore = Math.max(totalScore, 0.5);
@@ -143301,7 +143805,8 @@ function calculateRelevance(doc, referenceTime) {
143301
143805
  decayFactor,
143302
143806
  accessBoost,
143303
143807
  ageDays,
143304
- isImmune: immune
143808
+ isImmune: immune,
143809
+ feedbackMultiplier
143305
143810
  };
143306
143811
  }
143307
143812
  function rankByRelevance(docs, referenceTime) {
@@ -143309,6 +143814,7 @@ function rankByRelevance(docs, referenceTime) {
143309
143814
  }
143310
143815
  function getRetentionZone(doc, referenceTime) {
143311
143816
  const now3 = referenceTime ?? /* @__PURE__ */ new Date();
143817
+ if (doc.feedbackStatus === "archived") return "archive-candidate";
143312
143818
  const importance = getImportanceLevel(doc);
143313
143819
  const retention = getEffectiveRetentionDays(doc);
143314
143820
  const createdAt = new Date(doc.createdAt);
@@ -143380,7 +143886,7 @@ function explainRetention(doc, referenceTime) {
143380
143886
  summary: parts.join(" ; ")
143381
143887
  };
143382
143888
  }
143383
- function toRetentionDocument(obs, accessMap) {
143889
+ function toRetentionDocument(obs, accessMap, feedback) {
143384
143890
  const access2 = accessMap?.get(obs.id);
143385
143891
  return {
143386
143892
  id: `obs-${obs.id}`,
@@ -143402,11 +143908,12 @@ function toRetentionDocument(obs, accessMap) {
143402
143908
  sourceDetail: obs.sourceDetail ?? "",
143403
143909
  valueCategory: obs.valueCategory ?? "",
143404
143910
  admissionState: obs.admissionState ?? "",
143405
- admissionReason: obs.admissionReason ?? ""
143911
+ admissionReason: obs.admissionReason ?? "",
143912
+ ...feedback ? { feedbackWeight: feedback.weight, feedbackStatus: feedback.status } : {}
143406
143913
  };
143407
143914
  }
143408
143915
  function projectObservationRetention(observation, options = {}) {
143409
- const document2 = toRetentionDocument(observation, options.accessMap);
143916
+ const document2 = toRetentionDocument(observation, options.accessMap, options.feedback);
143410
143917
  const relevance = calculateRelevance(document2, options.referenceTime);
143411
143918
  const explanation = explainRetention(document2, options.referenceTime);
143412
143919
  return {
@@ -143431,7 +143938,17 @@ function summarizeRetentionProjections(projections) {
143431
143938
  }
143432
143939
  return { active, stale, archiveCandidates, immune };
143433
143940
  }
143434
- async function archiveExpiredBatch(_projectDir, options) {
143941
+ async function loadFeedbackStore(dataDir) {
143942
+ try {
143943
+ const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
143944
+ const store = new MemoryFeedbackStore2();
143945
+ await store.init(dataDir);
143946
+ return store;
143947
+ } catch {
143948
+ return void 0;
143949
+ }
143950
+ }
143951
+ async function archiveExpiredBatch(projectDir2, options) {
143435
143952
  const store = getObservationStore();
143436
143953
  const limit2 = Math.min(500, Math.max(1, Math.floor(options.limit ?? 100)));
143437
143954
  const afterId = Math.max(0, Math.floor(options.afterId ?? 0));
@@ -143440,11 +143957,13 @@ async function archiveExpiredBatch(_projectDir, options) {
143440
143957
  afterId,
143441
143958
  limit: limit2 + 1
143442
143959
  });
143960
+ const feedback = await loadFeedbackStore(projectDir2);
143443
143961
  const hasMore = page.length > limit2;
143444
143962
  const scanned = hasMore ? page.slice(0, limit2) : page;
143445
143963
  const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => projectObservationRetention(observation, {
143446
143964
  accessMap: options.accessMap,
143447
- referenceTime: options.referenceTime
143965
+ referenceTime: options.referenceTime,
143966
+ ...feedback ? { feedback: feedback.getState(options.projectId, "observation", String(observation.id)) } : {}
143448
143967
  }).zone === "archive-candidate").map((observation) => observation.id);
143449
143968
  const archived = candidateIds.length === 0 ? 0 : await store.atomic(async (tx) => {
143450
143969
  const updates = await Promise.all(
@@ -143475,6 +143994,7 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
143475
143994
  const remaining = reader ? remainingObservations.filter((observation) => canManageObservation(observation, reader)).length : remainingObservations.length;
143476
143995
  return { archived, remaining };
143477
143996
  }
143997
+ const feedback = await loadFeedbackStore(projectDir2);
143478
143998
  return await store.atomic(async (tx) => {
143479
143999
  const allObs = await tx.loadAll();
143480
144000
  const activeObs = allObs.filter(
@@ -143482,7 +144002,11 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
143482
144002
  );
143483
144003
  const archivedIds = [];
143484
144004
  for (const obs of activeObs) {
143485
- const retention = projectObservationRetention(obs, { accessMap, referenceTime });
144005
+ const retention = projectObservationRetention(obs, {
144006
+ accessMap,
144007
+ referenceTime,
144008
+ ...feedback ? { feedback: feedback.getState(obs.projectId, "observation", String(obs.id)) } : {}
144009
+ });
143486
144010
  if (retention.zone === "archive-candidate") {
143487
144011
  archivedIds.push(obs.id);
143488
144012
  }
@@ -143502,6 +144026,7 @@ var init_retention = __esm({
143502
144026
  init_esm_shims();
143503
144027
  init_obs_store();
143504
144028
  init_visibility();
144029
+ init_feedback_model();
143505
144030
  RETENTION_DAYS = {
143506
144031
  critical: 365,
143507
144032
  high: 180,
@@ -143538,7 +144063,7 @@ var init_retention = __esm({
143538
144063
  });
143539
144064
 
143540
144065
  // src/memory/owner.ts
143541
- import { randomUUID as randomUUID6 } from "crypto";
144066
+ import { randomUUID as randomUUID8 } from "crypto";
143542
144067
  import { promises as fs9 } from "fs";
143543
144068
  import path17 from "path";
143544
144069
  function ownerPath(dataDir) {
@@ -143568,7 +144093,7 @@ async function resolveLocalMemoryOwner(dataDir, options = {}) {
143568
144093
  await fs9.mkdir(path17.dirname(filePath), { recursive: true });
143569
144094
  const created = {
143570
144095
  version: 1,
143571
- id: "local-" + randomUUID6(),
144096
+ id: "local-" + randomUUID8(),
143572
144097
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
143573
144098
  };
143574
144099
  try {
@@ -143591,7 +144116,7 @@ var init_owner = __esm({
143591
144116
  });
143592
144117
 
143593
144118
  // src/memory/long-term-store.ts
143594
- import { randomUUID as randomUUID7 } from "crypto";
144119
+ import { randomUUID as randomUUID9 } from "crypto";
143595
144120
  function optionalText2(value) {
143596
144121
  return typeof value === "string" && value.trim() ? value : void 0;
143597
144122
  }
@@ -143642,7 +144167,7 @@ function rowToEvidence(row) {
143642
144167
  createdAt: row.createdAt
143643
144168
  };
143644
144169
  }
143645
- function rowToEvent(row) {
144170
+ function rowToEvent2(row) {
143646
144171
  return {
143647
144172
  id: row.id,
143648
144173
  memoryId: row.memoryId,
@@ -143751,7 +144276,7 @@ var init_long_term_store = __esm({
143751
144276
  }
143752
144277
  insertEvidence(input) {
143753
144278
  const evidence = {
143754
- id: randomUUID7(),
144279
+ id: randomUUID9(),
143755
144280
  ...input,
143756
144281
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
143757
144282
  };
@@ -143778,7 +144303,7 @@ var init_long_term_store = __esm({
143778
144303
  }
143779
144304
  recordEvent(input) {
143780
144305
  const event = {
143781
- id: randomUUID7(),
144306
+ id: randomUUID9(),
143782
144307
  ...input,
143783
144308
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
143784
144309
  };
@@ -143799,7 +144324,7 @@ var init_long_term_store = __esm({
143799
144324
  listEvents(memoryId) {
143800
144325
  return this.db.prepare(`
143801
144326
  SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
143802
- `).all(memoryId).map(rowToEvent);
144327
+ `).all(memoryId).map(rowToEvent2);
143803
144328
  }
143804
144329
  markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
143805
144330
  const unique = [...new Set(ids.filter(Boolean))];
@@ -143868,7 +144393,7 @@ __export(long_term_exports, {
143868
144393
  selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
143869
144394
  supersedeLongTermMemory: () => supersedeLongTermMemory
143870
144395
  });
143871
- import { randomUUID as randomUUID8 } from "crypto";
144396
+ import { randomUUID as randomUUID10 } from "crypto";
143872
144397
  function isOneOf(value, values) {
143873
144398
  return typeof value === "string" && values.includes(value);
143874
144399
  }
@@ -144050,7 +144575,7 @@ async function createManualLongTermMemory(input) {
144050
144575
  const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
144051
144576
  if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
144052
144577
  assertScope(input.scope);
144053
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
144578
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
144054
144579
  return createLongTermMemoryWithDataDir({
144055
144580
  dataDir: input.dataDir,
144056
144581
  originProjectId: input.projectId,
@@ -144105,7 +144630,7 @@ async function createLongTermMemoryWithDataDir(input) {
144105
144630
  assertPortableEvidence(input.scope, portability, evidenceInputs);
144106
144631
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
144107
144632
  const memory = {
144108
- id: randomUUID8(),
144633
+ id: randomUUID10(),
144109
144634
  originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
144110
144635
  ownerId: compactText(input.ownerId, "owner id", 160),
144111
144636
  scope: input.scope,
@@ -144423,9 +144948,9 @@ var init_long_term = __esm({
144423
144948
  });
144424
144949
 
144425
144950
  // src/codegraph/ids.ts
144426
- import { createHash as createHash5 } from "crypto";
144951
+ import { createHash as createHash6 } from "crypto";
144427
144952
  function digest(input) {
144428
- return createHash5("sha256").update(input).digest("hex").slice(0, 16);
144953
+ return createHash6("sha256").update(input).digest("hex").slice(0, 16);
144429
144954
  }
144430
144955
  function normalizeCodePath(path22) {
144431
144956
  return path22.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -144460,7 +144985,7 @@ var store_exports = {};
144460
144985
  __export(store_exports, {
144461
144986
  CodeGraphStore: () => CodeGraphStore
144462
144987
  });
144463
- import { randomUUID as randomUUID9 } from "crypto";
144988
+ import { randomUUID as randomUUID11 } from "crypto";
144464
144989
  import { posix as pathPosix } from "path";
144465
144990
  function rowToFile(row) {
144466
144991
  return {
@@ -145177,7 +145702,7 @@ var init_store = __esm({
145177
145702
  * so an interrupted scan cannot be advertised as complete.
145178
145703
  */
145179
145704
  recordCodeStateSnapshot(input) {
145180
- const id = randomUUID9();
145705
+ const id = randomUUID11();
145181
145706
  let snapshot;
145182
145707
  const tx = this.db.transaction(() => {
145183
145708
  const previous = this.db.prepare(
@@ -145255,7 +145780,7 @@ var init_store = __esm({
145255
145780
 
145256
145781
  // src/codegraph/code-state.ts
145257
145782
  import { execFile as execFile2 } from "child_process";
145258
- import { createHash as createHash6 } from "crypto";
145783
+ import { createHash as createHash7 } from "crypto";
145259
145784
  import { promisify as promisify3 } from "util";
145260
145785
  async function runGit(rootPath, args) {
145261
145786
  try {
@@ -145284,7 +145809,7 @@ function changedPathCount(status) {
145284
145809
  return count2;
145285
145810
  }
145286
145811
  function fingerprint(baseRevision, status) {
145287
- return createHash6("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
145812
+ return createHash7("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
145288
145813
  }
145289
145814
  async function collectCodeStateSnapshot(input) {
145290
145815
  const [revision, status] = await Promise.all([
@@ -145384,11 +145909,11 @@ __export(lite_provider_exports, {
145384
145909
  indexProjectLite: () => indexProjectLite,
145385
145910
  refreshProjectLite: () => refreshProjectLite
145386
145911
  });
145387
- import { createHash as createHash7 } from "crypto";
145912
+ import { createHash as createHash8 } from "crypto";
145388
145913
  import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
145389
145914
  import { join as join4, relative } from "path";
145390
145915
  function hashText(text2) {
145391
- return createHash7("sha256").update(text2).digest("hex");
145916
+ return createHash8("sha256").update(text2).digest("hex");
145392
145917
  }
145393
145918
  function extension2(path22) {
145394
145919
  const index = path22.lastIndexOf(".");
@@ -145955,7 +146480,7 @@ var claim_store_exports = {};
145955
146480
  __export(claim_store_exports, {
145956
146481
  ClaimStore: () => ClaimStore
145957
146482
  });
145958
- import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
146483
+ import { createHash as createHash9, randomUUID as randomUUID12 } from "crypto";
145959
146484
  function optionalText3(value) {
145960
146485
  return typeof value === "string" && value ? value : void 0;
145961
146486
  }
@@ -145994,7 +146519,7 @@ function rowToEvidence2(row) {
145994
146519
  createdAt: row.createdAt
145995
146520
  };
145996
146521
  }
145997
- function rowToEvent2(row) {
146522
+ function rowToEvent3(row) {
145998
146523
  return {
145999
146524
  id: row.id,
146000
146525
  projectId: row.projectId,
@@ -146008,7 +146533,7 @@ function rowToEvent2(row) {
146008
146533
  };
146009
146534
  }
146010
146535
  function evidenceKey(input) {
146011
- return createHash8("sha256").update(JSON.stringify([
146536
+ return createHash9("sha256").update(JSON.stringify([
146012
146537
  input.evidenceKind,
146013
146538
  input.evidenceId,
146014
146539
  input.relation,
@@ -146139,7 +146664,7 @@ var init_claim_store = __esm({
146139
146664
  locator, capturedHash, evidenceKey, createdAt
146140
146665
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
146141
146666
  `).run(
146142
- randomUUID10(),
146667
+ randomUUID12(),
146143
146668
  input.claimId,
146144
146669
  input.evidenceKind,
146145
146670
  input.evidenceId,
@@ -146161,7 +146686,7 @@ var init_claim_store = __esm({
146161
146686
  }
146162
146687
  recordEvent(input) {
146163
146688
  const event = {
146164
- id: randomUUID10(),
146689
+ id: randomUUID12(),
146165
146690
  ...input,
146166
146691
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
146167
146692
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -146189,7 +146714,7 @@ var init_claim_store = __esm({
146189
146714
  SELECT * FROM knowledge_claim_events
146190
146715
  WHERE claimId = ?
146191
146716
  ORDER BY createdAt ASC, id
146192
- `).all(claimId).map(rowToEvent2);
146717
+ `).all(claimId).map(rowToEvent3);
146193
146718
  }
146194
146719
  };
146195
146720
  }
@@ -146206,7 +146731,7 @@ __export(claims_exports, {
146206
146731
  supersedeClaim: () => supersedeClaim,
146207
146732
  writeClaim: () => writeClaim
146208
146733
  });
146209
- import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
146734
+ import { createHash as createHash10, randomUUID as randomUUID13 } from "crypto";
146210
146735
  function compactText2(value, field) {
146211
146736
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
146212
146737
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -146218,7 +146743,7 @@ function normalizedTerm(value) {
146218
146743
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
146219
146744
  }
146220
146745
  function hashIdentity(parts) {
146221
- return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
146746
+ return createHash10("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
146222
146747
  }
146223
146748
  function buildClaimIdentity(input) {
146224
146749
  const subject = normalizedTerm(input.subject);
@@ -146292,7 +146817,7 @@ function normalizeClaimInput(input) {
146292
146817
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
146293
146818
  return {
146294
146819
  claim: {
146295
- id: randomUUID11(),
146820
+ id: randomUUID13(),
146296
146821
  projectId,
146297
146822
  subject,
146298
146823
  predicate,
@@ -146459,7 +146984,7 @@ function supersedeClaim(store, input) {
146459
146984
  });
146460
146985
  }
146461
146986
  function hashObservation(observation) {
146462
- return createHash9("sha256").update(JSON.stringify([
146987
+ return createHash10("sha256").update(JSON.stringify([
146463
146988
  observation.title,
146464
146989
  observation.narrative,
146465
146990
  observation.facts,
@@ -146703,11 +147228,11 @@ var init_frontmatter = __esm({
146703
147228
  });
146704
147229
 
146705
147230
  // src/knowledge/markdown.ts
146706
- import { createHash as createHash10 } from "crypto";
147231
+ import { createHash as createHash11 } from "crypto";
146707
147232
  import { promises as fs10 } from "fs";
146708
147233
  import path18 from "path";
146709
147234
  function hashContent(content) {
146710
- return createHash10("sha256").update(content).digest("hex");
147235
+ return createHash11("sha256").update(content).digest("hex");
146711
147236
  }
146712
147237
  function stringField(data, key) {
146713
147238
  const value = data[key];
@@ -146833,11 +147358,11 @@ __export(wiki_exports, {
146833
147358
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
146834
147359
  readKnowledgePage: () => readKnowledgePage
146835
147360
  });
146836
- import { createHash as createHash11 } from "crypto";
147361
+ import { createHash as createHash12 } from "crypto";
146837
147362
  import { promises as fs11 } from "fs";
146838
147363
  import path19 from "path";
146839
147364
  function hash2(value) {
146840
- return createHash11("sha256").update(value).digest("hex");
147365
+ return createHash12("sha256").update(value).digest("hex");
146841
147366
  }
146842
147367
  function now() {
146843
147368
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -147377,7 +147902,7 @@ var init_workflow_sync = __esm({
147377
147902
  });
147378
147903
 
147379
147904
  // src/knowledge/workflow-store.ts
147380
- import { randomUUID as randomUUID12 } from "crypto";
147905
+ import { randomUUID as randomUUID14 } from "crypto";
147381
147906
  function parseJson(value, fallback) {
147382
147907
  if (typeof value !== "string" || !value) return fallback;
147383
147908
  try {
@@ -147477,7 +148002,7 @@ var init_workflow_store = __esm({
147477
148002
  }
147478
148003
  recordRun(input) {
147479
148004
  const run = {
147480
- id: randomUUID12(),
148005
+ id: randomUUID14(),
147481
148006
  workflowId: input.workflowId,
147482
148007
  projectId: input.projectId,
147483
148008
  task: input.task.trim(),
@@ -147545,7 +148070,7 @@ var init_outcome_types = __esm({
147545
148070
  });
147546
148071
 
147547
148072
  // src/knowledge/outcome-store.ts
147548
- import { randomUUID as randomUUID13 } from "crypto";
148073
+ import { randomUUID as randomUUID15 } from "crypto";
147549
148074
  function optionalText5(value) {
147550
148075
  return typeof value === "string" && value.trim() ? value : void 0;
147551
148076
  }
@@ -147589,7 +148114,7 @@ var init_outcome_store = __esm({
147589
148114
  }
147590
148115
  record(input) {
147591
148116
  const signal = {
147592
- id: randomUUID13(),
148117
+ id: randomUUID15(),
147593
148118
  projectId: text(input.projectId, "project id"),
147594
148119
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
147595
148120
  candidateId: text(input.candidateId, "candidate id"),
@@ -147694,11 +148219,11 @@ __export(workflows_exports, {
147694
148219
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
147695
148220
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
147696
148221
  });
147697
- import { createHash as createHash12 } from "crypto";
148222
+ import { createHash as createHash13 } from "crypto";
147698
148223
  import { promises as fs12 } from "fs";
147699
148224
  import path20 from "path";
147700
148225
  function hash3(value) {
147701
- return createHash12("sha256").update(value).digest("hex");
148226
+ return createHash13("sha256").update(value).digest("hex");
147702
148227
  }
147703
148228
  function now2() {
147704
148229
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -149080,6 +149605,16 @@ async function bindObservationCodeRefsBestEffort(observation) {
149080
149605
  } catch {
149081
149606
  }
149082
149607
  }
149608
+ async function persistEvidenceCard(observation) {
149609
+ if (!projectDir) return;
149610
+ try {
149611
+ const evidence = new EvidenceCardStore();
149612
+ await evidence.init(projectDir);
149613
+ evidence.upsertObservation(observation);
149614
+ } catch (error) {
149615
+ console.error(`[memorix] Evidence card projection failed for obs-${observation.id}: ${error instanceof Error ? error.message : String(error)}`);
149616
+ }
149617
+ }
149083
149618
  function queueClaimDerivation(observation) {
149084
149619
  const dataDir = projectDir;
149085
149620
  const isExplicit = observation.sourceDetail === "explicit";
@@ -149426,6 +149961,7 @@ async function storeObservation(input) {
149426
149961
  }
149427
149962
  if (!persistedCallbackInvoked) await input.onPersisted?.(observation);
149428
149963
  await bindObservationCodeRefsBestEffort(observation);
149964
+ await persistEvidenceCard(observation);
149429
149965
  queueObservationQualification(observation);
149430
149966
  if (resolveObservationVisibility(observation) === "project") {
149431
149967
  queueClaimDerivation(observation);
@@ -149518,6 +150054,7 @@ async function upsertObservation(existing, input, now3) {
149518
150054
  await store.update(existing);
149519
150055
  }
149520
150056
  await bindObservationCodeRefsBestEffort(existing);
150057
+ await persistEvidenceCard(existing);
149521
150058
  queueObservationQualification(existing);
149522
150059
  if (resolveObservationVisibility(existing) === "project") {
149523
150060
  queueClaimDerivation(existing);
@@ -149651,6 +150188,7 @@ async function resolveObservations(ids, status = "resolved") {
149651
150188
  await Promise.all(changed.map((observation) => tx.update(observation)));
149652
150189
  });
149653
150190
  }
150191
+ for (const observation of changed) await persistEvidenceCard(observation);
149654
150192
  return { resolved, notFound };
149655
150193
  }
149656
150194
  function getProjectObservations(projectId) {
@@ -149988,6 +150526,7 @@ var init_observations = __esm({
149988
150526
  init_secret_filter();
149989
150527
  init_lifecycle();
149990
150528
  init_visibility();
150529
+ init_evidence_store();
149991
150530
  observations = [];
149992
150531
  nextId = 1;
149993
150532
  projectDir = null;