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
package/dist/sdk.js CHANGED
@@ -437,7 +437,7 @@ function isSqliteAvailable() {
437
437
  return false;
438
438
  }
439
439
  }
440
- 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;
440
+ 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;
441
441
  var init_sqlite_db = __esm({
442
442
  "src/store/sqlite-db.ts"() {
443
443
  "use strict";
@@ -1083,6 +1083,79 @@ CREATE TABLE IF NOT EXISTS compaction_checkpoints (
1083
1083
  created_at TEXT NOT NULL,
1084
1084
  updated_at TEXT NOT NULL
1085
1085
  );
1086
+ `;
1087
+ CREATE_EVIDENCE_CARDS_TABLE = `
1088
+ CREATE TABLE IF NOT EXISTS evidence_cards (
1089
+ id TEXT PRIMARY KEY,
1090
+ project_id TEXT NOT NULL,
1091
+ candidate_kind TEXT NOT NULL,
1092
+ candidate_id TEXT NOT NULL,
1093
+ title TEXT NOT NULL,
1094
+ summary TEXT NOT NULL DEFAULT '',
1095
+ entity_name TEXT,
1096
+ source_kind TEXT NOT NULL,
1097
+ source_ref TEXT NOT NULL,
1098
+ locator TEXT,
1099
+ captured_hash TEXT,
1100
+ session_id TEXT,
1101
+ status TEXT NOT NULL DEFAULT 'active',
1102
+ verification TEXT NOT NULL DEFAULT 'unverified',
1103
+ freshness TEXT NOT NULL DEFAULT 'current',
1104
+ stale_reason TEXT,
1105
+ files_json TEXT NOT NULL DEFAULT '[]',
1106
+ related_entities_json TEXT NOT NULL DEFAULT '[]',
1107
+ created_at TEXT NOT NULL,
1108
+ updated_at TEXT NOT NULL,
1109
+ UNIQUE(project_id, candidate_kind, candidate_id)
1110
+ );
1111
+ `;
1112
+ CREATE_EVIDENCE_CARD_EVENTS_TABLE = `
1113
+ CREATE TABLE IF NOT EXISTS evidence_card_events (
1114
+ id TEXT PRIMARY KEY,
1115
+ project_id TEXT NOT NULL,
1116
+ card_id TEXT NOT NULL,
1117
+ kind TEXT NOT NULL,
1118
+ detail TEXT,
1119
+ created_at TEXT NOT NULL,
1120
+ FOREIGN KEY (card_id) REFERENCES evidence_cards(id) ON DELETE CASCADE
1121
+ );
1122
+ `;
1123
+ CREATE_MEMORY_FEEDBACK_STATES_TABLE = `
1124
+ CREATE TABLE IF NOT EXISTS memory_feedback_states (
1125
+ project_id TEXT NOT NULL,
1126
+ candidate_kind TEXT NOT NULL,
1127
+ candidate_id TEXT NOT NULL,
1128
+ weight REAL NOT NULL DEFAULT 1.0,
1129
+ lifecycle_status TEXT NOT NULL DEFAULT 'active',
1130
+ updated_at TEXT NOT NULL,
1131
+ PRIMARY KEY (project_id, candidate_kind, candidate_id)
1132
+ );
1133
+ `;
1134
+ CREATE_MEMORY_FEEDBACK_EVENTS_TABLE = `
1135
+ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1136
+ id TEXT PRIMARY KEY,
1137
+ project_id TEXT NOT NULL,
1138
+ candidate_kind TEXT NOT NULL,
1139
+ candidate_id TEXT NOT NULL,
1140
+ signal TEXT NOT NULL,
1141
+ actor TEXT,
1142
+ note TEXT,
1143
+ source_ref TEXT NOT NULL,
1144
+ target_event_id TEXT,
1145
+ delta REAL NOT NULL DEFAULT 0,
1146
+ observed_at TEXT NOT NULL
1147
+ );
1148
+ `;
1149
+ CREATE_MCP_BINDINGS_TABLE = `
1150
+ CREATE TABLE IF NOT EXISTS mcp_bindings (
1151
+ handle_id TEXT PRIMARY KEY,
1152
+ project_id TEXT NOT NULL,
1153
+ project_root TEXT NOT NULL,
1154
+ data_dir TEXT NOT NULL,
1155
+ created_at TEXT NOT NULL,
1156
+ last_used_at TEXT NOT NULL,
1157
+ expires_at TEXT
1158
+ );
1086
1159
  `;
1087
1160
  CREATE_INDEXES = `
1088
1161
  CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
@@ -1145,6 +1218,12 @@ CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_target
1145
1218
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
1146
1219
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
1147
1220
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
1221
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC);
1222
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id);
1223
+ CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1224
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1225
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1226
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1148
1227
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1149
1228
  ON maintenance_jobs(project_id, kind, dedupe_key)
1150
1229
  WHERE status IN ('pending', 'running', 'retry');
@@ -1300,6 +1379,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1300
1379
  }
1301
1380
  db2.exec("CREATE INDEX IF NOT EXISTS idx_media_jobs_source_asset ON media_jobs(project_id, source_asset_id, status)");
1302
1381
  }
1382
+ },
1383
+ {
1384
+ id: "1.8-context-control-plane",
1385
+ apply: (db2) => {
1386
+ db2.exec(CREATE_EVIDENCE_CARDS_TABLE);
1387
+ db2.exec(CREATE_EVIDENCE_CARD_EVENTS_TABLE);
1388
+ db2.exec(CREATE_MEMORY_FEEDBACK_STATES_TABLE);
1389
+ db2.exec(CREATE_MEMORY_FEEDBACK_EVENTS_TABLE);
1390
+ db2.exec(CREATE_MCP_BINDINGS_TABLE);
1391
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC)");
1392
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id)");
1393
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at)");
1394
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1395
+ 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)");
1396
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1397
+ }
1303
1398
  }
1304
1399
  ];
1305
1400
  _dbCache = /* @__PURE__ */ new Map();
@@ -3150,6 +3245,8 @@ var init_toml_loader = __esm({
3150
3245
  var dotenv_loader_exports = {};
3151
3246
  __export(dotenv_loader_exports, {
3152
3247
  getLoadedEnvFiles: () => getLoadedEnvFiles,
3248
+ getLoadedEnvKeys: () => getLoadedEnvKeys,
3249
+ getLoadedEnvValue: () => getLoadedEnvValue,
3153
3250
  loadDotenv: () => loadDotenv,
3154
3251
  resetDotenv: () => resetDotenv
3155
3252
  });
@@ -3161,6 +3258,8 @@ function loadEnvFile(filePath) {
3161
3258
  const parsed = parse(readFileSync6(filePath, "utf-8"));
3162
3259
  for (const [key, value] of Object.entries(parsed)) {
3163
3260
  if (!(key in process.env)) {
3261
+ loadedEnvKeys.add(key);
3262
+ loadedEnvValues.set(key, value);
3164
3263
  process.env[key] = value;
3165
3264
  injectedKeys.add(key);
3166
3265
  }
@@ -3173,6 +3272,8 @@ function loadDotenv(projectRoot, options = {}) {
3173
3272
  resetDotenv();
3174
3273
  }
3175
3274
  loadedEnvFiles.length = 0;
3275
+ loadedEnvKeys.clear();
3276
+ loadedEnvValues.clear();
3176
3277
  if (projectRoot) {
3177
3278
  loadEnvFile(getProjectDotenvPath(projectRoot));
3178
3279
  }
@@ -3188,11 +3289,18 @@ function resetDotenv() {
3188
3289
  dotenvLoaded = false;
3189
3290
  dotenvProjectRoot = null;
3190
3291
  loadedEnvFiles.length = 0;
3292
+ loadedEnvKeys.clear();
3191
3293
  }
3192
3294
  function getLoadedEnvFiles() {
3193
3295
  return loadedEnvFiles;
3194
3296
  }
3195
- var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, injectedKeys;
3297
+ function getLoadedEnvKeys() {
3298
+ return loadedEnvKeys;
3299
+ }
3300
+ function getLoadedEnvValue(key) {
3301
+ return loadedEnvValues.get(key);
3302
+ }
3303
+ var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
3196
3304
  var init_dotenv_loader = __esm({
3197
3305
  "src/config/dotenv-loader.ts"() {
3198
3306
  "use strict";
@@ -3201,6 +3309,8 @@ var init_dotenv_loader = __esm({
3201
3309
  dotenvLoaded = false;
3202
3310
  dotenvProjectRoot = null;
3203
3311
  loadedEnvFiles = [];
3312
+ loadedEnvKeys = /* @__PURE__ */ new Set();
3313
+ loadedEnvValues = /* @__PURE__ */ new Map();
3204
3314
  injectedKeys = /* @__PURE__ */ new Set();
3205
3315
  }
3206
3316
  });
@@ -9291,6 +9401,226 @@ var init_lifecycle = __esm({
9291
9401
  }
9292
9402
  });
9293
9403
 
9404
+ // src/store/evidence-store.ts
9405
+ import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
9406
+ function safeJson(value, fallback) {
9407
+ if (typeof value !== "string" || value.length === 0) return fallback;
9408
+ try {
9409
+ return JSON.parse(value);
9410
+ } catch {
9411
+ return fallback;
9412
+ }
9413
+ }
9414
+ function rowToCard(row) {
9415
+ return {
9416
+ id: String(row.id),
9417
+ projectId: String(row.project_id),
9418
+ candidateKind: row.candidate_kind,
9419
+ candidateId: String(row.candidate_id),
9420
+ title: String(row.title),
9421
+ summary: String(row.summary ?? ""),
9422
+ ...row.entity_name ? { entityName: String(row.entity_name) } : {},
9423
+ sourceKind: String(row.source_kind),
9424
+ sourceRef: String(row.source_ref),
9425
+ ...row.locator ? { locator: String(row.locator) } : {},
9426
+ ...row.captured_hash ? { capturedHash: String(row.captured_hash) } : {},
9427
+ ...row.session_id ? { sessionId: String(row.session_id) } : {},
9428
+ status: String(row.status ?? "active"),
9429
+ verification: row.verification,
9430
+ freshness: row.freshness,
9431
+ ...row.stale_reason ? { staleReason: String(row.stale_reason) } : {},
9432
+ files: safeJson(row.files_json, []),
9433
+ relatedEntities: safeJson(row.related_entities_json, []),
9434
+ createdAt: String(row.created_at),
9435
+ updatedAt: String(row.updated_at)
9436
+ };
9437
+ }
9438
+ function observationHash(observation) {
9439
+ return createHash3("sha256").update(JSON.stringify({
9440
+ projectId: observation.projectId,
9441
+ id: observation.id,
9442
+ title: observation.title,
9443
+ narrative: observation.narrative,
9444
+ facts: observation.facts,
9445
+ files: observation.filesModified,
9446
+ relatedEntities: observation.relatedEntities
9447
+ })).digest("hex");
9448
+ }
9449
+ function sourceKind(observation) {
9450
+ if (observation.source === "git" || observation.sourceDetail === "git-ingest") return "git";
9451
+ if (observation.sourceDetail === "hook") return "hook";
9452
+ return observation.source ?? "agent";
9453
+ }
9454
+ var EvidenceCardStore;
9455
+ var init_evidence_store = __esm({
9456
+ "src/store/evidence-store.ts"() {
9457
+ "use strict";
9458
+ init_esm_shims();
9459
+ init_sqlite_db();
9460
+ EvidenceCardStore = class {
9461
+ db = null;
9462
+ async init(dataDir) {
9463
+ this.db = getDatabase(dataDir);
9464
+ }
9465
+ requireDb() {
9466
+ if (!this.db) throw new Error("EvidenceCardStore is not initialized.");
9467
+ return this.db;
9468
+ }
9469
+ upsertObservation(observation) {
9470
+ const db2 = this.requireDb();
9471
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
9472
+ const card = {
9473
+ id: `evidence:observation:${observation.projectId}:${observation.id}`,
9474
+ projectId: observation.projectId,
9475
+ candidateKind: "observation",
9476
+ candidateId: String(observation.id),
9477
+ title: observation.title,
9478
+ summary: observation.narrative.slice(0, 500),
9479
+ entityName: observation.entityName,
9480
+ sourceKind: sourceKind(observation),
9481
+ sourceRef: `observation:${observation.id}`,
9482
+ locator: observation.filesModified?.join(", ") || `observation:${observation.id}`,
9483
+ capturedHash: observationHash(observation),
9484
+ ...observation.sessionId ? { sessionId: observation.sessionId } : {},
9485
+ status: observation.status ?? "active",
9486
+ verification: observation.status === "resolved" ? "verified" : "unverified",
9487
+ freshness: "current",
9488
+ files: observation.filesModified ?? [],
9489
+ relatedEntities: observation.relatedEntities ?? [],
9490
+ createdAt: observation.createdAt,
9491
+ updatedAt: now3
9492
+ };
9493
+ db2.prepare(`
9494
+ INSERT INTO evidence_cards (
9495
+ id, project_id, candidate_kind, candidate_id, title, summary, entity_name,
9496
+ source_kind, source_ref, locator, captured_hash, session_id, status,
9497
+ verification, freshness, stale_reason, files_json, related_entities_json,
9498
+ created_at, updated_at
9499
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?)
9500
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
9501
+ title = excluded.title,
9502
+ summary = excluded.summary,
9503
+ entity_name = excluded.entity_name,
9504
+ source_kind = excluded.source_kind,
9505
+ source_ref = excluded.source_ref,
9506
+ locator = excluded.locator,
9507
+ captured_hash = excluded.captured_hash,
9508
+ session_id = excluded.session_id,
9509
+ status = excluded.status,
9510
+ freshness = 'current',
9511
+ stale_reason = NULL,
9512
+ files_json = excluded.files_json,
9513
+ related_entities_json = excluded.related_entities_json,
9514
+ updated_at = excluded.updated_at
9515
+ `).run(
9516
+ card.id,
9517
+ card.projectId,
9518
+ card.candidateKind,
9519
+ card.candidateId,
9520
+ card.title,
9521
+ card.summary,
9522
+ card.entityName ?? null,
9523
+ card.sourceKind,
9524
+ card.sourceRef,
9525
+ card.locator ?? null,
9526
+ card.capturedHash ?? null,
9527
+ card.sessionId ?? null,
9528
+ card.status,
9529
+ card.verification,
9530
+ card.freshness,
9531
+ JSON.stringify(card.files),
9532
+ JSON.stringify(card.relatedEntities),
9533
+ card.createdAt,
9534
+ card.updatedAt
9535
+ );
9536
+ return this.get(card.projectId, card.candidateKind, card.candidateId) ?? card;
9537
+ }
9538
+ get(projectId, candidateKind, candidateId) {
9539
+ const row = this.requireDb().prepare(`
9540
+ SELECT * FROM evidence_cards WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
9541
+ `).get(projectId, candidateKind, candidateId);
9542
+ return row ? rowToCard(row) : void 0;
9543
+ }
9544
+ list(projectId, options = {}) {
9545
+ const limit2 = Math.min(100, Math.max(1, Math.floor(options.limit ?? 20)));
9546
+ const clauses = ["project_id = ?"];
9547
+ const args = [projectId];
9548
+ if (options.freshness) {
9549
+ clauses.push("freshness = ?");
9550
+ args.push(options.freshness);
9551
+ }
9552
+ if (options.status) {
9553
+ clauses.push("status = ?");
9554
+ args.push(options.status);
9555
+ }
9556
+ args.push(limit2);
9557
+ const rows = this.requireDb().prepare(`
9558
+ SELECT * FROM evidence_cards WHERE ${clauses.join(" AND ")} ORDER BY updated_at DESC, id DESC LIMIT ?
9559
+ `).all(...args);
9560
+ return rows.map(rowToCard);
9561
+ }
9562
+ syncObservations(observations2) {
9563
+ return observations2.map((observation) => this.upsertObservation(observation));
9564
+ }
9565
+ markStaleForPaths(projectId, paths, reason = "source file changed") {
9566
+ const normalized = new Set(paths.map((value) => value.replace(/\\/g, "/").toLowerCase()));
9567
+ if (normalized.size === 0) return 0;
9568
+ const db2 = this.requireDb();
9569
+ const rows = db2.prepare(`SELECT * FROM evidence_cards WHERE project_id = ? AND freshness != 'stale'`).all(projectId);
9570
+ const update2 = db2.prepare(`
9571
+ UPDATE evidence_cards SET freshness = 'stale', stale_reason = ?, updated_at = ?
9572
+ WHERE id = ?
9573
+ `);
9574
+ let changed = 0;
9575
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
9576
+ const run = db2.transaction(() => {
9577
+ for (const row of rows) {
9578
+ const files = safeJson(row.files_json, []);
9579
+ if (!files.some((file) => normalized.has(file.replace(/\\/g, "/").toLowerCase()))) continue;
9580
+ update2.run(reason, now3, row.id);
9581
+ changed++;
9582
+ }
9583
+ });
9584
+ run();
9585
+ return changed;
9586
+ }
9587
+ setVerification(projectId, cardId, verification) {
9588
+ this.requireDb().prepare(`
9589
+ UPDATE evidence_cards SET verification = ?, updated_at = ? WHERE project_id = ? AND id = ?
9590
+ `).run(verification, (/* @__PURE__ */ new Date()).toISOString(), projectId, cardId);
9591
+ }
9592
+ recordEvent(projectId, cardId, kind, detail) {
9593
+ const event = {
9594
+ id: randomUUID3(),
9595
+ projectId,
9596
+ cardId,
9597
+ kind,
9598
+ ...detail ? { detail: detail.slice(0, 2e3) } : {},
9599
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
9600
+ };
9601
+ this.requireDb().prepare(`
9602
+ INSERT INTO evidence_card_events (id, project_id, card_id, kind, detail, created_at)
9603
+ VALUES (?, ?, ?, ?, ?, ?)
9604
+ `).run(event.id, event.projectId, event.cardId, event.kind, event.detail ?? null, event.createdAt);
9605
+ return event;
9606
+ }
9607
+ listEvents(cardId, limit2 = 50) {
9608
+ const rows = this.requireDb().prepare(`
9609
+ SELECT * FROM evidence_card_events WHERE card_id = ? ORDER BY created_at DESC LIMIT ?
9610
+ `).all(cardId, Math.min(200, Math.max(1, Math.floor(limit2))));
9611
+ return rows.map((row) => ({
9612
+ id: String(row.id),
9613
+ projectId: String(row.project_id),
9614
+ cardId: String(row.card_id),
9615
+ kind: String(row.kind),
9616
+ ...row.detail ? { detail: String(row.detail) } : {},
9617
+ createdAt: String(row.created_at)
9618
+ }));
9619
+ }
9620
+ };
9621
+ }
9622
+ });
9623
+
9294
9624
  // src/runtime/isolated-maintenance.ts
9295
9625
  import { spawn } from "child_process";
9296
9626
  import { existsSync as existsSync8 } from "fs";
@@ -9452,7 +9782,7 @@ var workspace_store_exports = {};
9452
9782
  __export(workspace_store_exports, {
9453
9783
  KnowledgeWorkspaceStore: () => KnowledgeWorkspaceStore
9454
9784
  });
9455
- import { randomUUID as randomUUID3 } from "crypto";
9785
+ import { randomUUID as randomUUID4 } from "crypto";
9456
9786
  function arrayValue(raw) {
9457
9787
  if (typeof raw !== "string" || !raw) return [];
9458
9788
  try {
@@ -9593,7 +9923,7 @@ var init_workspace_store = __esm({
9593
9923
  }
9594
9924
  createProposal(input) {
9595
9925
  const proposal = {
9596
- id: input.id ?? randomUUID3(),
9926
+ id: input.id ?? randomUUID4(),
9597
9927
  ...input,
9598
9928
  status: input.status ?? "pending",
9599
9929
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -9646,13 +9976,13 @@ __export(workspace_exports, {
9646
9976
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
9647
9977
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
9648
9978
  });
9649
- import { createHash as createHash3 } from "crypto";
9979
+ import { createHash as createHash4 } from "crypto";
9650
9980
  import { execFile } from "child_process";
9651
9981
  import { promises as fs9 } from "fs";
9652
9982
  import path13 from "path";
9653
9983
  import { promisify } from "util";
9654
9984
  function hash(value) {
9655
- return createHash3("sha256").update(value).digest("hex");
9985
+ return createHash4("sha256").update(value).digest("hex");
9656
9986
  }
9657
9987
  function workspaceId(projectId, mode) {
9658
9988
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -10252,7 +10582,7 @@ var media_store_exports = {};
10252
10582
  __export(media_store_exports, {
10253
10583
  MediaStore: () => MediaStore
10254
10584
  });
10255
- import { randomUUID as randomUUID4 } from "crypto";
10585
+ import { randomUUID as randomUUID5 } from "crypto";
10256
10586
  function asOptionalText(value) {
10257
10587
  return typeof value === "string" && value.length > 0 ? value : void 0;
10258
10588
  }
@@ -10422,7 +10752,7 @@ var init_media_store = __esm({
10422
10752
  return { asset: this.getAsset(input.projectId, existing.id), deduplicated: true };
10423
10753
  }
10424
10754
  const asset = {
10425
- id: randomUUID4(),
10755
+ id: randomUUID5(),
10426
10756
  ...input,
10427
10757
  createdAt: now3,
10428
10758
  updatedAt: now3
@@ -10502,7 +10832,7 @@ var init_media_store = __esm({
10502
10832
  `).get(input.assetId, input.observationId ?? null, input.role);
10503
10833
  if (existing) return rowToLink(existing);
10504
10834
  const link = {
10505
- id: randomUUID4(),
10835
+ id: randomUUID5(),
10506
10836
  assetId: input.assetId,
10507
10837
  projectId: input.projectId,
10508
10838
  ...input.observationId !== void 0 ? { observationId: input.observationId } : {},
@@ -10541,7 +10871,7 @@ var init_media_store = __esm({
10541
10871
  return rowToDerivation(this.db.prepare("SELECT * FROM media_derivations WHERE id = ?").get(existing.id));
10542
10872
  }
10543
10873
  const derivation = {
10544
- id: randomUUID4(),
10874
+ id: randomUUID5(),
10545
10875
  ...input,
10546
10876
  content,
10547
10877
  ...error ? { error } : {},
@@ -10628,7 +10958,7 @@ var init_media_store = __esm({
10628
10958
  createJob(input) {
10629
10959
  const now3 = Date.now();
10630
10960
  const job = {
10631
- id: randomUUID4(),
10961
+ id: randomUUID5(),
10632
10962
  projectId: input.projectId,
10633
10963
  kind: input.kind,
10634
10964
  status: "queued",
@@ -10804,14 +11134,14 @@ __export(asset_store_exports, {
10804
11134
  readMediaAsset: () => readMediaAsset,
10805
11135
  removeMediaAsset: () => removeMediaAsset
10806
11136
  });
10807
- import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
11137
+ import { createHash as createHash5, randomUUID as randomUUID6 } from "crypto";
10808
11138
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
10809
11139
  import path14 from "path";
10810
11140
  function mediaRoot(dataDir) {
10811
11141
  return path14.resolve(dataDir, "media");
10812
11142
  }
10813
11143
  function projectStorageKey(projectId) {
10814
- return createHash4("sha256").update(projectId).digest("hex").slice(0, 16);
11144
+ return createHash5("sha256").update(projectId).digest("hex").slice(0, 16);
10815
11145
  }
10816
11146
  function validateUnderRoot(root, candidate) {
10817
11147
  const resolved = path14.resolve(candidate);
@@ -10871,14 +11201,14 @@ async function readHeader(filePath, bytes = 32) {
10871
11201
  }
10872
11202
  async function sha256File(filePath) {
10873
11203
  const bytes = await readFile3(filePath);
10874
- return createHash4("sha256").update(bytes).digest("hex");
11204
+ return createHash5("sha256").update(bytes).digest("hex");
10875
11205
  }
10876
11206
  function destinationFor(root, projectId, sha256, extension3) {
10877
11207
  const relative3 = path14.join("assets", projectStorageKey(projectId), sha256.slice(0, 2), `${sha256}${extension3}`);
10878
11208
  return { relative: relative3, absolute: validateUnderRoot(root, path14.join(root, relative3)) };
10879
11209
  }
10880
11210
  function deletionStagingPath(root, assetId) {
10881
- return validateUnderRoot(root, path14.join(root, ".trash", `${assetId}.${randomUUID5()}.pending-delete`));
11211
+ return validateUnderRoot(root, path14.join(root, ".trash", `${assetId}.${randomUUID6()}.pending-delete`));
10882
11212
  }
10883
11213
  async function destinationExists(destination) {
10884
11214
  try {
@@ -10892,7 +11222,7 @@ async function destinationExists(destination) {
10892
11222
  async function persistFile(source, destination) {
10893
11223
  await mkdir3(path14.dirname(destination), { recursive: true });
10894
11224
  if (await destinationExists(destination)) return false;
10895
- const temporary = `${destination}.${randomUUID5()}.tmp`;
11225
+ const temporary = `${destination}.${randomUUID6()}.tmp`;
10896
11226
  try {
10897
11227
  await copyFile(source, temporary);
10898
11228
  await rename2(temporary, destination);
@@ -10908,7 +11238,7 @@ async function persistFile(source, destination) {
10908
11238
  async function persistBuffer(bytes, destination) {
10909
11239
  await mkdir3(path14.dirname(destination), { recursive: true });
10910
11240
  if (await destinationExists(destination)) return false;
10911
- const temporary = `${destination}.${randomUUID5()}.tmp`;
11241
+ const temporary = `${destination}.${randomUUID6()}.tmp`;
10912
11242
  try {
10913
11243
  await writeFile3(temporary, bytes, { flag: "wx" });
10914
11244
  await rename2(temporary, destination);
@@ -10977,7 +11307,7 @@ async function importMediaBuffer(input) {
10977
11307
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
10978
11308
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
10979
11309
  const detected = detectMedia(input.bytes.subarray(0, 32));
10980
- const sha256 = createHash4("sha256").update(input.bytes).digest("hex");
11310
+ const sha256 = createHash5("sha256").update(input.bytes).digest("hex");
10981
11311
  return importKnownMedia({
10982
11312
  dataDir: input.dataDir,
10983
11313
  projectId: input.projectId,
@@ -10995,7 +11325,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
10995
11325
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
10996
11326
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
10997
11327
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
10998
- const sha256 = createHash4("sha256").update(bytes).digest("hex");
11328
+ const sha256 = createHash5("sha256").update(bytes).digest("hex");
10999
11329
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
11000
11330
  return bytes;
11001
11331
  }
@@ -63865,7 +64195,7 @@ var require_gaxios = __commonJS({
63865
64195
  var retry_js_1 = require_retry3();
63866
64196
  var stream_1 = __require("stream");
63867
64197
  var interceptor_js_1 = require_interceptor();
63868
- var randomUUID17 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
64198
+ var randomUUID19 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
63869
64199
  var HTTP_STATUS_NO_CONTENT = 204;
63870
64200
  var Gaxios = class {
63871
64201
  agentCache = /* @__PURE__ */ new Map();
@@ -64138,7 +64468,7 @@ var require_gaxios = __commonJS({
64138
64468
  */
64139
64469
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
64140
64470
  if (opts.multipart?.length) {
64141
- const boundary = await randomUUID17();
64471
+ const boundary = await randomUUID19();
64142
64472
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
64143
64473
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
64144
64474
  } else if (shouldDirectlyPassData) {
@@ -76365,7 +76695,7 @@ var require_websocket = __commonJS({
76365
76695
  var http3 = __require("http");
76366
76696
  var net = __require("net");
76367
76697
  var tls = __require("tls");
76368
- var { randomBytes: randomBytes2, createHash: createHash16 } = __require("crypto");
76698
+ var { randomBytes: randomBytes2, createHash: createHash17 } = __require("crypto");
76369
76699
  var { Duplex, Readable: Readable2 } = __require("stream");
76370
76700
  var { URL: URL2 } = __require("url");
76371
76701
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -77033,7 +77363,7 @@ var require_websocket = __commonJS({
77033
77363
  abortHandshake(websocket, socket, "Invalid Upgrade header");
77034
77364
  return;
77035
77365
  }
77036
- const digest2 = createHash16("sha1").update(key + GUID).digest("base64");
77366
+ const digest2 = createHash17("sha1").update(key + GUID).digest("base64");
77037
77367
  if (res.headers["sec-websocket-accept"] !== digest2) {
77038
77368
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
77039
77369
  return;
@@ -77405,7 +77735,7 @@ var require_websocket_server = __commonJS({
77405
77735
  var EventEmitter2 = __require("events");
77406
77736
  var http3 = __require("http");
77407
77737
  var { Duplex } = __require("stream");
77408
- var { createHash: createHash16 } = __require("crypto");
77738
+ var { createHash: createHash17 } = __require("crypto");
77409
77739
  var extension3 = require_extension();
77410
77740
  var PerMessageDeflate2 = require_permessage_deflate();
77411
77741
  var subprotocol2 = require_subprotocol();
@@ -77712,7 +78042,7 @@ var require_websocket_server = __commonJS({
77712
78042
  );
77713
78043
  }
77714
78044
  if (this._state > RUNNING) return abortHandshake(socket, 503);
77715
- const digest2 = createHash16("sha1").update(key + GUID).digest("base64");
78045
+ const digest2 = createHash17("sha1").update(key + GUID).digest("base64");
77716
78046
  const headers = [
77717
78047
  "HTTP/1.1 101 Switching Protocols",
77718
78048
  "Upgrade: websocket",
@@ -144966,6 +145296,186 @@ var init_audio_jobs = __esm({
144966
145296
  }
144967
145297
  });
144968
145298
 
145299
+ // src/memory/feedback-model.ts
145300
+ function feedbackWeightMultiplier(weight) {
145301
+ return Math.max(0.35, Math.min(1.4, 0.35 + Math.max(0, Math.min(2, weight)) * 0.525));
145302
+ }
145303
+ var init_feedback_model = __esm({
145304
+ "src/memory/feedback-model.ts"() {
145305
+ "use strict";
145306
+ init_esm_shims();
145307
+ }
145308
+ });
145309
+
145310
+ // src/memory/feedback.ts
145311
+ var feedback_exports = {};
145312
+ __export(feedback_exports, {
145313
+ FEEDBACK_DELTAS: () => FEEDBACK_DELTAS,
145314
+ MemoryFeedbackStore: () => MemoryFeedbackStore,
145315
+ applyMemoryFeedback: () => applyMemoryFeedback,
145316
+ feedbackWeightMultiplier: () => feedbackWeightMultiplier
145317
+ });
145318
+ import { randomUUID as randomUUID7 } from "crypto";
145319
+ function applyMemoryFeedback(current, event) {
145320
+ const delta = event.signal === "revoke" ? 0 : FEEDBACK_DELTAS[event.signal];
145321
+ const weight = Math.max(0, Math.min(2, current.weight + delta));
145322
+ const status = (event.signal === "code-conflict" || event.signal === "user-correction" || event.signal === "weaken") && weight < 0.5 ? "archived" : current.status;
145323
+ return { weight, status, audit: [...current.audit, { ...event }] };
145324
+ }
145325
+ function rowToEvent(row) {
145326
+ return {
145327
+ id: String(row.id),
145328
+ projectId: String(row.project_id),
145329
+ candidateKind: row.candidate_kind,
145330
+ candidateId: String(row.candidate_id),
145331
+ signal: row.signal,
145332
+ ...row.observed_at ? { at: String(row.observed_at) } : { at: (/* @__PURE__ */ new Date()).toISOString() },
145333
+ ...row.actor ? { actor: String(row.actor) } : {},
145334
+ ...row.note ? { note: String(row.note) } : {},
145335
+ ...row.source_ref ? { sourceRef: String(row.source_ref) } : {},
145336
+ ...row.target_event_id ? { targetEventId: String(row.target_event_id) } : {},
145337
+ delta: Number(row.delta ?? 0)
145338
+ };
145339
+ }
145340
+ var FEEDBACK_DELTAS, MemoryFeedbackStore;
145341
+ var init_feedback = __esm({
145342
+ "src/memory/feedback.ts"() {
145343
+ "use strict";
145344
+ init_esm_shims();
145345
+ init_sqlite_db();
145346
+ init_feedback_model();
145347
+ FEEDBACK_DELTAS = {
145348
+ "user-correction": -0.35,
145349
+ "verification-success": 0.25,
145350
+ "verification-failure": -0.25,
145351
+ "code-change": -0.15,
145352
+ used: 0.1,
145353
+ "not-used": -0.05,
145354
+ "code-conflict": -0.4,
145355
+ strengthen: 0.2,
145356
+ weaken: -0.2
145357
+ };
145358
+ MemoryFeedbackStore = class {
145359
+ db = null;
145360
+ async init(dataDir) {
145361
+ this.db = getDatabase(dataDir);
145362
+ }
145363
+ requireDb() {
145364
+ if (!this.db) throw new Error("MemoryFeedbackStore is not initialized.");
145365
+ return this.db;
145366
+ }
145367
+ loadEvents(projectId, candidateKind, candidateId) {
145368
+ return this.requireDb().prepare(`
145369
+ SELECT * FROM memory_feedback_events
145370
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
145371
+ ORDER BY observed_at ASC, rowid ASC
145372
+ `).all(projectId, candidateKind, candidateId).map(rowToEvent);
145373
+ }
145374
+ rebuild(projectId, candidateKind, candidateId) {
145375
+ const events = this.loadEvents(projectId, candidateKind, candidateId);
145376
+ const revoked = new Set(events.filter((event) => event.signal === "revoke" && event.targetEventId).map((event) => event.targetEventId));
145377
+ let state = { weight: 1, status: "active", audit: [] };
145378
+ for (const event of events) {
145379
+ if (event.signal === "revoke" || revoked.has(event.id)) {
145380
+ state.audit.push(event);
145381
+ continue;
145382
+ }
145383
+ state = applyMemoryFeedback(state, event);
145384
+ }
145385
+ this.requireDb().prepare(`
145386
+ INSERT INTO memory_feedback_states (project_id, candidate_kind, candidate_id, weight, lifecycle_status, updated_at)
145387
+ VALUES (?, ?, ?, ?, ?, ?)
145388
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
145389
+ weight = excluded.weight, lifecycle_status = excluded.lifecycle_status, updated_at = excluded.updated_at
145390
+ `).run(projectId, candidateKind, candidateId, state.weight, state.status, (/* @__PURE__ */ new Date()).toISOString());
145391
+ return state;
145392
+ }
145393
+ getState(projectId, candidateKind, candidateId) {
145394
+ const row = this.requireDb().prepare(`
145395
+ SELECT weight, lifecycle_status FROM memory_feedback_states
145396
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
145397
+ `).get(projectId, candidateKind, candidateId);
145398
+ if (!row) return { weight: 1, status: "active", audit: [] };
145399
+ return {
145400
+ weight: Number(row.weight ?? 1),
145401
+ status: row.lifecycle_status ?? "active",
145402
+ audit: this.loadEvents(projectId, candidateKind, candidateId)
145403
+ };
145404
+ }
145405
+ listStates(projectId, candidateKind = "observation") {
145406
+ return this.requireDb().prepare(`
145407
+ SELECT candidate_id, weight, lifecycle_status, updated_at
145408
+ FROM memory_feedback_states WHERE project_id = ? AND candidate_kind = ?
145409
+ ORDER BY updated_at DESC
145410
+ `).all(projectId, candidateKind).map((row) => ({
145411
+ candidateId: String(row.candidate_id),
145412
+ weight: Number(row.weight),
145413
+ status: row.lifecycle_status,
145414
+ updatedAt: String(row.updated_at)
145415
+ }));
145416
+ }
145417
+ weights(projectId, candidateKind = "observation") {
145418
+ const states = /* @__PURE__ */ new Map();
145419
+ for (const row of this.listStates(projectId, candidateKind)) {
145420
+ states.set(row.candidateId, this.getState(projectId, candidateKind, row.candidateId));
145421
+ }
145422
+ return states;
145423
+ }
145424
+ record(input) {
145425
+ if (input.signal === "revoke" && !input.targetEventId) {
145426
+ throw new Error("A revoke feedback event requires targetEventId.");
145427
+ }
145428
+ if (input.signal === "revoke") {
145429
+ const target = this.requireDb().prepare(`
145430
+ SELECT 1 FROM memory_feedback_events
145431
+ WHERE id = ? AND project_id = ? AND candidate_kind = ? AND candidate_id = ?
145432
+ `).get(input.targetEventId, input.projectId, input.candidateKind, input.candidateId);
145433
+ if (!target) throw new Error("The feedback event to revoke was not found in the same candidate scope.");
145434
+ }
145435
+ const event = {
145436
+ id: randomUUID7(),
145437
+ projectId: input.projectId,
145438
+ candidateKind: input.candidateKind,
145439
+ candidateId: input.candidateId,
145440
+ signal: input.signal,
145441
+ sourceRef: input.sourceRef,
145442
+ ...input.actor ? { actor: input.actor } : {},
145443
+ ...input.note ? { note: input.note.slice(0, 2e3) } : {},
145444
+ ...input.targetEventId ? { targetEventId: input.targetEventId } : {},
145445
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
145446
+ delta: input.signal === "revoke" ? 0 : FEEDBACK_DELTAS[input.signal]
145447
+ };
145448
+ this.requireDb().prepare(`
145449
+ INSERT INTO memory_feedback_events (
145450
+ id, project_id, candidate_kind, candidate_id, signal, actor, note,
145451
+ source_ref, target_event_id, delta, observed_at
145452
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
145453
+ `).run(
145454
+ event.id,
145455
+ event.projectId,
145456
+ event.candidateKind,
145457
+ event.candidateId,
145458
+ event.signal,
145459
+ event.actor ?? null,
145460
+ event.note ?? null,
145461
+ event.sourceRef,
145462
+ event.targetEventId ?? null,
145463
+ event.delta,
145464
+ event.at
145465
+ );
145466
+ return { event, state: this.rebuild(input.projectId, input.candidateKind, input.candidateId) };
145467
+ }
145468
+ audit(projectId, candidateKind, candidateId, limit2 = 100) {
145469
+ return this.requireDb().prepare(`
145470
+ SELECT * FROM memory_feedback_events
145471
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
145472
+ ORDER BY observed_at DESC, rowid DESC LIMIT ?
145473
+ `).all(projectId, candidateKind, candidateId, Math.min(200, Math.max(1, Math.floor(limit2)))).map(rowToEvent);
145474
+ }
145475
+ };
145476
+ }
145477
+ });
145478
+
144969
145479
  // src/memory/retention.ts
144970
145480
  var retention_exports = {};
144971
145481
  __export(retention_exports, {
@@ -145007,6 +145517,7 @@ function getEffectiveRetentionDays(doc) {
145007
145517
  }
145008
145518
  function isImmune(doc) {
145009
145519
  if (doc.type === "probe") return false;
145520
+ if (doc.feedbackStatus === "archived") return false;
145010
145521
  if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
145011
145522
  if (doc.valueCategory === "core") return true;
145012
145523
  const importance = getImportanceLevel(doc);
@@ -145039,7 +145550,8 @@ function calculateRelevance(doc, referenceTime) {
145039
145550
  const decayFactor = Math.exp(-ageDays / retention);
145040
145551
  const accessCount = doc.accessCount ?? 0;
145041
145552
  const accessBoost = Math.min(2, 1 + 0.1 * accessCount);
145042
- let totalScore = base * decayFactor * accessBoost;
145553
+ const feedbackMultiplier = doc.feedbackWeight === void 0 ? 1 : feedbackWeightMultiplier(doc.feedbackWeight);
145554
+ let totalScore = base * decayFactor * accessBoost * feedbackMultiplier;
145043
145555
  const immune = isImmune(doc);
145044
145556
  if (immune) {
145045
145557
  totalScore = Math.max(totalScore, 0.5);
@@ -145051,7 +145563,8 @@ function calculateRelevance(doc, referenceTime) {
145051
145563
  decayFactor,
145052
145564
  accessBoost,
145053
145565
  ageDays,
145054
- isImmune: immune
145566
+ isImmune: immune,
145567
+ feedbackMultiplier
145055
145568
  };
145056
145569
  }
145057
145570
  function rankByRelevance(docs, referenceTime) {
@@ -145059,6 +145572,7 @@ function rankByRelevance(docs, referenceTime) {
145059
145572
  }
145060
145573
  function getRetentionZone(doc, referenceTime) {
145061
145574
  const now3 = referenceTime ?? /* @__PURE__ */ new Date();
145575
+ if (doc.feedbackStatus === "archived") return "archive-candidate";
145062
145576
  const importance = getImportanceLevel(doc);
145063
145577
  const retention = getEffectiveRetentionDays(doc);
145064
145578
  const createdAt = new Date(doc.createdAt);
@@ -145130,7 +145644,7 @@ function explainRetention(doc, referenceTime) {
145130
145644
  summary: parts.join(" ; ")
145131
145645
  };
145132
145646
  }
145133
- function toRetentionDocument(obs, accessMap) {
145647
+ function toRetentionDocument(obs, accessMap, feedback) {
145134
145648
  const access3 = accessMap?.get(obs.id);
145135
145649
  return {
145136
145650
  id: `obs-${obs.id}`,
@@ -145152,11 +145666,12 @@ function toRetentionDocument(obs, accessMap) {
145152
145666
  sourceDetail: obs.sourceDetail ?? "",
145153
145667
  valueCategory: obs.valueCategory ?? "",
145154
145668
  admissionState: obs.admissionState ?? "",
145155
- admissionReason: obs.admissionReason ?? ""
145669
+ admissionReason: obs.admissionReason ?? "",
145670
+ ...feedback ? { feedbackWeight: feedback.weight, feedbackStatus: feedback.status } : {}
145156
145671
  };
145157
145672
  }
145158
145673
  function projectObservationRetention(observation, options = {}) {
145159
- const document2 = toRetentionDocument(observation, options.accessMap);
145674
+ const document2 = toRetentionDocument(observation, options.accessMap, options.feedback);
145160
145675
  const relevance = calculateRelevance(document2, options.referenceTime);
145161
145676
  const explanation = explainRetention(document2, options.referenceTime);
145162
145677
  return {
@@ -145181,7 +145696,17 @@ function summarizeRetentionProjections(projections) {
145181
145696
  }
145182
145697
  return { active, stale, archiveCandidates, immune };
145183
145698
  }
145184
- async function archiveExpiredBatch(_projectDir, options) {
145699
+ async function loadFeedbackStore(dataDir) {
145700
+ try {
145701
+ const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
145702
+ const store = new MemoryFeedbackStore2();
145703
+ await store.init(dataDir);
145704
+ return store;
145705
+ } catch {
145706
+ return void 0;
145707
+ }
145708
+ }
145709
+ async function archiveExpiredBatch(projectDir2, options) {
145185
145710
  const store = getObservationStore();
145186
145711
  const limit2 = Math.min(500, Math.max(1, Math.floor(options.limit ?? 100)));
145187
145712
  const afterId = Math.max(0, Math.floor(options.afterId ?? 0));
@@ -145190,11 +145715,13 @@ async function archiveExpiredBatch(_projectDir, options) {
145190
145715
  afterId,
145191
145716
  limit: limit2 + 1
145192
145717
  });
145718
+ const feedback = await loadFeedbackStore(projectDir2);
145193
145719
  const hasMore = page.length > limit2;
145194
145720
  const scanned = hasMore ? page.slice(0, limit2) : page;
145195
145721
  const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => projectObservationRetention(observation, {
145196
145722
  accessMap: options.accessMap,
145197
- referenceTime: options.referenceTime
145723
+ referenceTime: options.referenceTime,
145724
+ ...feedback ? { feedback: feedback.getState(options.projectId, "observation", String(observation.id)) } : {}
145198
145725
  }).zone === "archive-candidate").map((observation) => observation.id);
145199
145726
  const archived = candidateIds.length === 0 ? 0 : await store.atomic(async (tx) => {
145200
145727
  const updates = await Promise.all(
@@ -145225,6 +145752,7 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
145225
145752
  const remaining = reader2 ? remainingObservations.filter((observation) => canManageObservation(observation, reader2)).length : remainingObservations.length;
145226
145753
  return { archived, remaining };
145227
145754
  }
145755
+ const feedback = await loadFeedbackStore(projectDir2);
145228
145756
  return await store.atomic(async (tx) => {
145229
145757
  const allObs = await tx.loadAll();
145230
145758
  const activeObs = allObs.filter(
@@ -145232,7 +145760,11 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
145232
145760
  );
145233
145761
  const archivedIds = [];
145234
145762
  for (const obs of activeObs) {
145235
- const retention = projectObservationRetention(obs, { accessMap, referenceTime });
145763
+ const retention = projectObservationRetention(obs, {
145764
+ accessMap,
145765
+ referenceTime,
145766
+ ...feedback ? { feedback: feedback.getState(obs.projectId, "observation", String(obs.id)) } : {}
145767
+ });
145236
145768
  if (retention.zone === "archive-candidate") {
145237
145769
  archivedIds.push(obs.id);
145238
145770
  }
@@ -145252,6 +145784,7 @@ var init_retention = __esm({
145252
145784
  init_esm_shims();
145253
145785
  init_obs_store();
145254
145786
  init_visibility();
145787
+ init_feedback_model();
145255
145788
  RETENTION_DAYS = {
145256
145789
  critical: 365,
145257
145790
  high: 180,
@@ -145292,7 +145825,7 @@ var owner_exports = {};
145292
145825
  __export(owner_exports, {
145293
145826
  resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
145294
145827
  });
145295
- import { randomUUID as randomUUID6 } from "crypto";
145828
+ import { randomUUID as randomUUID8 } from "crypto";
145296
145829
  import { promises as fs12 } from "fs";
145297
145830
  import path20 from "path";
145298
145831
  function ownerPath(dataDir) {
@@ -145322,7 +145855,7 @@ async function resolveLocalMemoryOwner(dataDir, options = {}) {
145322
145855
  await fs12.mkdir(path20.dirname(filePath), { recursive: true });
145323
145856
  const created = {
145324
145857
  version: 1,
145325
- id: "local-" + randomUUID6(),
145858
+ id: "local-" + randomUUID8(),
145326
145859
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
145327
145860
  };
145328
145861
  try {
@@ -145345,7 +145878,7 @@ var init_owner = __esm({
145345
145878
  });
145346
145879
 
145347
145880
  // src/memory/long-term-store.ts
145348
- import { randomUUID as randomUUID7 } from "crypto";
145881
+ import { randomUUID as randomUUID9 } from "crypto";
145349
145882
  function optionalText2(value) {
145350
145883
  return typeof value === "string" && value.trim() ? value : void 0;
145351
145884
  }
@@ -145396,7 +145929,7 @@ function rowToEvidence(row) {
145396
145929
  createdAt: row.createdAt
145397
145930
  };
145398
145931
  }
145399
- function rowToEvent(row) {
145932
+ function rowToEvent2(row) {
145400
145933
  return {
145401
145934
  id: row.id,
145402
145935
  memoryId: row.memoryId,
@@ -145505,7 +146038,7 @@ var init_long_term_store = __esm({
145505
146038
  }
145506
146039
  insertEvidence(input) {
145507
146040
  const evidence = {
145508
- id: randomUUID7(),
146041
+ id: randomUUID9(),
145509
146042
  ...input,
145510
146043
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
145511
146044
  };
@@ -145532,7 +146065,7 @@ var init_long_term_store = __esm({
145532
146065
  }
145533
146066
  recordEvent(input) {
145534
146067
  const event = {
145535
- id: randomUUID7(),
146068
+ id: randomUUID9(),
145536
146069
  ...input,
145537
146070
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
145538
146071
  };
@@ -145553,7 +146086,7 @@ var init_long_term_store = __esm({
145553
146086
  listEvents(memoryId) {
145554
146087
  return this.db.prepare(`
145555
146088
  SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
145556
- `).all(memoryId).map(rowToEvent);
146089
+ `).all(memoryId).map(rowToEvent2);
145557
146090
  }
145558
146091
  markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
145559
146092
  const unique2 = [...new Set(ids.filter(Boolean))];
@@ -145622,7 +146155,7 @@ __export(long_term_exports, {
145622
146155
  selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
145623
146156
  supersedeLongTermMemory: () => supersedeLongTermMemory
145624
146157
  });
145625
- import { randomUUID as randomUUID8 } from "crypto";
146158
+ import { randomUUID as randomUUID10 } from "crypto";
145626
146159
  function isOneOf(value, values) {
145627
146160
  return typeof value === "string" && values.includes(value);
145628
146161
  }
@@ -145804,7 +146337,7 @@ async function createManualLongTermMemory(input) {
145804
146337
  const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
145805
146338
  if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
145806
146339
  assertScope(input.scope);
145807
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
146340
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
145808
146341
  return createLongTermMemoryWithDataDir({
145809
146342
  dataDir: input.dataDir,
145810
146343
  originProjectId: input.projectId,
@@ -145859,7 +146392,7 @@ async function createLongTermMemoryWithDataDir(input) {
145859
146392
  assertPortableEvidence(input.scope, portability, evidenceInputs);
145860
146393
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
145861
146394
  const memory = {
145862
- id: randomUUID8(),
146395
+ id: randomUUID10(),
145863
146396
  originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
145864
146397
  ownerId: compactText(input.ownerId, "owner id", 160),
145865
146398
  scope: input.scope,
@@ -146177,9 +146710,9 @@ var init_long_term = __esm({
146177
146710
  });
146178
146711
 
146179
146712
  // src/codegraph/ids.ts
146180
- import { createHash as createHash5 } from "crypto";
146713
+ import { createHash as createHash6 } from "crypto";
146181
146714
  function digest(input) {
146182
- return createHash5("sha256").update(input).digest("hex").slice(0, 16);
146715
+ return createHash6("sha256").update(input).digest("hex").slice(0, 16);
146183
146716
  }
146184
146717
  function normalizeCodePath(path35) {
146185
146718
  return path35.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -146214,7 +146747,7 @@ var store_exports = {};
146214
146747
  __export(store_exports, {
146215
146748
  CodeGraphStore: () => CodeGraphStore
146216
146749
  });
146217
- import { randomUUID as randomUUID9 } from "crypto";
146750
+ import { randomUUID as randomUUID11 } from "crypto";
146218
146751
  import { posix as pathPosix } from "path";
146219
146752
  function rowToFile(row) {
146220
146753
  return {
@@ -146931,7 +147464,7 @@ var init_store = __esm({
146931
147464
  * so an interrupted scan cannot be advertised as complete.
146932
147465
  */
146933
147466
  recordCodeStateSnapshot(input) {
146934
- const id = randomUUID9();
147467
+ const id = randomUUID11();
146935
147468
  let snapshot;
146936
147469
  const tx = this.db.transaction(() => {
146937
147470
  const previous = this.db.prepare(
@@ -147009,7 +147542,7 @@ var init_store = __esm({
147009
147542
 
147010
147543
  // src/codegraph/code-state.ts
147011
147544
  import { execFile as execFile2 } from "child_process";
147012
- import { createHash as createHash6 } from "crypto";
147545
+ import { createHash as createHash7 } from "crypto";
147013
147546
  import { promisify as promisify3 } from "util";
147014
147547
  async function runGit(rootPath, args) {
147015
147548
  try {
@@ -147038,7 +147571,7 @@ function changedPathCount(status) {
147038
147571
  return count2;
147039
147572
  }
147040
147573
  function fingerprint(baseRevision, status) {
147041
- return createHash6("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
147574
+ return createHash7("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
147042
147575
  }
147043
147576
  async function collectCodeStateSnapshot(input) {
147044
147577
  const [revision, status] = await Promise.all([
@@ -147138,11 +147671,11 @@ __export(lite_provider_exports, {
147138
147671
  indexProjectLite: () => indexProjectLite,
147139
147672
  refreshProjectLite: () => refreshProjectLite
147140
147673
  });
147141
- import { createHash as createHash7 } from "crypto";
147674
+ import { createHash as createHash8 } from "crypto";
147142
147675
  import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
147143
147676
  import { join as join4, relative } from "path";
147144
147677
  function hashText(text2) {
147145
- return createHash7("sha256").update(text2).digest("hex");
147678
+ return createHash8("sha256").update(text2).digest("hex");
147146
147679
  }
147147
147680
  function extension2(path35) {
147148
147681
  const index = path35.lastIndexOf(".");
@@ -147709,7 +148242,7 @@ var claim_store_exports = {};
147709
148242
  __export(claim_store_exports, {
147710
148243
  ClaimStore: () => ClaimStore
147711
148244
  });
147712
- import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
148245
+ import { createHash as createHash9, randomUUID as randomUUID12 } from "crypto";
147713
148246
  function optionalText3(value) {
147714
148247
  return typeof value === "string" && value ? value : void 0;
147715
148248
  }
@@ -147748,7 +148281,7 @@ function rowToEvidence2(row) {
147748
148281
  createdAt: row.createdAt
147749
148282
  };
147750
148283
  }
147751
- function rowToEvent2(row) {
148284
+ function rowToEvent3(row) {
147752
148285
  return {
147753
148286
  id: row.id,
147754
148287
  projectId: row.projectId,
@@ -147762,7 +148295,7 @@ function rowToEvent2(row) {
147762
148295
  };
147763
148296
  }
147764
148297
  function evidenceKey(input) {
147765
- return createHash8("sha256").update(JSON.stringify([
148298
+ return createHash9("sha256").update(JSON.stringify([
147766
148299
  input.evidenceKind,
147767
148300
  input.evidenceId,
147768
148301
  input.relation,
@@ -147893,7 +148426,7 @@ var init_claim_store = __esm({
147893
148426
  locator, capturedHash, evidenceKey, createdAt
147894
148427
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
147895
148428
  `).run(
147896
- randomUUID10(),
148429
+ randomUUID12(),
147897
148430
  input.claimId,
147898
148431
  input.evidenceKind,
147899
148432
  input.evidenceId,
@@ -147915,7 +148448,7 @@ var init_claim_store = __esm({
147915
148448
  }
147916
148449
  recordEvent(input) {
147917
148450
  const event = {
147918
- id: randomUUID10(),
148451
+ id: randomUUID12(),
147919
148452
  ...input,
147920
148453
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
147921
148454
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -147943,7 +148476,7 @@ var init_claim_store = __esm({
147943
148476
  SELECT * FROM knowledge_claim_events
147944
148477
  WHERE claimId = ?
147945
148478
  ORDER BY createdAt ASC, id
147946
- `).all(claimId).map(rowToEvent2);
148479
+ `).all(claimId).map(rowToEvent3);
147947
148480
  }
147948
148481
  };
147949
148482
  }
@@ -147960,7 +148493,7 @@ __export(claims_exports, {
147960
148493
  supersedeClaim: () => supersedeClaim,
147961
148494
  writeClaim: () => writeClaim
147962
148495
  });
147963
- import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
148496
+ import { createHash as createHash10, randomUUID as randomUUID13 } from "crypto";
147964
148497
  function compactText2(value, field) {
147965
148498
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
147966
148499
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -147972,7 +148505,7 @@ function normalizedTerm(value) {
147972
148505
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
147973
148506
  }
147974
148507
  function hashIdentity(parts) {
147975
- return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
148508
+ return createHash10("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
147976
148509
  }
147977
148510
  function buildClaimIdentity(input) {
147978
148511
  const subject = normalizedTerm(input.subject);
@@ -148046,7 +148579,7 @@ function normalizeClaimInput(input) {
148046
148579
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
148047
148580
  return {
148048
148581
  claim: {
148049
- id: randomUUID11(),
148582
+ id: randomUUID13(),
148050
148583
  projectId,
148051
148584
  subject,
148052
148585
  predicate,
@@ -148213,7 +148746,7 @@ function supersedeClaim(store, input) {
148213
148746
  });
148214
148747
  }
148215
148748
  function hashObservation(observation) {
148216
- return createHash9("sha256").update(JSON.stringify([
148749
+ return createHash10("sha256").update(JSON.stringify([
148217
148750
  observation.title,
148218
148751
  observation.narrative,
148219
148752
  observation.facts,
@@ -148457,11 +148990,11 @@ var init_frontmatter = __esm({
148457
148990
  });
148458
148991
 
148459
148992
  // src/knowledge/markdown.ts
148460
- import { createHash as createHash10 } from "crypto";
148993
+ import { createHash as createHash11 } from "crypto";
148461
148994
  import { promises as fs13 } from "fs";
148462
148995
  import path21 from "path";
148463
148996
  function hashContent(content) {
148464
- return createHash10("sha256").update(content).digest("hex");
148997
+ return createHash11("sha256").update(content).digest("hex");
148465
148998
  }
148466
148999
  function stringField(data, key) {
148467
149000
  const value = data[key];
@@ -148587,11 +149120,11 @@ __export(wiki_exports, {
148587
149120
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
148588
149121
  readKnowledgePage: () => readKnowledgePage
148589
149122
  });
148590
- import { createHash as createHash11 } from "crypto";
149123
+ import { createHash as createHash12 } from "crypto";
148591
149124
  import { promises as fs14 } from "fs";
148592
149125
  import path22 from "path";
148593
149126
  function hash2(value) {
148594
- return createHash11("sha256").update(value).digest("hex");
149127
+ return createHash12("sha256").update(value).digest("hex");
148595
149128
  }
148596
149129
  function now() {
148597
149130
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -149135,7 +149668,7 @@ var workflow_store_exports = {};
149135
149668
  __export(workflow_store_exports, {
149136
149669
  WorkflowStore: () => WorkflowStore
149137
149670
  });
149138
- import { randomUUID as randomUUID12 } from "crypto";
149671
+ import { randomUUID as randomUUID14 } from "crypto";
149139
149672
  function parseJson(value, fallback) {
149140
149673
  if (typeof value !== "string" || !value) return fallback;
149141
149674
  try {
@@ -149235,7 +149768,7 @@ var init_workflow_store = __esm({
149235
149768
  }
149236
149769
  recordRun(input) {
149237
149770
  const run = {
149238
- id: randomUUID12(),
149771
+ id: randomUUID14(),
149239
149772
  workflowId: input.workflowId,
149240
149773
  projectId: input.projectId,
149241
149774
  task: input.task.trim(),
@@ -149313,7 +149846,7 @@ var init_outcome_types = __esm({
149313
149846
  });
149314
149847
 
149315
149848
  // src/knowledge/outcome-store.ts
149316
- import { randomUUID as randomUUID13 } from "crypto";
149849
+ import { randomUUID as randomUUID15 } from "crypto";
149317
149850
  function optionalText5(value) {
149318
149851
  return typeof value === "string" && value.trim() ? value : void 0;
149319
149852
  }
@@ -149357,7 +149890,7 @@ var init_outcome_store = __esm({
149357
149890
  }
149358
149891
  record(input) {
149359
149892
  const signal = {
149360
- id: randomUUID13(),
149893
+ id: randomUUID15(),
149361
149894
  projectId: text(input.projectId, "project id"),
149362
149895
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
149363
149896
  candidateId: text(input.candidateId, "candidate id"),
@@ -149784,11 +150317,11 @@ __export(workflows_exports, {
149784
150317
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
149785
150318
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
149786
150319
  });
149787
- import { createHash as createHash12 } from "crypto";
150320
+ import { createHash as createHash13 } from "crypto";
149788
150321
  import { promises as fs15 } from "fs";
149789
150322
  import path23 from "path";
149790
150323
  function hash3(value) {
149791
- return createHash12("sha256").update(value).digest("hex");
150324
+ return createHash13("sha256").update(value).digest("hex");
149792
150325
  }
149793
150326
  function now2() {
149794
150327
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -151185,6 +151718,16 @@ async function bindObservationCodeRefsBestEffort(observation) {
151185
151718
  } catch {
151186
151719
  }
151187
151720
  }
151721
+ async function persistEvidenceCard(observation) {
151722
+ if (!projectDir) return;
151723
+ try {
151724
+ const evidence = new EvidenceCardStore();
151725
+ await evidence.init(projectDir);
151726
+ evidence.upsertObservation(observation);
151727
+ } catch (error) {
151728
+ console.error(`[memorix] Evidence card projection failed for obs-${observation.id}: ${error instanceof Error ? error.message : String(error)}`);
151729
+ }
151730
+ }
151188
151731
  function queueClaimDerivation(observation) {
151189
151732
  const dataDir = projectDir;
151190
151733
  const isExplicit = observation.sourceDetail === "explicit";
@@ -151531,6 +152074,7 @@ async function storeObservation(input) {
151531
152074
  }
151532
152075
  if (!persistedCallbackInvoked) await input.onPersisted?.(observation);
151533
152076
  await bindObservationCodeRefsBestEffort(observation);
152077
+ await persistEvidenceCard(observation);
151534
152078
  queueObservationQualification(observation);
151535
152079
  if (resolveObservationVisibility(observation) === "project") {
151536
152080
  queueClaimDerivation(observation);
@@ -151623,6 +152167,7 @@ async function upsertObservation(existing, input, now3) {
151623
152167
  await store.update(existing);
151624
152168
  }
151625
152169
  await bindObservationCodeRefsBestEffort(existing);
152170
+ await persistEvidenceCard(existing);
151626
152171
  queueObservationQualification(existing);
151627
152172
  if (resolveObservationVisibility(existing) === "project") {
151628
152173
  queueClaimDerivation(existing);
@@ -151756,6 +152301,7 @@ async function resolveObservations(ids, status = "resolved") {
151756
152301
  await Promise.all(changed.map((observation) => tx.update(observation)));
151757
152302
  });
151758
152303
  }
152304
+ for (const observation of changed) await persistEvidenceCard(observation);
151759
152305
  return { resolved, notFound };
151760
152306
  }
151761
152307
  function getProjectObservations(projectId) {
@@ -152093,6 +152639,7 @@ var init_observations = __esm({
152093
152639
  init_secret_filter();
152094
152640
  init_lifecycle();
152095
152641
  init_visibility();
152642
+ init_evidence_store();
152096
152643
  observations = [];
152097
152644
  nextId = 1;
152098
152645
  projectDir = null;
@@ -155772,7 +156319,7 @@ var compaction_checkpoint_store_exports = {};
155772
156319
  __export(compaction_checkpoint_store_exports, {
155773
156320
  CompactionCheckpointStore: () => CompactionCheckpointStore
155774
156321
  });
155775
- import { createHash as createHash14, randomUUID as randomUUID14 } from "crypto";
156322
+ import { createHash as createHash15, randomUUID as randomUUID16 } from "crypto";
155776
156323
  function optionalText7(value) {
155777
156324
  return typeof value === "string" && value ? value : void 0;
155778
156325
  }
@@ -155814,7 +156361,7 @@ function sourceKeyFor(input, summary) {
155814
156361
  input.tokensBefore ?? "",
155815
156362
  input.firstKeptEntryId ?? ""
155816
156363
  ].join("\0");
155817
- return `derived:${createHash14("sha256").update(material).digest("hex").slice(0, 32)}`;
156364
+ return `derived:${createHash15("sha256").update(material).digest("hex").slice(0, 32)}`;
155818
156365
  }
155819
156366
  function rowToCheckpoint(row) {
155820
156367
  return {
@@ -155873,7 +156420,7 @@ var init_compaction_checkpoint_store = __esm({
155873
156420
  if (pending) return rowToCheckpoint(pending);
155874
156421
  const now3 = input.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
155875
156422
  const checkpoint = {
155876
- id: randomUUID14(),
156423
+ id: randomUUID16(),
155877
156424
  projectId: input.projectId,
155878
156425
  sessionId: input.sessionId,
155879
156426
  agent: input.agent,
@@ -155881,7 +156428,7 @@ var init_compaction_checkpoint_store = __esm({
155881
156428
  captureKind: "preflight",
155882
156429
  reason: safeReason(input.reason),
155883
156430
  sourceEvent: input.sourceEvent,
155884
- sourceKey: `pre:${randomUUID14()}`,
156431
+ sourceKey: `pre:${randomUUID16()}`,
155885
156432
  transcriptAvailable: Boolean(input.transcriptAvailable),
155886
156433
  status: "active",
155887
156434
  preCapturedAt: now3,
@@ -155947,7 +156494,7 @@ var init_compaction_checkpoint_store = __esm({
155947
156494
  return this.get(checkpoint2.id);
155948
156495
  }
155949
156496
  const checkpoint = {
155950
- id: randomUUID14(),
156497
+ id: randomUUID16(),
155951
156498
  projectId: input.projectId,
155952
156499
  sessionId: input.sessionId,
155953
156500
  agent: input.agent,
@@ -157311,7 +157858,7 @@ __export(team_store_exports, {
157311
157858
  isTeamStoreInitialized: () => isTeamStoreInitialized,
157312
157859
  resetTeamStore: () => resetTeamStore
157313
157860
  });
157314
- import { randomUUID as randomUUID15 } from "crypto";
157861
+ import { randomUUID as randomUUID17 } from "crypto";
157315
157862
  import path30 from "path";
157316
157863
  import fs18 from "fs";
157317
157864
  function getTeamStore() {
@@ -157600,7 +158147,7 @@ var init_team_store = __esm({
157600
158147
  * reactivate it (preserving agent_id). Otherwise create a new one.
157601
158148
  */
157602
158149
  registerAgent(input) {
157603
- const instanceId = input.instanceId || randomUUID15();
158150
+ const instanceId = input.instanceId || randomUUID17();
157604
158151
  const now3 = Date.now();
157605
158152
  const existing = this.stmtAgentFindByInstance.get(
157606
158153
  input.projectId,
@@ -157626,7 +158173,7 @@ var init_team_store = __esm({
157626
158173
  this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
157627
158174
  return row2;
157628
158175
  }
157629
- const agentId = randomUUID15();
158176
+ const agentId = randomUUID17();
157630
158177
  this.stmtAgentUpsert.run({
157631
158178
  agent_id: agentId,
157632
158179
  project_id: input.projectId,
@@ -157728,7 +158275,7 @@ var init_team_store = __esm({
157728
158275
  return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
157729
158276
  }
157730
158277
  }
157731
- const id = randomUUID15();
158278
+ const id = randomUUID17();
157732
158279
  const now3 = Date.now();
157733
158280
  const row = {
157734
158281
  id,
@@ -157778,7 +158325,7 @@ var init_team_store = __esm({
157778
158325
  // Task Operations (with atomic claim semantics)
157779
158326
  // ═══════════════════════════════════════════════════════════════════
157780
158327
  createTask(input) {
157781
- const taskId = randomUUID15();
158328
+ const taskId = randomUUID17();
157782
158329
  const now3 = Date.now();
157783
158330
  const row = {
157784
158331
  task_id: taskId,
@@ -158050,7 +158597,7 @@ var init_team_store = __esm({
158050
158597
  for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
158051
158598
  for (const msg of msgs) {
158052
158599
  this.stmtMsgInsert.run({
158053
- id: msg.id ?? randomUUID15(),
158600
+ id: msg.id ?? randomUUID17(),
158054
158601
  project_id: "migrated",
158055
158602
  sender_agent_id: msg.from ?? "unknown",
158056
158603
  recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
@@ -158921,6 +159468,23 @@ var init_maintenance = __esm({
158921
159468
  }
158922
159469
  });
158923
159470
 
159471
+ // src/dashboard/config-provenance.ts
159472
+ function resolveDashboardKeySource({ value, envKeys, configSource }) {
159473
+ for (const envKey of envKeys) {
159474
+ const envValue = process.env[envKey];
159475
+ if (envValue !== value) continue;
159476
+ return getLoadedEnvValue(envKey) === envValue ? ".env" : `env:${envKey}`;
159477
+ }
159478
+ return configSource;
159479
+ }
159480
+ var init_config_provenance = __esm({
159481
+ "src/dashboard/config-provenance.ts"() {
159482
+ "use strict";
159483
+ init_esm_shims();
159484
+ init_dotenv_loader();
159485
+ }
159486
+ });
159487
+
158924
159488
  // src/dashboard/project-classification.ts
158925
159489
  var project_classification_exports = {};
158926
159490
  __export(project_classification_exports, {
@@ -159700,6 +160264,20 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
159700
160264
  sendJson(res, observations2);
159701
160265
  break;
159702
160266
  }
160267
+ case "/evidence": {
160268
+ const observations2 = filterDashboardObservations(
160269
+ await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
160270
+ effectiveProjectId
160271
+ );
160272
+ const evidenceStore = new EvidenceCardStore();
160273
+ await evidenceStore.init(effectiveDataDir);
160274
+ evidenceStore.syncObservations(observations2);
160275
+ const query = url.searchParams.get("q") || "";
160276
+ const limit2 = Number(url.searchParams.get("limit") || 20);
160277
+ const cards = evidenceStore.list(effectiveProjectId, { limit: Math.min(100, Math.max(1, limit2)) }).filter((card) => !query || [card.title, card.summary, card.sourceRef, card.locator ?? ""].join(" ").toLowerCase().includes(query.toLowerCase()));
160278
+ sendJson(res, { cards, projectId: effectiveProjectId });
160279
+ break;
160280
+ }
159703
160281
  case "/sessions": {
159704
160282
  const allSessions = await getSessionStore().loadAll();
159705
160283
  const sessions = filterByProject(allSessions, effectiveProjectId);
@@ -159747,8 +160325,13 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
159747
160325
  filesModified: o.filesModified
159748
160326
  }));
159749
160327
  const retentionReferenceTime = new Date(now3);
160328
+ const feedback = new MemoryFeedbackStore();
160329
+ await feedback.init(effectiveDataDir).catch(() => void 0);
159750
160330
  const retention = summarizeRetentionProjections(
159751
- observations2.filter((observation) => observation.type !== "probe").map((observation) => projectObservationRetention(observation, { referenceTime: retentionReferenceTime }))
160331
+ observations2.filter((observation) => observation.type !== "probe").map((observation) => projectObservationRetention(observation, {
160332
+ referenceTime: retentionReferenceTime,
160333
+ feedback: feedback.getState(effectiveProjectId, "observation", String(observation.id))
160334
+ }))
159752
160335
  );
159753
160336
  const retentionSummary = {
159754
160337
  active: retention.active,
@@ -159842,9 +160425,14 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
159842
160425
  effectiveProjectId
159843
160426
  );
159844
160427
  const referenceTime = /* @__PURE__ */ new Date();
160428
+ const feedback = new MemoryFeedbackStore();
160429
+ await feedback.init(effectiveDataDir).catch(() => void 0);
159845
160430
  const rows = observations2.filter((observation) => observation.type !== "probe").map((observation) => ({
159846
160431
  observation,
159847
- retention: projectObservationRetention(observation, { referenceTime })
160432
+ retention: projectObservationRetention(observation, {
160433
+ referenceTime,
160434
+ feedback: feedback.getState(effectiveProjectId, "observation", String(observation.id))
160435
+ })
159848
160436
  })).sort((a, b) => b.retention.displayScore - a.retention.displayScore);
159849
160437
  const summary = summarizeRetentionProjections(rows.map((row) => row.retention));
159850
160438
  const scored = rows.map(({ observation, retention }) => ({
@@ -159915,23 +160503,13 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
159915
160503
  const os6 = await import("os");
159916
160504
  const { existsSync: existsSync21 } = await import("fs");
159917
160505
  const { join: join25 } = await import("path");
159918
- let yml = {};
159919
160506
  const configProjectRoot = effectiveProjectRoot;
159920
- try {
159921
- const { loadYamlConfig: loadYamlConfig2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
159922
- yml = configProjectRoot ? loadYamlConfig2(configProjectRoot) : loadYamlConfig2(null);
159923
- } catch {
159924
- }
159925
- if (configProjectRoot) {
159926
- try {
159927
- const { loadDotenv: loadDotenv2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
159928
- loadDotenv2(configProjectRoot);
159929
- } catch {
159930
- }
159931
- }
160507
+ const resolved = getResolvedConfig({ projectRoot: configProjectRoot });
159932
160508
  const files = {
159933
160509
  "project memorix.yml": { exists: false, path: "", unavailable: !configProjectRoot },
159934
160510
  "user memorix.yml": { exists: false, path: "" },
160511
+ "project memorix.toml": { exists: false, path: "", unavailable: !configProjectRoot },
160512
+ "user config.toml": { exists: false, path: "" },
159935
160513
  "project .env": { exists: false, path: "", unavailable: !configProjectRoot },
159936
160514
  "user .env": { exists: false, path: "" },
159937
160515
  "legacy config.json": { exists: false, path: "" }
@@ -159941,6 +160519,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
159941
160519
  const paths = {
159942
160520
  "project memorix.yml": configProjectRoot ? join25(configProjectRoot, "memorix.yml") : null,
159943
160521
  "user memorix.yml": join25(home, ".memorix", "memorix.yml"),
160522
+ "project memorix.toml": configProjectRoot ? join25(configProjectRoot, "memorix.toml") : null,
160523
+ "user config.toml": join25(home, ".memorix", "config.toml"),
159944
160524
  "project .env": configProjectRoot ? join25(configProjectRoot, ".env") : null,
159945
160525
  "user .env": join25(home, ".memorix", ".env"),
159946
160526
  "legacy config.json": join25(home, ".memorix", "config.json")
@@ -159955,67 +160535,61 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
159955
160535
  } catch {
159956
160536
  }
159957
160537
  const values = [];
159958
- const getEnvSource = async (envKey, ymlSource) => {
159959
- if (process.env[envKey]) {
159960
- try {
159961
- const { getLoadedEnvFiles: getLoadedEnvFiles2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
159962
- const envFiles = getLoadedEnvFiles2();
159963
- if (envFiles.length > 0) return `.env (${envKey})`;
159964
- } catch {
159965
- }
159966
- return `env:${envKey}`;
159967
- }
159968
- return ymlSource ?? "default";
159969
- };
159970
- const llmProvider = process.env.MEMORIX_LLM_PROVIDER || yml.llm?.provider;
159971
- if (llmProvider) values.push({ key: "llm.provider", value: llmProvider, source: await getEnvSource("MEMORIX_LLM_PROVIDER", yml.llm?.provider ? "memorix.yml" : void 0) });
159972
- const llmModel = process.env.MEMORIX_LLM_MODEL || yml.llm?.model;
159973
- if (llmModel) values.push({ key: "llm.model", value: llmModel, source: await getEnvSource("MEMORIX_LLM_MODEL", yml.llm?.model ? "memorix.yml" : void 0) });
159974
- const llmKey = process.env.MEMORIX_LLM_API_KEY || process.env.MEMORIX_API_KEY || yml.llm?.apiKey || process.env.OPENAI_API_KEY;
160538
+ const tomlSource = configProjectRoot && files["project memorix.toml"].exists ? "memorix.toml" : files["user config.toml"].exists ? "config.toml" : "default";
160539
+ const legacySource = files["project memorix.yml"].exists || files["user memorix.yml"].exists ? "memorix.yml" : files["legacy config.json"].exists ? "config.json" : "default";
160540
+ const configSource = resolved.sources.toml.length > 0 ? tomlSource : resolved.sources.legacy.length > 0 ? legacySource : "default";
160541
+ const source = (envKey) => resolveDashboardKeySource({
160542
+ value: process.env[envKey] ?? "",
160543
+ envKeys: [envKey],
160544
+ configSource
160545
+ });
160546
+ if (resolved.memory.llm.provider) values.push({ key: "llm.provider", value: resolved.memory.llm.provider, source: source("MEMORIX_LLM_PROVIDER") });
160547
+ if (resolved.memory.llm.model) values.push({ key: "llm.model", value: resolved.memory.llm.model, source: source("MEMORIX_LLM_MODEL") });
160548
+ const llmKey = resolved.memory.llm.apiKey;
160549
+ const memoryKeyEnv = [
160550
+ "MEMORIX_LLM_API_KEY",
160551
+ "MEMORIX_API_KEY",
160552
+ "OPENAI_API_KEY",
160553
+ "ANTHROPIC_API_KEY",
160554
+ ...resolved.memory.llm.provider === "openrouter" || resolved.memory.llm.baseUrl?.includes("openrouter.ai") ? ["OPENROUTER_API_KEY"] : []
160555
+ ];
159975
160556
  if (llmKey) {
159976
- let src = "unknown";
159977
- if (process.env.MEMORIX_LLM_API_KEY) src = await getEnvSource("MEMORIX_LLM_API_KEY");
159978
- else if (process.env.MEMORIX_API_KEY) src = await getEnvSource("MEMORIX_API_KEY");
159979
- else if (yml.llm?.apiKey) src = "memorix.yml (move to .env!)";
159980
- else if (process.env.OPENAI_API_KEY) src = await getEnvSource("OPENAI_API_KEY");
160557
+ const src = resolveDashboardKeySource({ value: llmKey, envKeys: memoryKeyEnv, configSource });
159981
160558
  values.push({ key: "llm.apiKey", value: "****" + llmKey.slice(-4), source: src, sensitive: true });
159982
160559
  } else {
159983
160560
  values.push({ key: "llm.apiKey", value: "not set", source: "none" });
159984
160561
  }
159985
- const agentProvider = process.env.MEMORIX_AGENT_PROVIDER || process.env.MEMORIX_AGENT_LLM_PROVIDER || yml.agent?.provider;
159986
- if (agentProvider) {
159987
- let source;
159988
- if (process.env.MEMORIX_AGENT_PROVIDER) source = await getEnvSource("MEMORIX_AGENT_PROVIDER");
159989
- else if (process.env.MEMORIX_AGENT_LLM_PROVIDER) source = `${await getEnvSource("MEMORIX_AGENT_LLM_PROVIDER")} (legacy)`;
159990
- else source = yml.agent?.provider ? "memorix.yml" : "default";
159991
- values.push({ key: "agent.provider", value: agentProvider, source });
159992
- }
159993
- const agentModel = process.env.MEMORIX_AGENT_MODEL || process.env.MEMORIX_AGENT_LLM_MODEL || yml.agent?.model;
159994
- if (agentModel) {
159995
- let source;
159996
- if (process.env.MEMORIX_AGENT_MODEL) source = await getEnvSource("MEMORIX_AGENT_MODEL");
159997
- else if (process.env.MEMORIX_AGENT_LLM_MODEL) source = `${await getEnvSource("MEMORIX_AGENT_LLM_MODEL")} (legacy)`;
159998
- else source = yml.agent?.model ? "memorix.yml" : "default";
159999
- values.push({ key: "agent.model", value: agentModel, source });
160000
- }
160001
- const agentKey = process.env.MEMORIX_AGENT_API_KEY || process.env.MEMORIX_AGENT_LLM_API_KEY || yml.agent?.apiKey;
160562
+ if (resolved.agent.provider) {
160563
+ values.push({ key: "agent.provider", value: resolved.agent.provider, source: source("MEMORIX_AGENT_PROVIDER") });
160564
+ }
160565
+ if (resolved.agent.model) {
160566
+ values.push({ key: "agent.model", value: resolved.agent.model, source: source("MEMORIX_AGENT_MODEL") });
160567
+ }
160568
+ const agentKey = resolved.agent.apiKey;
160002
160569
  if (agentKey) {
160003
- let src = "unknown";
160004
- if (process.env.MEMORIX_AGENT_API_KEY) src = await getEnvSource("MEMORIX_AGENT_API_KEY");
160005
- else if (process.env.MEMORIX_AGENT_LLM_API_KEY) src = `${await getEnvSource("MEMORIX_AGENT_LLM_API_KEY")} (legacy)`;
160006
- else if (yml.agent?.apiKey) src = "memorix.yml (move to .env!)";
160570
+ const src = resolveDashboardKeySource({
160571
+ value: agentKey,
160572
+ envKeys: ["MEMORIX_AGENT_API_KEY", "MEMORIX_AGENT_LLM_API_KEY"],
160573
+ configSource
160574
+ });
160007
160575
  values.push({ key: "agent.apiKey", value: "****" + agentKey.slice(-4), source: src, sensitive: true });
160008
160576
  } else {
160009
- values.push({ key: "agent.apiKey", value: "fallback to llm.apiKey", source: "default" });
160010
- }
160011
- const embProvider = process.env.MEMORIX_EMBEDDING || yml.embedding?.provider || "off";
160012
- values.push({ key: "embedding.provider", value: embProvider, source: await getEnvSource("MEMORIX_EMBEDDING", yml.embedding?.provider ? "memorix.yml" : void 0) });
160013
- values.push({ key: "git.autoHook", value: String(yml.git?.autoHook ?? false), source: yml.git?.autoHook !== void 0 ? "memorix.yml" : "default" });
160014
- values.push({ key: "git.skipMergeCommits", value: String(yml.git?.skipMergeCommits ?? true), source: yml.git?.skipMergeCommits !== void 0 ? "memorix.yml" : "default" });
160015
- if (yml.behavior?.formationMode) values.push({ key: "behavior.formationMode", value: yml.behavior.formationMode, source: "memorix.yml" });
160016
- if (yml.behavior?.sessionInject) values.push({ key: "behavior.sessionInject", value: yml.behavior.sessionInject, source: "memorix.yml" });
160017
- values.push({ key: "server.transport", value: yml.server?.transport || "stdio", source: yml.server?.transport ? "memorix.yml" : "default" });
160018
- values.push({ key: "server.dashboard", value: String(yml.server?.dashboard ?? true), source: yml.server?.dashboard !== void 0 ? "memorix.yml" : "default" });
160577
+ values.push({
160578
+ key: "agent.apiKey",
160579
+ value: "fallback to llm.apiKey",
160580
+ source: llmKey ? resolveDashboardKeySource({ value: llmKey, envKeys: memoryKeyEnv, configSource }) : "default"
160581
+ });
160582
+ }
160583
+ values.push({ key: "embedding.provider", value: resolved.embedding.provider || "off", source: source("MEMORIX_EMBEDDING") });
160584
+ if (resolved.embedding.model) values.push({ key: "embedding.model", value: resolved.embedding.model, source: source("MEMORIX_EMBEDDING_MODEL") });
160585
+ if (resolved.rerank.provider !== "off") {
160586
+ values.push({ key: "rerank.provider", value: resolved.rerank.provider, source: source("MEMORIX_RERANK_PROVIDER") });
160587
+ if (resolved.rerank.model) values.push({ key: "rerank.model", value: resolved.rerank.model, source: source("MEMORIX_RERANK_MODEL") });
160588
+ }
160589
+ values.push({ key: "git.autoHook", value: String(resolved.git.autoHook ?? false), source: configSource });
160590
+ values.push({ key: "git.skipMergeCommits", value: String(resolved.git.skipMergeCommits ?? true), source: configSource });
160591
+ values.push({ key: "server.transport", value: resolved.server.transport || "stdio", source: configSource });
160592
+ values.push({ key: "server.dashboard", value: String(resolved.server.dashboard ?? true), source: configSource });
160019
160593
  sendJson(res, { files, values });
160020
160594
  break;
160021
160595
  }
@@ -160250,6 +160824,37 @@ function normalizeDashboardTask(task) {
160250
160824
  preferredRole: task.preferred_role ?? task.preferredRole ?? null
160251
160825
  };
160252
160826
  }
160827
+ function emptyTeamSnapshot(mode, scope, status) {
160828
+ return {
160829
+ status,
160830
+ mode,
160831
+ readOnly: true,
160832
+ scope,
160833
+ agents: [],
160834
+ activeCount: 0,
160835
+ recentCount: 0,
160836
+ historicalCount: 0,
160837
+ totalAgents: 0,
160838
+ recentWindowDays: 7,
160839
+ locks: [],
160840
+ tasks: [],
160841
+ availableTasks: 0,
160842
+ sessions: 0,
160843
+ roles: [],
160844
+ roleOccupancy: [],
160845
+ handoffs: [],
160846
+ openTasks: 0,
160847
+ openHandoffs: 0,
160848
+ totalUnread: 0,
160849
+ activeSessions: 0,
160850
+ error: status === "error" ? "Coordination state unavailable" : "Coordination state is incomplete",
160851
+ errorCode: status === "error" ? "COORDINATION_STATE_UNAVAILABLE" : "COORDINATION_STATE_DEGRADED"
160852
+ };
160853
+ }
160854
+ function logTeamSnapshotFailure(scope, status) {
160855
+ const safeScope = scope === "global" ? "global" : "project";
160856
+ console.error("[dashboard] coordination snapshot unavailable", { status, scope: safeScope });
160857
+ }
160253
160858
  async function buildTeamSnapshot(dataDir, projectId, scope, mode) {
160254
160859
  try {
160255
160860
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
@@ -160276,8 +160881,9 @@ async function buildTeamSnapshot(dataDir, projectId, scope, mode) {
160276
160881
  const roleOccupancy = effectiveProjectId ? teamStore.getRoleOccupancy(effectiveProjectId) : [];
160277
160882
  const handoffs = effectiveProjectId ? teamStore.listHandoffs(effectiveProjectId) : [];
160278
160883
  return {
160884
+ status: "ok",
160279
160885
  mode,
160280
- readOnly: mode === "standalone",
160886
+ readOnly: true,
160281
160887
  scope,
160282
160888
  agents: withTier,
160283
160889
  activeCount,
@@ -160298,28 +160904,8 @@ async function buildTeamSnapshot(dataDir, projectId, scope, mode) {
160298
160904
  activeSessions: activeCount
160299
160905
  };
160300
160906
  } catch {
160301
- return {
160302
- mode,
160303
- readOnly: mode === "standalone",
160304
- scope,
160305
- agents: [],
160306
- activeCount: 0,
160307
- recentCount: 0,
160308
- historicalCount: 0,
160309
- totalAgents: 0,
160310
- recentWindowDays: 7,
160311
- locks: [],
160312
- tasks: [],
160313
- availableTasks: 0,
160314
- sessions: 0,
160315
- roles: [],
160316
- roleOccupancy: [],
160317
- handoffs: [],
160318
- openTasks: 0,
160319
- openHandoffs: 0,
160320
- totalUnread: 0,
160321
- activeSessions: 0
160322
- };
160907
+ logTeamSnapshotFailure(scope, "error");
160908
+ return emptyTeamSnapshot(mode, scope, "error");
160323
160909
  }
160324
160910
  }
160325
160911
  function readBody(req) {
@@ -160338,13 +160924,12 @@ function readBody(req) {
160338
160924
  req.on("error", reject);
160339
160925
  });
160340
160926
  }
160341
- async function startDashboard(dataDir, port, staticDir, projectId, projectName, autoOpen = true, teamInstances, projectRoot = null, projectResolved = true) {
160927
+ async function startDashboard(dataDir, port, staticDir, projectId, projectName, autoOpen = true, projectRoot = null, projectResolved = true) {
160342
160928
  await initObservationStore(dataDir);
160343
160929
  await initSessionStore(dataDir);
160344
160930
  const resolvedStaticDir = staticDir;
160345
160931
  const baseDir = getBaseDataDir();
160346
- const isControlPlane = !!teamInstances;
160347
- const state = { projectId, projectName, dataDir, projectRoot, projectResolved, mode: isControlPlane ? "control-plane" : "standalone", port };
160932
+ const state = { projectId, projectName, dataDir, projectRoot, projectResolved, mode: "standalone", port };
160348
160933
  const server = createServer(async (req, res) => {
160349
160934
  const url = req.url || "/";
160350
160935
  if (url.startsWith("/api/set-current-project") && req.method === "POST") {
@@ -160366,54 +160951,20 @@ async function startDashboard(dataDir, port, staticDir, projectId, projectName,
160366
160951
  }
160367
160952
  return;
160368
160953
  }
160369
- if (url.startsWith("/api/team")) {
160370
- if (!teamInstances) {
160371
- const parsedUrl = new URL(url, `http://127.0.0.1:${port}`);
160372
- const scope = parsedUrl.searchParams.get("scope") || "project";
160373
- sendJson(res, await buildTeamSnapshot(state.dataDir, state.projectId, scope, state.mode));
160374
- return;
160375
- }
160376
- try {
160377
- teamInstances.fileLocks.cleanExpired();
160378
- const agents = teamInstances.registry.listAgents();
160379
- const locks = teamInstances.fileLocks.listLocks();
160380
- const tasks = teamInstances.taskManager.list();
160381
- const available = teamInstances.taskManager.getAvailable();
160382
- let roles = [];
160383
- let roleOccupancy = [];
160384
- let handoffs = [];
160385
- try {
160386
- const { getTeamStore: getTeamStore2, isTeamStoreInitialized: isTeamStoreInitialized2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
160387
- if (isTeamStoreInitialized2()) {
160388
- const teamStore = getTeamStore2();
160389
- const projectId2 = state.projectId;
160390
- roles = teamStore.listRoles(projectId2);
160391
- roleOccupancy = teamStore.getRoleOccupancy(projectId2);
160392
- handoffs = teamStore.listHandoffs(projectId2);
160393
- }
160394
- } catch {
160395
- }
160954
+ const parsedUrl = new URL(url, `http://127.0.0.1:${port}`);
160955
+ if (parsedUrl.pathname === "/api/team") {
160956
+ if (req.method !== "GET") {
160396
160957
  sendJson(res, {
160397
- agents: agents.map((a) => ({
160398
- ...a,
160399
- unread: teamInstances.messageBus.getUnreadCount(a.id)
160400
- })),
160401
- activeCount: teamInstances.registry.getActiveCount(),
160402
- locks,
160403
- tasks,
160404
- availableTasks: available.length,
160405
- roles,
160406
- roleOccupancy,
160407
- handoffs,
160408
- // Resume data for "Continue this project" area
160409
- openTasks: tasks.filter((t2) => t2.status === "pending" || t2.status === "in_progress").length,
160410
- openHandoffs: handoffs.filter((h2) => h2.handoff_status === "open" || h2.handoffStatus === "open").length,
160411
- totalUnread: agents.reduce((sum, a) => sum + teamInstances.messageBus.getUnreadCount(a.id), 0),
160412
- activeSessions: agents.filter((a) => a.status === "active").length
160413
- });
160414
- } catch {
160415
- sendJson(res, { agents: [], activeCount: 0, locks: [], tasks: [], availableTasks: 0, roles: [], roleOccupancy: [], handoffs: [] });
160958
+ status: "error",
160959
+ readOnly: true,
160960
+ error: "Coordination status is read-only",
160961
+ errorCode: "COORDINATION_STATUS_READ_ONLY"
160962
+ }, 405);
160963
+ return;
160416
160964
  }
160965
+ const scope = parsedUrl.searchParams.get("scope") || "project";
160966
+ const snapshot = await buildTeamSnapshot(state.dataDir, state.projectId, scope, state.mode);
160967
+ sendJson(res, snapshot, snapshot.status === "error" ? 503 : 200);
160417
160968
  return;
160418
160969
  }
160419
160970
  if (url.startsWith("/api/")) {
@@ -160434,13 +160985,12 @@ async function startDashboard(dataDir, port, staticDir, projectId, projectName,
160434
160985
  server.listen(port, "127.0.0.1", () => {
160435
160986
  const url = `http://127.0.0.1:${port}`;
160436
160987
  const resolvedLabel = projectResolved ? "resolved" : "unresolved";
160437
- const modeLabel = isControlPlane ? "Control Plane" : "Standalone";
160988
+ const modeLabel = "Standalone";
160438
160989
  console.error(` Memorix Dashboard [${modeLabel}]`);
160439
160990
  console.error(` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
160440
160991
  console.error(` Mode: ${modeLabel}`);
160441
160992
  console.error(` Project: ${projectName} (${projectId}) [${resolvedLabel}]`);
160442
160993
  console.error(` Local: ${url}`);
160443
- if (isControlPlane) console.error(` MCP: ${url}/mcp`);
160444
160994
  console.error(` Data dir: ${dataDir}`);
160445
160995
  console.error(`
160446
160996
  Press Ctrl+C to stop
@@ -160463,10 +161013,14 @@ var init_server = __esm({
160463
161013
  init_dotenv_loader();
160464
161014
  init_yaml_loader();
160465
161015
  init_yaml_loader();
161016
+ init_resolved_config();
160466
161017
  init_graph_scope();
160467
161018
  init_retention();
160468
161019
  init_visibility();
161020
+ init_evidence_store();
161021
+ init_feedback();
160469
161022
  init_maintenance();
161023
+ init_config_provenance();
160470
161024
  MIME_TYPES = {
160471
161025
  ".html": "text/html; charset=utf-8",
160472
161026
  ".css": "text/css; charset=utf-8",
@@ -160934,7 +161488,7 @@ __export(planner_exports, {
160934
161488
  materializeTaskGraph: () => materializeTaskGraph,
160935
161489
  seedAutonomousPipeline: () => seedAutonomousPipeline
160936
161490
  });
160937
- import { randomUUID as randomUUID16 } from "crypto";
161491
+ import { randomUUID as randomUUID18 } from "crypto";
160938
161492
  function isPlannerTask(metadata) {
160939
161493
  if (!metadata) return null;
160940
161494
  try {
@@ -160992,7 +161546,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
160992
161546
  const maxIterations = config.maxIterations ?? 3;
160993
161547
  const taskBudget = config.taskBudget ?? 15;
160994
161548
  const structuredPlan = opts?.structuredPlan ?? true;
160995
- const pipelineId = randomUUID16();
161549
+ const pipelineId = randomUUID18();
160996
161550
  const meta = {
160997
161551
  plannerType: "plan",
160998
161552
  pipelineId,
@@ -161664,8 +162218,8 @@ var init_official_skills = __esm({
161664
162218
  "",
161665
162219
  "| Situation | Prefer | CLI fallback |",
161666
162220
  "|---|---|---|",
161667
- '| Starting a coding task and needing the best task-lensed project map | `memorix_project_context` with the user\'s task | `memorix context "<task>" --brief-json` |',
161668
- '| Continuing prior project work | `memorix_project_context` with the user\'s task | `memorix resume "<task>" --brief-json` |',
162221
+ '| Starting a coding task and needing the best task-lensed project map | `memorix_project_context` with the user\'s task | `memorix context "<task>" --fallback --brief-json` |',
162222
+ '| Continuing prior project work | `memorix_project_context` with the user\'s task | `memorix resume "<task>" --fallback --brief-json` |',
161669
162223
  '| Need structured refs/freshness for code-bound memories | `memorix_context_pack` | `memorix codegraph context-pack --task "<topic>"` |',
161670
162224
  '| Explicit memory graph question | `memorix_graph_context` | `memorix memory graph-context --query "<topic>"` |',
161671
162225
  '| Specific past decision, bug, file, or change | `memorix_search` | `memorix memory search --query "<topic>"` |',
@@ -161678,7 +162232,7 @@ var init_official_skills = __esm({
161678
162232
  "",
161679
162233
  "- Default first step for non-trivial coding work: call `memorix_project_context` before progress files, dev-log reads, ad-hoc file reads, or git archaeology. Pass the user's actual task so Memorix can choose a bugfix, feature, release, onboarding, refactor, docs, test, or general brief. Start by reading its suggested files.",
161680
162234
  "- In Claude Code headless/print-mode, an initial MCP `pending` status is not a failure by itself. Prefer MCP first: use the client's tool discovery/dynamic loading to find `memorix_project_context` when it is not in the first visible tool list.",
161681
- '- Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run `memorix resume "<task>" --brief-json` exactly once before inspecting files, Git history, progress notes, or guessing. The absence of `.memorix` or visible memory files never proves project memory is empty. For a new task, use `memorix context "<task>" --brief-json` instead. Use `--json` only when a diagnostic needs the detailed legacy payload. If that one command fails, report it and proceed normally; do not probe help, enumerate commands, or chain broad searches.',
162235
+ '- Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run `memorix resume "<task>" --fallback --brief-json` exactly once before inspecting files, Git history, progress notes, or guessing. The absence of `.memorix` or visible memory files never proves project memory is empty. For a new task, use `memorix context "<task>" --fallback --brief-json` instead. Use `--json` only when a diagnostic needs the detailed legacy payload. If that one command fails, report it and proceed normally; do not probe help, enumerate commands, or chain broad searches.',
161682
162236
  "- After a successful `memorix_project_context`, the brief is the default retrieval boundary. Do not call more Memorix retrieval tools after a complete brief. Use Context Pack, search, or detail only when the brief lacks a specific reference, freshness field, or fact needed for the task, or when the user explicitly asks for deeper history. In MCP, name that missing fact in `purpose` when intentionally expanding beyond the brief. Do not retrieve the same decision twice just to confirm an already-complete brief.",
161683
162237
  "- If the user asks for read-only work or says not to modify files, do not call `memorix_store` just to record an assessment. Store only when the user explicitly asks to preserve it.",
161684
162238
  "- Use `memorix_search` for a specific decision, bug, file, or prior change that the project context did not answer. Fetch detail only for a result you still need after the brief.",
@@ -162937,7 +163491,7 @@ alwaysApply: true
162937
163491
  "",
162938
163492
  `Default first step for non-trivial coding work: call \`memorix_project_context\` with the user's actual task before progress files, dev-log reads, ad-hoc file reads, or git archaeology. Memorix will choose a task-lensed brief (bugfix, feature, release, onboarding, refactor, docs, test, or general). When the task is continuing prior work, the same brief also includes a bounded prior-work projection. Treat its "Start here" files as the first ${contextNoun} files to inspect.`,
162939
163493
  "",
162940
- 'If the MCP tool is not visible yet but the client supports tool discovery or dynamic loading, search/select `memorix_project_context` first. Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run exactly one CLI brief with the user\'s real task before inspecting files, Git history, progress notes, or guessing: `memorix resume "<task>" --brief-json`. For a new task, use `memorix context "<task>" --brief-json` instead. The absence of `.memorix` or visible memory files never proves project memory is empty. Use `--json` only when a diagnostic needs the detailed legacy payload. If that one command fails, report it and proceed normally. Do not probe help, enumerate commands, chain broad searches, wait indefinitely on MCP startup, or hand-write tool-call syntax.',
163494
+ 'If the MCP tool is not visible yet but the client supports tool discovery or dynamic loading, search/select `memorix_project_context` first. Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run exactly one CLI brief with the user\'s real task before inspecting files, Git history, progress notes, or guessing: `memorix resume "<task>" --fallback --brief-json`. For a new task, use `memorix context "<task>" --fallback --brief-json` instead. The absence of `.memorix` or visible memory files never proves project memory is empty. Use `--json` only when a diagnostic needs the detailed legacy payload. If that one command fails, report it and proceed normally. Do not probe help, enumerate commands, chain broad searches, wait indefinitely on MCP startup, or hand-write tool-call syntax.',
162941
163495
  "",
162942
163496
  "After a successful `memorix_project_context` result, the brief is the default retrieval boundary. Do not call more Memorix retrieval tools after a complete brief. Use `memorix_context_pack`, `memorix_search`, or `memorix_detail` only when the brief lacks a specific reference, freshness field, or fact needed for the task, or when the user explicitly asks for deeper history. In MCP, name that missing fact in `purpose` when intentionally expanding beyond the brief. Do not retrieve the same decision twice just to confirm an already-complete brief.",
162943
163497
  "If the user asks for read-only work or says not to modify files, do not call `memorix_store` just to record an assessment. Store only when the user explicitly asks to preserve it.",
@@ -163240,7 +163794,7 @@ init_freshness();
163240
163794
  init_obs_store();
163241
163795
  init_mini_skill_store();
163242
163796
  init_session_store();
163243
- import { createHash as createHash15 } from "crypto";
163797
+ import { createHash as createHash16 } from "crypto";
163244
163798
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
163245
163799
  import { z as z626 } from "zod";
163246
163800
 
@@ -163720,6 +164274,8 @@ init_mini_skill_store();
163720
164274
  init_mini_skills();
163721
164275
  init_secret_filter();
163722
164276
  init_visibility();
164277
+ init_persistence();
164278
+ init_feedback();
163723
164279
  function normalizeMemoryBrowseQuery(query) {
163724
164280
  const trimmed = query.trim();
163725
164281
  if (!trimmed) return "";
@@ -163757,9 +164313,27 @@ function normalizeMemoryBrowseQuery(query) {
163757
164313
  async function compactSearch(options, surface = "mcp") {
163758
164314
  await ensureFreshIndex();
163759
164315
  const searchOptions = { ...options, query: normalizeMemoryBrowseQuery(options.query) };
163760
- const entries = (await searchObservations(searchOptions)).map(
164316
+ let entries = (await searchObservations(searchOptions)).map(
163761
164317
  (entry) => entry.projectId || !options.projectId ? entry : { ...entry, projectId: options.projectId }
163762
164318
  );
164319
+ if (options.projectId) {
164320
+ try {
164321
+ const feedback = new MemoryFeedbackStore();
164322
+ await feedback.init(await getProjectDataDir(options.projectId));
164323
+ const states = feedback.weights(options.projectId, "observation");
164324
+ entries = entries.map((entry) => {
164325
+ const state = states.get(String(entry.id));
164326
+ if (!state) return entry;
164327
+ return {
164328
+ ...entry,
164329
+ score: (entry.score ?? 0) * feedbackWeightMultiplier(state.weight),
164330
+ feedbackWeight: state.weight,
164331
+ feedbackStatus: state.status
164332
+ };
164333
+ }).filter((entry) => entry.feedbackStatus !== "archived" || options.status === "all").sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
164334
+ } catch {
164335
+ }
164336
+ }
163763
164337
  let formatted = formatIndexTable(entries, searchOptions.query, !options.projectId, surface);
163764
164338
  if (entries.length === 0 && options.projectId) {
163765
164339
  const allObservations = filterReadableObservations(getAllObservations(), options.reader);
@@ -164588,6 +165162,8 @@ init_detector();
164588
165162
  init_launch_root();
164589
165163
  init_aliases();
164590
165164
  init_persistence();
165165
+ init_evidence_store();
165166
+ init_feedback();
164591
165167
 
164592
165168
  // src/rules/syncer.ts
164593
165169
  init_esm_shims();
@@ -164600,9 +165176,9 @@ init_frontmatter();
164600
165176
 
164601
165177
  // src/rules/utils.ts
164602
165178
  init_esm_shims();
164603
- import { createHash as createHash13 } from "crypto";
165179
+ import { createHash as createHash14 } from "crypto";
164604
165180
  function hashContent2(content) {
164605
- return createHash13("sha256").update(content.trim()).digest("hex").substring(0, 16);
165181
+ return createHash14("sha256").update(content.trim()).digest("hex").substring(0, 16);
164606
165182
  }
164607
165183
  function generateRuleId(source, filePath) {
164608
165184
  const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
@@ -166804,6 +167380,10 @@ var TOOL_PROFILES = Object.freeze({
166804
167380
  // multimodal support to one additional schema rather than enabling lite.
166805
167381
  memorix_session_start: ["micro", "lite", "team", "full"],
166806
167382
  memorix_media: ["micro", "lite", "team", "full"],
167383
+ // Evidence and feedback are deliberate operator/context-control surfaces;
167384
+ // keep the default micro profile at its nine-tool token budget.
167385
+ memorix_evidence: ["lite", "team", "full"],
167386
+ memorix_feedback: ["lite", "team", "full"],
166807
167387
  // ── lite: extended solo memory surface ────────────────────────────
166808
167388
  memorix_graph_context: ["lite", "team", "full"],
166809
167389
  memorix_timeline: ["lite", "team", "full"],
@@ -167102,7 +167682,7 @@ function coerceObjectArray(val) {
167102
167682
  return [];
167103
167683
  }
167104
167684
  function createDeterministicInstanceId(projectId, agentType, agentName) {
167105
- const digest2 = createHash15("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
167685
+ const digest2 = createHash16("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
167106
167686
  return `auto-${digest2}`;
167107
167687
  }
167108
167688
  var BOOTSTRAP_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
@@ -167403,7 +167983,7 @@ The path should point to a directory containing a .git folder.`
167403
167983
  };
167404
167984
  const server = existingServer ?? new McpServer({
167405
167985
  name: "memorix",
167406
- version: true ? "1.7.3" : "1.0.1"
167986
+ version: true ? "1.8.1" : "1.0.1"
167407
167987
  });
167408
167988
  const originalRegisterTool = server.registerTool.bind(server);
167409
167989
  server.registerTool = ((name, ...args) => {
@@ -170556,7 +171136,7 @@ ${json2}
170556
171136
  }
170557
171137
  }
170558
171138
  console.error(`[memorix] Dashboard staticDir: ${staticDir}`);
170559
- startDashboard2(projectDir2, portNum, staticDir, project.id, project.name, false, void 0, project.rootPath, projectResolved).then(() => {
171139
+ startDashboard2(projectDir2, portNum, staticDir, project.id, project.name, false, project.rootPath, projectResolved).then(() => {
170560
171140
  dashboardRunning = true;
170561
171141
  }).catch((err) => {
170562
171142
  console.error("[memorix] Dashboard error:", err);
@@ -170582,7 +171162,7 @@ ${json2}
170582
171162
  dashboardRunning = true;
170583
171163
  const { exec: execCmd } = await import("child_process");
170584
171164
  const openCmd = process.platform === "win32" ? `start "" "${url}"` : process.platform === "darwin" ? `open "${url}"` : `xdg-open "${url}"`;
170585
- execCmd(openCmd, () => {
171165
+ execCmd(openCmd, { windowsHide: true }, () => {
170586
171166
  });
170587
171167
  return {
170588
171168
  content: [{
@@ -170606,6 +171186,113 @@ ${json2}
170606
171186
  }
170607
171187
  }
170608
171188
  );
171189
+ server.registerTool(
171190
+ "memorix_evidence",
171191
+ {
171192
+ title: "Evidence Cards",
171193
+ description: "Inspect the bounded, project-scoped provenance cards behind Memorix context. Use list for a compact index, get/events for one source, sync to repair the projection, and stale when known source files changed. Cards expose sourceRef, locator, capturedHash, and freshness.",
171194
+ inputSchema: {
171195
+ action: z626.enum(["list", "get", "sync", "stale", "events"]).describe("Evidence operation"),
171196
+ candidateKind: z626.enum(["observation", "code", "git", "test", "document", "workflow", "graph"]).optional().default("observation"),
171197
+ candidateId: z626.string().optional().describe("Candidate id, usually an observation id"),
171198
+ query: z626.string().optional().describe("Optional title/source query for list"),
171199
+ paths: z626.array(z626.string()).max(200).optional().describe("Changed source paths for stale"),
171200
+ reason: z626.string().max(500).optional().describe("Why evidence became stale"),
171201
+ limit: z626.number().int().positive().max(100).optional().default(20)
171202
+ }
171203
+ },
171204
+ async ({ action, candidateKind, candidateId, query, paths, reason, limit: limit2 }) => {
171205
+ const unresolved = requireResolvedProject("inspect evidence cards for the current project");
171206
+ if (unresolved) return unresolved;
171207
+ const cards = new EvidenceCardStore();
171208
+ await cards.init(projectDir2);
171209
+ if (action === "sync") {
171210
+ const observations2 = await getObservationStore().loadByProject(project.id);
171211
+ const synced = cards.syncObservations(observations2);
171212
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, synced: synced.length, cards: synced.slice(0, limit2 ?? 20) }, null, 2) }] };
171213
+ }
171214
+ if (action === "stale") {
171215
+ const changed = cards.markStaleForPaths(project.id, paths ?? [], reason ?? "source file changed");
171216
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, staleMarked: changed, paths: paths ?? [] }, null, 2) }] };
171217
+ }
171218
+ if (action === "get") {
171219
+ if (!candidateId?.trim()) return { content: [{ type: "text", text: "candidateId is required for evidence get." }], isError: true };
171220
+ const card = cards.get(project.id, candidateKind ?? "observation", candidateId.trim());
171221
+ if (!card) return { content: [{ type: "text", text: "Evidence card not found." }], isError: true };
171222
+ return { content: [{ type: "text", text: JSON.stringify({ card, events: cards.listEvents(card.id), projectId: project.id }, null, 2) }] };
171223
+ }
171224
+ if (action === "events") {
171225
+ if (!candidateId?.trim()) return { content: [{ type: "text", text: "candidateId is required for evidence events." }], isError: true };
171226
+ const card = cards.get(project.id, candidateKind ?? "observation", candidateId.trim());
171227
+ if (!card) return { content: [{ type: "text", text: "Evidence card not found." }], isError: true };
171228
+ return { content: [{ type: "text", text: JSON.stringify(cards.listEvents(card.id, limit2), null, 2) }] };
171229
+ }
171230
+ const needle = query?.trim().toLowerCase();
171231
+ const listed = cards.list(project.id, { limit: Math.max(limit2 ?? 20, 100) }).filter((card) => !needle || [card.title, card.summary, card.sourceRef, card.locator ?? ""].join(" ").toLowerCase().includes(needle)).slice(0, limit2 ?? 20);
171232
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, cards: listed }, null, 2) }] };
171233
+ }
171234
+ );
171235
+ server.registerTool(
171236
+ "memorix_feedback",
171237
+ {
171238
+ title: "Memory Feedback",
171239
+ description: "Record or inspect auditable memory outcomes. Positive verification/use strengthens recall; correction, conflict, source change, or weaken lowers it. Use revoke with targetEventId to undo one prior signal without deleting audit history.",
171240
+ inputSchema: {
171241
+ action: z626.enum(["record", "show", "audit"]).describe("Feedback operation"),
171242
+ candidateKind: z626.enum(["observation", "claim", "durable-memory", "workflow"]).optional().default("observation"),
171243
+ candidateId: z626.string().describe("Memory or knowledge candidate id"),
171244
+ signal: z626.enum(["user-correction", "verification-success", "verification-failure", "code-change", "used", "not-used", "code-conflict", "strengthen", "weaken", "revoke"]).optional(),
171245
+ sourceRef: z626.string().max(500).optional().describe("Source of this feedback, such as test:run-42 or user:chat"),
171246
+ actor: z626.string().max(200).optional(),
171247
+ note: z626.string().max(2e3).optional(),
171248
+ targetEventId: z626.string().optional().describe("Existing event id when action=record and signal=revoke"),
171249
+ limit: z626.number().int().positive().max(200).optional().default(100)
171250
+ }
171251
+ },
171252
+ async ({ action, candidateKind, candidateId, signal, sourceRef, actor, note, targetEventId, limit: limit2 }) => {
171253
+ const unresolved = requireResolvedProject("record memory feedback for the current project");
171254
+ if (unresolved) return unresolved;
171255
+ const feedback = new MemoryFeedbackStore();
171256
+ await feedback.init(projectDir2);
171257
+ const kind = candidateKind ?? "observation";
171258
+ if (action === "show") {
171259
+ return { content: [{ type: "text", text: JSON.stringify(feedback.getState(project.id, kind, candidateId), null, 2) }] };
171260
+ }
171261
+ if (action === "audit") {
171262
+ return { content: [{ type: "text", text: JSON.stringify(feedback.audit(project.id, kind, candidateId, limit2), null, 2) }] };
171263
+ }
171264
+ if (!signal || !sourceRef?.trim()) {
171265
+ return { content: [{ type: "text", text: "record requires signal and sourceRef." }], isError: true };
171266
+ }
171267
+ try {
171268
+ const result = feedback.record({
171269
+ projectId: project.id,
171270
+ candidateKind: kind,
171271
+ candidateId,
171272
+ signal,
171273
+ sourceRef,
171274
+ ...actor || currentAgentId ? { actor: actor ?? currentAgentId } : {},
171275
+ ...note ? { note } : {},
171276
+ ...targetEventId ? { targetEventId } : {}
171277
+ });
171278
+ if (kind === "observation") {
171279
+ const cards = new EvidenceCardStore();
171280
+ await cards.init(projectDir2);
171281
+ const card = cards.get(project.id, "observation", candidateId);
171282
+ if (card) {
171283
+ const verification = signal === "verification-success" || signal === "strengthen" ? "verified" : signal === "user-correction" || signal === "code-conflict" || signal === "weaken" ? "conflicted" : card.verification;
171284
+ cards.recordEvent(project.id, card.id, `feedback:${signal}`, note);
171285
+ if (verification !== card.verification) {
171286
+ cards.setVerification(project.id, card.id, verification);
171287
+ }
171288
+ }
171289
+ }
171290
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
171291
+ } catch (error) {
171292
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true };
171293
+ }
171294
+ }
171295
+ );
170609
171296
  if (teamFeaturesEnabled) {
170610
171297
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
170611
171298
  initTeamStoreForProject = initTeamStore2;
@@ -170791,7 +171478,7 @@ ${lines.join("\n")}` }] };
170791
171478
  server.registerTool(
170792
171479
  "team_task",
170793
171480
  {
170794
- title: "Task Board",
171481
+ title: "Task Status",
170795
171482
  description: 'Create, claim, complete, or list tasks in the team task board. Supports dependencies. Action "create": create a task. Action "claim": assign to yourself (atomic, race-safe). Action "complete": mark done with result. Action "list": show tasks.',
170796
171483
  inputSchema: {
170797
171484
  action: z626.enum(["create", "claim", "complete", "list"]).describe("Operation to perform"),