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
  });
@@ -518,7 +525,7 @@ function closeAllDatabases() {
518
525
  _dbCache.delete(key);
519
526
  }
520
527
  }
521
- 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;
528
+ 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;
522
529
  var init_sqlite_db = __esm({
523
530
  "src/store/sqlite-db.ts"() {
524
531
  "use strict";
@@ -1164,6 +1171,79 @@ CREATE TABLE IF NOT EXISTS compaction_checkpoints (
1164
1171
  created_at TEXT NOT NULL,
1165
1172
  updated_at TEXT NOT NULL
1166
1173
  );
1174
+ `;
1175
+ CREATE_EVIDENCE_CARDS_TABLE = `
1176
+ CREATE TABLE IF NOT EXISTS evidence_cards (
1177
+ id TEXT PRIMARY KEY,
1178
+ project_id TEXT NOT NULL,
1179
+ candidate_kind TEXT NOT NULL,
1180
+ candidate_id TEXT NOT NULL,
1181
+ title TEXT NOT NULL,
1182
+ summary TEXT NOT NULL DEFAULT '',
1183
+ entity_name TEXT,
1184
+ source_kind TEXT NOT NULL,
1185
+ source_ref TEXT NOT NULL,
1186
+ locator TEXT,
1187
+ captured_hash TEXT,
1188
+ session_id TEXT,
1189
+ status TEXT NOT NULL DEFAULT 'active',
1190
+ verification TEXT NOT NULL DEFAULT 'unverified',
1191
+ freshness TEXT NOT NULL DEFAULT 'current',
1192
+ stale_reason TEXT,
1193
+ files_json TEXT NOT NULL DEFAULT '[]',
1194
+ related_entities_json TEXT NOT NULL DEFAULT '[]',
1195
+ created_at TEXT NOT NULL,
1196
+ updated_at TEXT NOT NULL,
1197
+ UNIQUE(project_id, candidate_kind, candidate_id)
1198
+ );
1199
+ `;
1200
+ CREATE_EVIDENCE_CARD_EVENTS_TABLE = `
1201
+ CREATE TABLE IF NOT EXISTS evidence_card_events (
1202
+ id TEXT PRIMARY KEY,
1203
+ project_id TEXT NOT NULL,
1204
+ card_id TEXT NOT NULL,
1205
+ kind TEXT NOT NULL,
1206
+ detail TEXT,
1207
+ created_at TEXT NOT NULL,
1208
+ FOREIGN KEY (card_id) REFERENCES evidence_cards(id) ON DELETE CASCADE
1209
+ );
1210
+ `;
1211
+ CREATE_MEMORY_FEEDBACK_STATES_TABLE = `
1212
+ CREATE TABLE IF NOT EXISTS memory_feedback_states (
1213
+ project_id TEXT NOT NULL,
1214
+ candidate_kind TEXT NOT NULL,
1215
+ candidate_id TEXT NOT NULL,
1216
+ weight REAL NOT NULL DEFAULT 1.0,
1217
+ lifecycle_status TEXT NOT NULL DEFAULT 'active',
1218
+ updated_at TEXT NOT NULL,
1219
+ PRIMARY KEY (project_id, candidate_kind, candidate_id)
1220
+ );
1221
+ `;
1222
+ CREATE_MEMORY_FEEDBACK_EVENTS_TABLE = `
1223
+ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1224
+ id TEXT PRIMARY KEY,
1225
+ project_id TEXT NOT NULL,
1226
+ candidate_kind TEXT NOT NULL,
1227
+ candidate_id TEXT NOT NULL,
1228
+ signal TEXT NOT NULL,
1229
+ actor TEXT,
1230
+ note TEXT,
1231
+ source_ref TEXT NOT NULL,
1232
+ target_event_id TEXT,
1233
+ delta REAL NOT NULL DEFAULT 0,
1234
+ observed_at TEXT NOT NULL
1235
+ );
1236
+ `;
1237
+ CREATE_MCP_BINDINGS_TABLE = `
1238
+ CREATE TABLE IF NOT EXISTS mcp_bindings (
1239
+ handle_id TEXT PRIMARY KEY,
1240
+ project_id TEXT NOT NULL,
1241
+ project_root TEXT NOT NULL,
1242
+ data_dir TEXT NOT NULL,
1243
+ created_at TEXT NOT NULL,
1244
+ last_used_at TEXT NOT NULL,
1245
+ expires_at TEXT
1246
+ );
1167
1247
  `;
1168
1248
  CREATE_INDEXES = `
1169
1249
  CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
@@ -1226,6 +1306,12 @@ CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_target
1226
1306
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
1227
1307
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
1228
1308
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
1309
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC);
1310
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id);
1311
+ CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1312
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1313
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1314
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1229
1315
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1230
1316
  ON maintenance_jobs(project_id, kind, dedupe_key)
1231
1317
  WHERE status IN ('pending', 'running', 'retry');
@@ -1381,6 +1467,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1381
1467
  }
1382
1468
  db2.exec("CREATE INDEX IF NOT EXISTS idx_media_jobs_source_asset ON media_jobs(project_id, source_asset_id, status)");
1383
1469
  }
1470
+ },
1471
+ {
1472
+ id: "1.8-context-control-plane",
1473
+ apply: (db2) => {
1474
+ db2.exec(CREATE_EVIDENCE_CARDS_TABLE);
1475
+ db2.exec(CREATE_EVIDENCE_CARD_EVENTS_TABLE);
1476
+ db2.exec(CREATE_MEMORY_FEEDBACK_STATES_TABLE);
1477
+ db2.exec(CREATE_MEMORY_FEEDBACK_EVENTS_TABLE);
1478
+ db2.exec(CREATE_MCP_BINDINGS_TABLE);
1479
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC)");
1480
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id)");
1481
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at)");
1482
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1483
+ 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)");
1484
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1485
+ }
1384
1486
  }
1385
1487
  ];
1386
1488
  _dbCache = /* @__PURE__ */ new Map();
@@ -7981,8 +8083,228 @@ 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/knowledge/workspace-store.ts
7985
- import { randomUUID as randomUUID3 } from "crypto";
8307
+ import { randomUUID as randomUUID4 } from "crypto";
7986
8308
  function arrayValue(raw) {
7987
8309
  if (typeof raw !== "string" || !raw) return [];
7988
8310
  try {
@@ -8123,7 +8445,7 @@ var init_workspace_store = __esm({
8123
8445
  }
8124
8446
  createProposal(input) {
8125
8447
  const proposal = {
8126
- id: input.id ?? randomUUID3(),
8448
+ id: input.id ?? randomUUID4(),
8127
8449
  ...input,
8128
8450
  status: input.status ?? "pending",
8129
8451
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -8176,13 +8498,13 @@ __export(workspace_exports, {
8176
8498
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
8177
8499
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
8178
8500
  });
8179
- import { createHash as createHash3 } from "crypto";
8501
+ import { createHash as createHash4 } from "crypto";
8180
8502
  import { execFile } from "child_process";
8181
8503
  import { promises as fs6 } from "fs";
8182
8504
  import path9 from "path";
8183
8505
  import { promisify } from "util";
8184
8506
  function hash(value) {
8185
- return createHash3("sha256").update(value).digest("hex");
8507
+ return createHash4("sha256").update(value).digest("hex");
8186
8508
  }
8187
8509
  function workspaceId(projectId, mode) {
8188
8510
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -8778,7 +9100,7 @@ Respond in JSON only:
8778
9100
  });
8779
9101
 
8780
9102
  // src/media/media-store.ts
8781
- import { randomUUID as randomUUID4 } from "crypto";
9103
+ import { randomUUID as randomUUID5 } from "crypto";
8782
9104
  function asOptionalText(value) {
8783
9105
  return typeof value === "string" && value.length > 0 ? value : void 0;
8784
9106
  }
@@ -8948,7 +9270,7 @@ var init_media_store = __esm({
8948
9270
  return { asset: this.getAsset(input.projectId, existing.id), deduplicated: true };
8949
9271
  }
8950
9272
  const asset = {
8951
- id: randomUUID4(),
9273
+ id: randomUUID5(),
8952
9274
  ...input,
8953
9275
  createdAt: now3,
8954
9276
  updatedAt: now3
@@ -9028,7 +9350,7 @@ var init_media_store = __esm({
9028
9350
  `).get(input.assetId, input.observationId ?? null, input.role);
9029
9351
  if (existing) return rowToLink(existing);
9030
9352
  const link = {
9031
- id: randomUUID4(),
9353
+ id: randomUUID5(),
9032
9354
  assetId: input.assetId,
9033
9355
  projectId: input.projectId,
9034
9356
  ...input.observationId !== void 0 ? { observationId: input.observationId } : {},
@@ -9067,7 +9389,7 @@ var init_media_store = __esm({
9067
9389
  return rowToDerivation(this.db.prepare("SELECT * FROM media_derivations WHERE id = ?").get(existing.id));
9068
9390
  }
9069
9391
  const derivation = {
9070
- id: randomUUID4(),
9392
+ id: randomUUID5(),
9071
9393
  ...input,
9072
9394
  content,
9073
9395
  ...error ? { error } : {},
@@ -9154,7 +9476,7 @@ var init_media_store = __esm({
9154
9476
  createJob(input) {
9155
9477
  const now3 = Date.now();
9156
9478
  const job = {
9157
- id: randomUUID4(),
9479
+ id: randomUUID5(),
9158
9480
  projectId: input.projectId,
9159
9481
  kind: input.kind,
9160
9482
  status: "queued",
@@ -9316,14 +9638,14 @@ var init_types2 = __esm({
9316
9638
  });
9317
9639
 
9318
9640
  // src/media/asset-store.ts
9319
- import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
9641
+ import { createHash as createHash5, randomUUID as randomUUID6 } from "crypto";
9320
9642
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
9321
9643
  import path10 from "path";
9322
9644
  function mediaRoot(dataDir) {
9323
9645
  return path10.resolve(dataDir, "media");
9324
9646
  }
9325
9647
  function projectStorageKey(projectId) {
9326
- return createHash4("sha256").update(projectId).digest("hex").slice(0, 16);
9648
+ return createHash5("sha256").update(projectId).digest("hex").slice(0, 16);
9327
9649
  }
9328
9650
  function validateUnderRoot(root, candidate) {
9329
9651
  const resolved = path10.resolve(candidate);
@@ -9387,7 +9709,7 @@ async function destinationExists(destination) {
9387
9709
  async function persistBuffer(bytes, destination) {
9388
9710
  await mkdir3(path10.dirname(destination), { recursive: true });
9389
9711
  if (await destinationExists(destination)) return false;
9390
- const temporary = `${destination}.${randomUUID5()}.tmp`;
9712
+ const temporary = `${destination}.${randomUUID6()}.tmp`;
9391
9713
  try {
9392
9714
  await writeFile3(temporary, bytes, { flag: "wx" });
9393
9715
  await rename2(temporary, destination);
@@ -9432,7 +9754,7 @@ async function importMediaBuffer(input) {
9432
9754
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
9433
9755
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
9434
9756
  const detected = detectMedia(input.bytes.subarray(0, 32));
9435
- const sha256 = createHash4("sha256").update(input.bytes).digest("hex");
9757
+ const sha256 = createHash5("sha256").update(input.bytes).digest("hex");
9436
9758
  return importKnownMedia({
9437
9759
  dataDir: input.dataDir,
9438
9760
  projectId: input.projectId,
@@ -9450,7 +9772,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
9450
9772
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
9451
9773
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
9452
9774
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
9453
- const sha256 = createHash4("sha256").update(bytes).digest("hex");
9775
+ const sha256 = createHash5("sha256").update(bytes).digest("hex");
9454
9776
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
9455
9777
  return bytes;
9456
9778
  }
@@ -62201,7 +62523,7 @@ var require_gaxios = __commonJS({
62201
62523
  var retry_js_1 = require_retry3();
62202
62524
  var stream_1 = __require("stream");
62203
62525
  var interceptor_js_1 = require_interceptor();
62204
- var randomUUID14 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
62526
+ var randomUUID16 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
62205
62527
  var HTTP_STATUS_NO_CONTENT = 204;
62206
62528
  var Gaxios = class {
62207
62529
  agentCache = /* @__PURE__ */ new Map();
@@ -62474,7 +62796,7 @@ var require_gaxios = __commonJS({
62474
62796
  */
62475
62797
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
62476
62798
  if (opts.multipart?.length) {
62477
- const boundary = await randomUUID14();
62799
+ const boundary = await randomUUID16();
62478
62800
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
62479
62801
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
62480
62802
  } else if (shouldDirectlyPassData) {
@@ -74701,7 +75023,7 @@ var require_websocket = __commonJS({
74701
75023
  var http3 = __require("http");
74702
75024
  var net = __require("net");
74703
75025
  var tls = __require("tls");
74704
- var { randomBytes, createHash: createHash13 } = __require("crypto");
75026
+ var { randomBytes, createHash: createHash14 } = __require("crypto");
74705
75027
  var { Duplex, Readable: Readable2 } = __require("stream");
74706
75028
  var { URL: URL2 } = __require("url");
74707
75029
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -75369,7 +75691,7 @@ var require_websocket = __commonJS({
75369
75691
  abortHandshake(websocket, socket, "Invalid Upgrade header");
75370
75692
  return;
75371
75693
  }
75372
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
75694
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
75373
75695
  if (res.headers["sec-websocket-accept"] !== digest2) {
75374
75696
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
75375
75697
  return;
@@ -75741,7 +76063,7 @@ var require_websocket_server = __commonJS({
75741
76063
  var EventEmitter = __require("events");
75742
76064
  var http3 = __require("http");
75743
76065
  var { Duplex } = __require("stream");
75744
- var { createHash: createHash13 } = __require("crypto");
76066
+ var { createHash: createHash14 } = __require("crypto");
75745
76067
  var extension3 = require_extension();
75746
76068
  var PerMessageDeflate2 = require_permessage_deflate();
75747
76069
  var subprotocol2 = require_subprotocol();
@@ -76048,7 +76370,7 @@ var require_websocket_server = __commonJS({
76048
76370
  );
76049
76371
  }
76050
76372
  if (this._state > RUNNING) return abortHandshake(socket, 503);
76051
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
76373
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
76052
76374
  const headers = [
76053
76375
  "HTTP/1.1 101 Switching Protocols",
76054
76376
  "Upgrade: websocket",
@@ -143186,6 +143508,186 @@ var init_audio_jobs = __esm({
143186
143508
  }
143187
143509
  });
143188
143510
 
143511
+ // src/memory/feedback-model.ts
143512
+ function feedbackWeightMultiplier(weight) {
143513
+ return Math.max(0.35, Math.min(1.4, 0.35 + Math.max(0, Math.min(2, weight)) * 0.525));
143514
+ }
143515
+ var init_feedback_model = __esm({
143516
+ "src/memory/feedback-model.ts"() {
143517
+ "use strict";
143518
+ init_esm_shims();
143519
+ }
143520
+ });
143521
+
143522
+ // src/memory/feedback.ts
143523
+ var feedback_exports = {};
143524
+ __export(feedback_exports, {
143525
+ FEEDBACK_DELTAS: () => FEEDBACK_DELTAS,
143526
+ MemoryFeedbackStore: () => MemoryFeedbackStore,
143527
+ applyMemoryFeedback: () => applyMemoryFeedback,
143528
+ feedbackWeightMultiplier: () => feedbackWeightMultiplier
143529
+ });
143530
+ import { randomUUID as randomUUID7 } from "crypto";
143531
+ function applyMemoryFeedback(current, event) {
143532
+ const delta = event.signal === "revoke" ? 0 : FEEDBACK_DELTAS[event.signal];
143533
+ const weight = Math.max(0, Math.min(2, current.weight + delta));
143534
+ const status = (event.signal === "code-conflict" || event.signal === "user-correction" || event.signal === "weaken") && weight < 0.5 ? "archived" : current.status;
143535
+ return { weight, status, audit: [...current.audit, { ...event }] };
143536
+ }
143537
+ function rowToEvent(row) {
143538
+ return {
143539
+ id: String(row.id),
143540
+ projectId: String(row.project_id),
143541
+ candidateKind: row.candidate_kind,
143542
+ candidateId: String(row.candidate_id),
143543
+ signal: row.signal,
143544
+ ...row.observed_at ? { at: String(row.observed_at) } : { at: (/* @__PURE__ */ new Date()).toISOString() },
143545
+ ...row.actor ? { actor: String(row.actor) } : {},
143546
+ ...row.note ? { note: String(row.note) } : {},
143547
+ ...row.source_ref ? { sourceRef: String(row.source_ref) } : {},
143548
+ ...row.target_event_id ? { targetEventId: String(row.target_event_id) } : {},
143549
+ delta: Number(row.delta ?? 0)
143550
+ };
143551
+ }
143552
+ var FEEDBACK_DELTAS, MemoryFeedbackStore;
143553
+ var init_feedback = __esm({
143554
+ "src/memory/feedback.ts"() {
143555
+ "use strict";
143556
+ init_esm_shims();
143557
+ init_sqlite_db();
143558
+ init_feedback_model();
143559
+ FEEDBACK_DELTAS = {
143560
+ "user-correction": -0.35,
143561
+ "verification-success": 0.25,
143562
+ "verification-failure": -0.25,
143563
+ "code-change": -0.15,
143564
+ used: 0.1,
143565
+ "not-used": -0.05,
143566
+ "code-conflict": -0.4,
143567
+ strengthen: 0.2,
143568
+ weaken: -0.2
143569
+ };
143570
+ MemoryFeedbackStore = class {
143571
+ db = null;
143572
+ async init(dataDir) {
143573
+ this.db = getDatabase(dataDir);
143574
+ }
143575
+ requireDb() {
143576
+ if (!this.db) throw new Error("MemoryFeedbackStore is not initialized.");
143577
+ return this.db;
143578
+ }
143579
+ loadEvents(projectId, candidateKind, candidateId) {
143580
+ return this.requireDb().prepare(`
143581
+ SELECT * FROM memory_feedback_events
143582
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143583
+ ORDER BY observed_at ASC, rowid ASC
143584
+ `).all(projectId, candidateKind, candidateId).map(rowToEvent);
143585
+ }
143586
+ rebuild(projectId, candidateKind, candidateId) {
143587
+ const events = this.loadEvents(projectId, candidateKind, candidateId);
143588
+ const revoked = new Set(events.filter((event) => event.signal === "revoke" && event.targetEventId).map((event) => event.targetEventId));
143589
+ let state = { weight: 1, status: "active", audit: [] };
143590
+ for (const event of events) {
143591
+ if (event.signal === "revoke" || revoked.has(event.id)) {
143592
+ state.audit.push(event);
143593
+ continue;
143594
+ }
143595
+ state = applyMemoryFeedback(state, event);
143596
+ }
143597
+ this.requireDb().prepare(`
143598
+ INSERT INTO memory_feedback_states (project_id, candidate_kind, candidate_id, weight, lifecycle_status, updated_at)
143599
+ VALUES (?, ?, ?, ?, ?, ?)
143600
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
143601
+ weight = excluded.weight, lifecycle_status = excluded.lifecycle_status, updated_at = excluded.updated_at
143602
+ `).run(projectId, candidateKind, candidateId, state.weight, state.status, (/* @__PURE__ */ new Date()).toISOString());
143603
+ return state;
143604
+ }
143605
+ getState(projectId, candidateKind, candidateId) {
143606
+ const row = this.requireDb().prepare(`
143607
+ SELECT weight, lifecycle_status FROM memory_feedback_states
143608
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143609
+ `).get(projectId, candidateKind, candidateId);
143610
+ if (!row) return { weight: 1, status: "active", audit: [] };
143611
+ return {
143612
+ weight: Number(row.weight ?? 1),
143613
+ status: row.lifecycle_status ?? "active",
143614
+ audit: this.loadEvents(projectId, candidateKind, candidateId)
143615
+ };
143616
+ }
143617
+ listStates(projectId, candidateKind = "observation") {
143618
+ return this.requireDb().prepare(`
143619
+ SELECT candidate_id, weight, lifecycle_status, updated_at
143620
+ FROM memory_feedback_states WHERE project_id = ? AND candidate_kind = ?
143621
+ ORDER BY updated_at DESC
143622
+ `).all(projectId, candidateKind).map((row) => ({
143623
+ candidateId: String(row.candidate_id),
143624
+ weight: Number(row.weight),
143625
+ status: row.lifecycle_status,
143626
+ updatedAt: String(row.updated_at)
143627
+ }));
143628
+ }
143629
+ weights(projectId, candidateKind = "observation") {
143630
+ const states = /* @__PURE__ */ new Map();
143631
+ for (const row of this.listStates(projectId, candidateKind)) {
143632
+ states.set(row.candidateId, this.getState(projectId, candidateKind, row.candidateId));
143633
+ }
143634
+ return states;
143635
+ }
143636
+ record(input) {
143637
+ if (input.signal === "revoke" && !input.targetEventId) {
143638
+ throw new Error("A revoke feedback event requires targetEventId.");
143639
+ }
143640
+ if (input.signal === "revoke") {
143641
+ const target = this.requireDb().prepare(`
143642
+ SELECT 1 FROM memory_feedback_events
143643
+ WHERE id = ? AND project_id = ? AND candidate_kind = ? AND candidate_id = ?
143644
+ `).get(input.targetEventId, input.projectId, input.candidateKind, input.candidateId);
143645
+ if (!target) throw new Error("The feedback event to revoke was not found in the same candidate scope.");
143646
+ }
143647
+ const event = {
143648
+ id: randomUUID7(),
143649
+ projectId: input.projectId,
143650
+ candidateKind: input.candidateKind,
143651
+ candidateId: input.candidateId,
143652
+ signal: input.signal,
143653
+ sourceRef: input.sourceRef,
143654
+ ...input.actor ? { actor: input.actor } : {},
143655
+ ...input.note ? { note: input.note.slice(0, 2e3) } : {},
143656
+ ...input.targetEventId ? { targetEventId: input.targetEventId } : {},
143657
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
143658
+ delta: input.signal === "revoke" ? 0 : FEEDBACK_DELTAS[input.signal]
143659
+ };
143660
+ this.requireDb().prepare(`
143661
+ INSERT INTO memory_feedback_events (
143662
+ id, project_id, candidate_kind, candidate_id, signal, actor, note,
143663
+ source_ref, target_event_id, delta, observed_at
143664
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
143665
+ `).run(
143666
+ event.id,
143667
+ event.projectId,
143668
+ event.candidateKind,
143669
+ event.candidateId,
143670
+ event.signal,
143671
+ event.actor ?? null,
143672
+ event.note ?? null,
143673
+ event.sourceRef,
143674
+ event.targetEventId ?? null,
143675
+ event.delta,
143676
+ event.at
143677
+ );
143678
+ return { event, state: this.rebuild(input.projectId, input.candidateKind, input.candidateId) };
143679
+ }
143680
+ audit(projectId, candidateKind, candidateId, limit2 = 100) {
143681
+ return this.requireDb().prepare(`
143682
+ SELECT * FROM memory_feedback_events
143683
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143684
+ ORDER BY observed_at DESC, rowid DESC LIMIT ?
143685
+ `).all(projectId, candidateKind, candidateId, Math.min(200, Math.max(1, Math.floor(limit2)))).map(rowToEvent);
143686
+ }
143687
+ };
143688
+ }
143689
+ });
143690
+
143189
143691
  // src/memory/retention.ts
143190
143692
  var retention_exports = {};
143191
143693
  __export(retention_exports, {
@@ -143227,6 +143729,7 @@ function getEffectiveRetentionDays(doc) {
143227
143729
  }
143228
143730
  function isImmune(doc) {
143229
143731
  if (doc.type === "probe") return false;
143732
+ if (doc.feedbackStatus === "archived") return false;
143230
143733
  if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
143231
143734
  if (doc.valueCategory === "core") return true;
143232
143735
  const importance = getImportanceLevel(doc);
@@ -143259,7 +143762,8 @@ function calculateRelevance(doc, referenceTime) {
143259
143762
  const decayFactor = Math.exp(-ageDays / retention);
143260
143763
  const accessCount = doc.accessCount ?? 0;
143261
143764
  const accessBoost = Math.min(2, 1 + 0.1 * accessCount);
143262
- let totalScore = base * decayFactor * accessBoost;
143765
+ const feedbackMultiplier = doc.feedbackWeight === void 0 ? 1 : feedbackWeightMultiplier(doc.feedbackWeight);
143766
+ let totalScore = base * decayFactor * accessBoost * feedbackMultiplier;
143263
143767
  const immune = isImmune(doc);
143264
143768
  if (immune) {
143265
143769
  totalScore = Math.max(totalScore, 0.5);
@@ -143271,7 +143775,8 @@ function calculateRelevance(doc, referenceTime) {
143271
143775
  decayFactor,
143272
143776
  accessBoost,
143273
143777
  ageDays,
143274
- isImmune: immune
143778
+ isImmune: immune,
143779
+ feedbackMultiplier
143275
143780
  };
143276
143781
  }
143277
143782
  function rankByRelevance(docs, referenceTime) {
@@ -143279,6 +143784,7 @@ function rankByRelevance(docs, referenceTime) {
143279
143784
  }
143280
143785
  function getRetentionZone(doc, referenceTime) {
143281
143786
  const now3 = referenceTime ?? /* @__PURE__ */ new Date();
143787
+ if (doc.feedbackStatus === "archived") return "archive-candidate";
143282
143788
  const importance = getImportanceLevel(doc);
143283
143789
  const retention = getEffectiveRetentionDays(doc);
143284
143790
  const createdAt = new Date(doc.createdAt);
@@ -143350,7 +143856,7 @@ function explainRetention(doc, referenceTime) {
143350
143856
  summary: parts.join(" ; ")
143351
143857
  };
143352
143858
  }
143353
- function toRetentionDocument(obs, accessMap) {
143859
+ function toRetentionDocument(obs, accessMap, feedback) {
143354
143860
  const access2 = accessMap?.get(obs.id);
143355
143861
  return {
143356
143862
  id: `obs-${obs.id}`,
@@ -143372,11 +143878,12 @@ function toRetentionDocument(obs, accessMap) {
143372
143878
  sourceDetail: obs.sourceDetail ?? "",
143373
143879
  valueCategory: obs.valueCategory ?? "",
143374
143880
  admissionState: obs.admissionState ?? "",
143375
- admissionReason: obs.admissionReason ?? ""
143881
+ admissionReason: obs.admissionReason ?? "",
143882
+ ...feedback ? { feedbackWeight: feedback.weight, feedbackStatus: feedback.status } : {}
143376
143883
  };
143377
143884
  }
143378
143885
  function projectObservationRetention(observation, options = {}) {
143379
- const document2 = toRetentionDocument(observation, options.accessMap);
143886
+ const document2 = toRetentionDocument(observation, options.accessMap, options.feedback);
143380
143887
  const relevance = calculateRelevance(document2, options.referenceTime);
143381
143888
  const explanation = explainRetention(document2, options.referenceTime);
143382
143889
  return {
@@ -143401,7 +143908,17 @@ function summarizeRetentionProjections(projections) {
143401
143908
  }
143402
143909
  return { active, stale, archiveCandidates, immune };
143403
143910
  }
143404
- async function archiveExpiredBatch(_projectDir, options) {
143911
+ async function loadFeedbackStore(dataDir) {
143912
+ try {
143913
+ const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
143914
+ const store = new MemoryFeedbackStore2();
143915
+ await store.init(dataDir);
143916
+ return store;
143917
+ } catch {
143918
+ return void 0;
143919
+ }
143920
+ }
143921
+ async function archiveExpiredBatch(projectDir2, options) {
143405
143922
  const store = getObservationStore();
143406
143923
  const limit2 = Math.min(500, Math.max(1, Math.floor(options.limit ?? 100)));
143407
143924
  const afterId = Math.max(0, Math.floor(options.afterId ?? 0));
@@ -143410,11 +143927,13 @@ async function archiveExpiredBatch(_projectDir, options) {
143410
143927
  afterId,
143411
143928
  limit: limit2 + 1
143412
143929
  });
143930
+ const feedback = await loadFeedbackStore(projectDir2);
143413
143931
  const hasMore = page.length > limit2;
143414
143932
  const scanned = hasMore ? page.slice(0, limit2) : page;
143415
143933
  const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => projectObservationRetention(observation, {
143416
143934
  accessMap: options.accessMap,
143417
- referenceTime: options.referenceTime
143935
+ referenceTime: options.referenceTime,
143936
+ ...feedback ? { feedback: feedback.getState(options.projectId, "observation", String(observation.id)) } : {}
143418
143937
  }).zone === "archive-candidate").map((observation) => observation.id);
143419
143938
  const archived = candidateIds.length === 0 ? 0 : await store.atomic(async (tx) => {
143420
143939
  const updates = await Promise.all(
@@ -143445,6 +143964,7 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
143445
143964
  const remaining = reader ? remainingObservations.filter((observation) => canManageObservation(observation, reader)).length : remainingObservations.length;
143446
143965
  return { archived, remaining };
143447
143966
  }
143967
+ const feedback = await loadFeedbackStore(projectDir2);
143448
143968
  return await store.atomic(async (tx) => {
143449
143969
  const allObs = await tx.loadAll();
143450
143970
  const activeObs = allObs.filter(
@@ -143452,7 +143972,11 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
143452
143972
  );
143453
143973
  const archivedIds = [];
143454
143974
  for (const obs of activeObs) {
143455
- const retention = projectObservationRetention(obs, { accessMap, referenceTime });
143975
+ const retention = projectObservationRetention(obs, {
143976
+ accessMap,
143977
+ referenceTime,
143978
+ ...feedback ? { feedback: feedback.getState(obs.projectId, "observation", String(obs.id)) } : {}
143979
+ });
143456
143980
  if (retention.zone === "archive-candidate") {
143457
143981
  archivedIds.push(obs.id);
143458
143982
  }
@@ -143472,6 +143996,7 @@ var init_retention = __esm({
143472
143996
  init_esm_shims();
143473
143997
  init_obs_store();
143474
143998
  init_visibility();
143999
+ init_feedback_model();
143475
144000
  RETENTION_DAYS = {
143476
144001
  critical: 365,
143477
144002
  high: 180,
@@ -143508,7 +144033,7 @@ var init_retention = __esm({
143508
144033
  });
143509
144034
 
143510
144035
  // src/memory/owner.ts
143511
- import { randomUUID as randomUUID6 } from "crypto";
144036
+ import { randomUUID as randomUUID8 } from "crypto";
143512
144037
  import { promises as fs9 } from "fs";
143513
144038
  import path16 from "path";
143514
144039
  function ownerPath(dataDir) {
@@ -143538,7 +144063,7 @@ async function resolveLocalMemoryOwner(dataDir, options = {}) {
143538
144063
  await fs9.mkdir(path16.dirname(filePath), { recursive: true });
143539
144064
  const created = {
143540
144065
  version: 1,
143541
- id: "local-" + randomUUID6(),
144066
+ id: "local-" + randomUUID8(),
143542
144067
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
143543
144068
  };
143544
144069
  try {
@@ -143561,7 +144086,7 @@ var init_owner = __esm({
143561
144086
  });
143562
144087
 
143563
144088
  // src/memory/long-term-store.ts
143564
- import { randomUUID as randomUUID7 } from "crypto";
144089
+ import { randomUUID as randomUUID9 } from "crypto";
143565
144090
  function optionalText2(value) {
143566
144091
  return typeof value === "string" && value.trim() ? value : void 0;
143567
144092
  }
@@ -143612,7 +144137,7 @@ function rowToEvidence(row) {
143612
144137
  createdAt: row.createdAt
143613
144138
  };
143614
144139
  }
143615
- function rowToEvent(row) {
144140
+ function rowToEvent2(row) {
143616
144141
  return {
143617
144142
  id: row.id,
143618
144143
  memoryId: row.memoryId,
@@ -143721,7 +144246,7 @@ var init_long_term_store = __esm({
143721
144246
  }
143722
144247
  insertEvidence(input) {
143723
144248
  const evidence = {
143724
- id: randomUUID7(),
144249
+ id: randomUUID9(),
143725
144250
  ...input,
143726
144251
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
143727
144252
  };
@@ -143748,7 +144273,7 @@ var init_long_term_store = __esm({
143748
144273
  }
143749
144274
  recordEvent(input) {
143750
144275
  const event = {
143751
- id: randomUUID7(),
144276
+ id: randomUUID9(),
143752
144277
  ...input,
143753
144278
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
143754
144279
  };
@@ -143769,7 +144294,7 @@ var init_long_term_store = __esm({
143769
144294
  listEvents(memoryId) {
143770
144295
  return this.db.prepare(`
143771
144296
  SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
143772
- `).all(memoryId).map(rowToEvent);
144297
+ `).all(memoryId).map(rowToEvent2);
143773
144298
  }
143774
144299
  markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
143775
144300
  const unique = [...new Set(ids.filter(Boolean))];
@@ -143838,7 +144363,7 @@ __export(long_term_exports, {
143838
144363
  selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
143839
144364
  supersedeLongTermMemory: () => supersedeLongTermMemory
143840
144365
  });
143841
- import { randomUUID as randomUUID8 } from "crypto";
144366
+ import { randomUUID as randomUUID10 } from "crypto";
143842
144367
  function isOneOf(value, values) {
143843
144368
  return typeof value === "string" && values.includes(value);
143844
144369
  }
@@ -144020,7 +144545,7 @@ async function createManualLongTermMemory(input) {
144020
144545
  const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
144021
144546
  if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
144022
144547
  assertScope(input.scope);
144023
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
144548
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
144024
144549
  return createLongTermMemoryWithDataDir({
144025
144550
  dataDir: input.dataDir,
144026
144551
  originProjectId: input.projectId,
@@ -144075,7 +144600,7 @@ async function createLongTermMemoryWithDataDir(input) {
144075
144600
  assertPortableEvidence(input.scope, portability, evidenceInputs);
144076
144601
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
144077
144602
  const memory = {
144078
- id: randomUUID8(),
144603
+ id: randomUUID10(),
144079
144604
  originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
144080
144605
  ownerId: compactText(input.ownerId, "owner id", 160),
144081
144606
  scope: input.scope,
@@ -144393,9 +144918,9 @@ var init_long_term = __esm({
144393
144918
  });
144394
144919
 
144395
144920
  // src/codegraph/ids.ts
144396
- import { createHash as createHash5 } from "crypto";
144921
+ import { createHash as createHash6 } from "crypto";
144397
144922
  function digest(input) {
144398
- return createHash5("sha256").update(input).digest("hex").slice(0, 16);
144923
+ return createHash6("sha256").update(input).digest("hex").slice(0, 16);
144399
144924
  }
144400
144925
  function normalizeCodePath(path22) {
144401
144926
  return path22.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -144430,7 +144955,7 @@ var store_exports = {};
144430
144955
  __export(store_exports, {
144431
144956
  CodeGraphStore: () => CodeGraphStore
144432
144957
  });
144433
- import { randomUUID as randomUUID9 } from "crypto";
144958
+ import { randomUUID as randomUUID11 } from "crypto";
144434
144959
  import { posix as pathPosix } from "path";
144435
144960
  function rowToFile(row) {
144436
144961
  return {
@@ -145147,7 +145672,7 @@ var init_store = __esm({
145147
145672
  * so an interrupted scan cannot be advertised as complete.
145148
145673
  */
145149
145674
  recordCodeStateSnapshot(input) {
145150
- const id = randomUUID9();
145675
+ const id = randomUUID11();
145151
145676
  let snapshot;
145152
145677
  const tx = this.db.transaction(() => {
145153
145678
  const previous = this.db.prepare(
@@ -145225,7 +145750,7 @@ var init_store = __esm({
145225
145750
 
145226
145751
  // src/codegraph/code-state.ts
145227
145752
  import { execFile as execFile2 } from "child_process";
145228
- import { createHash as createHash6 } from "crypto";
145753
+ import { createHash as createHash7 } from "crypto";
145229
145754
  import { promisify as promisify3 } from "util";
145230
145755
  async function runGit(rootPath, args) {
145231
145756
  try {
@@ -145254,7 +145779,7 @@ function changedPathCount(status) {
145254
145779
  return count2;
145255
145780
  }
145256
145781
  function fingerprint(baseRevision, status) {
145257
- return createHash6("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
145782
+ return createHash7("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
145258
145783
  }
145259
145784
  async function collectCodeStateSnapshot(input) {
145260
145785
  const [revision, status] = await Promise.all([
@@ -145354,11 +145879,11 @@ __export(lite_provider_exports, {
145354
145879
  indexProjectLite: () => indexProjectLite,
145355
145880
  refreshProjectLite: () => refreshProjectLite
145356
145881
  });
145357
- import { createHash as createHash7 } from "crypto";
145882
+ import { createHash as createHash8 } from "crypto";
145358
145883
  import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
145359
145884
  import { join as join4, relative } from "path";
145360
145885
  function hashText(text2) {
145361
- return createHash7("sha256").update(text2).digest("hex");
145886
+ return createHash8("sha256").update(text2).digest("hex");
145362
145887
  }
145363
145888
  function extension2(path22) {
145364
145889
  const index = path22.lastIndexOf(".");
@@ -145925,7 +146450,7 @@ var claim_store_exports = {};
145925
146450
  __export(claim_store_exports, {
145926
146451
  ClaimStore: () => ClaimStore
145927
146452
  });
145928
- import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
146453
+ import { createHash as createHash9, randomUUID as randomUUID12 } from "crypto";
145929
146454
  function optionalText3(value) {
145930
146455
  return typeof value === "string" && value ? value : void 0;
145931
146456
  }
@@ -145964,7 +146489,7 @@ function rowToEvidence2(row) {
145964
146489
  createdAt: row.createdAt
145965
146490
  };
145966
146491
  }
145967
- function rowToEvent2(row) {
146492
+ function rowToEvent3(row) {
145968
146493
  return {
145969
146494
  id: row.id,
145970
146495
  projectId: row.projectId,
@@ -145978,7 +146503,7 @@ function rowToEvent2(row) {
145978
146503
  };
145979
146504
  }
145980
146505
  function evidenceKey(input) {
145981
- return createHash8("sha256").update(JSON.stringify([
146506
+ return createHash9("sha256").update(JSON.stringify([
145982
146507
  input.evidenceKind,
145983
146508
  input.evidenceId,
145984
146509
  input.relation,
@@ -146109,7 +146634,7 @@ var init_claim_store = __esm({
146109
146634
  locator, capturedHash, evidenceKey, createdAt
146110
146635
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
146111
146636
  `).run(
146112
- randomUUID10(),
146637
+ randomUUID12(),
146113
146638
  input.claimId,
146114
146639
  input.evidenceKind,
146115
146640
  input.evidenceId,
@@ -146131,7 +146656,7 @@ var init_claim_store = __esm({
146131
146656
  }
146132
146657
  recordEvent(input) {
146133
146658
  const event = {
146134
- id: randomUUID10(),
146659
+ id: randomUUID12(),
146135
146660
  ...input,
146136
146661
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
146137
146662
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -146159,7 +146684,7 @@ var init_claim_store = __esm({
146159
146684
  SELECT * FROM knowledge_claim_events
146160
146685
  WHERE claimId = ?
146161
146686
  ORDER BY createdAt ASC, id
146162
- `).all(claimId).map(rowToEvent2);
146687
+ `).all(claimId).map(rowToEvent3);
146163
146688
  }
146164
146689
  };
146165
146690
  }
@@ -146176,7 +146701,7 @@ __export(claims_exports, {
146176
146701
  supersedeClaim: () => supersedeClaim,
146177
146702
  writeClaim: () => writeClaim
146178
146703
  });
146179
- import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
146704
+ import { createHash as createHash10, randomUUID as randomUUID13 } from "crypto";
146180
146705
  function compactText2(value, field) {
146181
146706
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
146182
146707
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -146188,7 +146713,7 @@ function normalizedTerm(value) {
146188
146713
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
146189
146714
  }
146190
146715
  function hashIdentity(parts) {
146191
- return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
146716
+ return createHash10("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
146192
146717
  }
146193
146718
  function buildClaimIdentity(input) {
146194
146719
  const subject = normalizedTerm(input.subject);
@@ -146262,7 +146787,7 @@ function normalizeClaimInput(input) {
146262
146787
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
146263
146788
  return {
146264
146789
  claim: {
146265
- id: randomUUID11(),
146790
+ id: randomUUID13(),
146266
146791
  projectId,
146267
146792
  subject,
146268
146793
  predicate,
@@ -146429,7 +146954,7 @@ function supersedeClaim(store, input) {
146429
146954
  });
146430
146955
  }
146431
146956
  function hashObservation(observation) {
146432
- return createHash9("sha256").update(JSON.stringify([
146957
+ return createHash10("sha256").update(JSON.stringify([
146433
146958
  observation.title,
146434
146959
  observation.narrative,
146435
146960
  observation.facts,
@@ -146673,11 +147198,11 @@ var init_frontmatter = __esm({
146673
147198
  });
146674
147199
 
146675
147200
  // src/knowledge/markdown.ts
146676
- import { createHash as createHash10 } from "crypto";
147201
+ import { createHash as createHash11 } from "crypto";
146677
147202
  import { promises as fs10 } from "fs";
146678
147203
  import path17 from "path";
146679
147204
  function hashContent(content) {
146680
- return createHash10("sha256").update(content).digest("hex");
147205
+ return createHash11("sha256").update(content).digest("hex");
146681
147206
  }
146682
147207
  function stringField(data, key) {
146683
147208
  const value = data[key];
@@ -146803,11 +147328,11 @@ __export(wiki_exports, {
146803
147328
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
146804
147329
  readKnowledgePage: () => readKnowledgePage
146805
147330
  });
146806
- import { createHash as createHash11 } from "crypto";
147331
+ import { createHash as createHash12 } from "crypto";
146807
147332
  import { promises as fs11 } from "fs";
146808
147333
  import path18 from "path";
146809
147334
  function hash2(value) {
146810
- return createHash11("sha256").update(value).digest("hex");
147335
+ return createHash12("sha256").update(value).digest("hex");
146811
147336
  }
146812
147337
  function now() {
146813
147338
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -147347,7 +147872,7 @@ var init_workflow_sync = __esm({
147347
147872
  });
147348
147873
 
147349
147874
  // src/knowledge/workflow-store.ts
147350
- import { randomUUID as randomUUID12 } from "crypto";
147875
+ import { randomUUID as randomUUID14 } from "crypto";
147351
147876
  function parseJson(value, fallback) {
147352
147877
  if (typeof value !== "string" || !value) return fallback;
147353
147878
  try {
@@ -147447,7 +147972,7 @@ var init_workflow_store = __esm({
147447
147972
  }
147448
147973
  recordRun(input) {
147449
147974
  const run = {
147450
- id: randomUUID12(),
147975
+ id: randomUUID14(),
147451
147976
  workflowId: input.workflowId,
147452
147977
  projectId: input.projectId,
147453
147978
  task: input.task.trim(),
@@ -147515,7 +148040,7 @@ var init_outcome_types = __esm({
147515
148040
  });
147516
148041
 
147517
148042
  // src/knowledge/outcome-store.ts
147518
- import { randomUUID as randomUUID13 } from "crypto";
148043
+ import { randomUUID as randomUUID15 } from "crypto";
147519
148044
  function optionalText5(value) {
147520
148045
  return typeof value === "string" && value.trim() ? value : void 0;
147521
148046
  }
@@ -147559,7 +148084,7 @@ var init_outcome_store = __esm({
147559
148084
  }
147560
148085
  record(input) {
147561
148086
  const signal = {
147562
- id: randomUUID13(),
148087
+ id: randomUUID15(),
147563
148088
  projectId: text(input.projectId, "project id"),
147564
148089
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
147565
148090
  candidateId: text(input.candidateId, "candidate id"),
@@ -147664,11 +148189,11 @@ __export(workflows_exports, {
147664
148189
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
147665
148190
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
147666
148191
  });
147667
- import { createHash as createHash12 } from "crypto";
148192
+ import { createHash as createHash13 } from "crypto";
147668
148193
  import { promises as fs12 } from "fs";
147669
148194
  import path19 from "path";
147670
148195
  function hash3(value) {
147671
- return createHash12("sha256").update(value).digest("hex");
148196
+ return createHash13("sha256").update(value).digest("hex");
147672
148197
  }
147673
148198
  function now2() {
147674
148199
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -149050,6 +149575,16 @@ async function bindObservationCodeRefsBestEffort(observation) {
149050
149575
  } catch {
149051
149576
  }
149052
149577
  }
149578
+ async function persistEvidenceCard(observation) {
149579
+ if (!projectDir) return;
149580
+ try {
149581
+ const evidence = new EvidenceCardStore();
149582
+ await evidence.init(projectDir);
149583
+ evidence.upsertObservation(observation);
149584
+ } catch (error) {
149585
+ console.error(`[memorix] Evidence card projection failed for obs-${observation.id}: ${error instanceof Error ? error.message : String(error)}`);
149586
+ }
149587
+ }
149053
149588
  function queueClaimDerivation(observation) {
149054
149589
  const dataDir = projectDir;
149055
149590
  const isExplicit = observation.sourceDetail === "explicit";
@@ -149396,6 +149931,7 @@ async function storeObservation(input) {
149396
149931
  }
149397
149932
  if (!persistedCallbackInvoked) await input.onPersisted?.(observation);
149398
149933
  await bindObservationCodeRefsBestEffort(observation);
149934
+ await persistEvidenceCard(observation);
149399
149935
  queueObservationQualification(observation);
149400
149936
  if (resolveObservationVisibility(observation) === "project") {
149401
149937
  queueClaimDerivation(observation);
@@ -149488,6 +150024,7 @@ async function upsertObservation(existing, input, now3) {
149488
150024
  await store.update(existing);
149489
150025
  }
149490
150026
  await bindObservationCodeRefsBestEffort(existing);
150027
+ await persistEvidenceCard(existing);
149491
150028
  queueObservationQualification(existing);
149492
150029
  if (resolveObservationVisibility(existing) === "project") {
149493
150030
  queueClaimDerivation(existing);
@@ -149621,6 +150158,7 @@ async function resolveObservations(ids, status = "resolved") {
149621
150158
  await Promise.all(changed.map((observation) => tx.update(observation)));
149622
150159
  });
149623
150160
  }
150161
+ for (const observation of changed) await persistEvidenceCard(observation);
149624
150162
  return { resolved, notFound };
149625
150163
  }
149626
150164
  function getProjectObservations(projectId) {
@@ -149958,6 +150496,7 @@ var init_observations = __esm({
149958
150496
  init_secret_filter();
149959
150497
  init_lifecycle();
149960
150498
  init_visibility();
150499
+ init_evidence_store();
149961
150500
  observations = [];
149962
150501
  nextId = 1;
149963
150502
  projectDir = null;