memorix 1.7.2 → 1.8.0

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 (90) hide show
  1. package/CHANGELOG.md +59 -1
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/assets/star-history-dark.svg +4 -4
  5. package/assets/star-history-light.svg +4 -4
  6. package/assets/star-history.json +47 -3
  7. package/dist/cli/index.js +1801 -254
  8. package/dist/dashboard/static/app.js +88 -4
  9. package/dist/dashboard/static/index.html +10 -0
  10. package/dist/dashboard/static/style.css +86 -0
  11. package/dist/index.js +1241 -224
  12. package/dist/maintenance-runner.js +972 -126
  13. package/dist/media-audio-runner.js +967 -121
  14. package/dist/media-video-runner.js +972 -126
  15. package/dist/memcode-runtime/CHANGELOG.md +59 -1
  16. package/dist/sdk.js +1240 -223
  17. package/dist/types.d.ts +8 -0
  18. package/dist/vector-backfill-runner.js +972 -126
  19. package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +1 -1
  20. package/docs/1.8.0-RELEASE-CANDIDATE-SPEC.md +75 -0
  21. package/docs/API_REFERENCE.md +37 -1
  22. package/docs/ARCHITECTURE.md +1 -1
  23. package/docs/CONFIGURATION.md +57 -1
  24. package/docs/DASHBOARD.md +10 -0
  25. package/docs/DEVELOPMENT.md +3 -2
  26. package/docs/INTEGRATIONS.md +1 -1
  27. package/docs/PERFORMANCE.md +3 -1
  28. package/docs/README.md +11 -1
  29. package/docs/SETUP.md +2 -1
  30. package/package.json +1 -1
  31. package/plugins/antigravity/memorix/rules/memorix.md +1 -1
  32. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  33. package/plugins/claude/memorix/SKILL.md +1 -1
  34. package/plugins/claude/memorix/skills/memorix-memory/SKILL.md +2 -2
  35. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  36. package/plugins/codebuddy/memorix/skills/memorix-memory/SKILL.md +2 -2
  37. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  38. package/plugins/codex/memorix/skills/memorix-memory/SKILL.md +2 -2
  39. package/plugins/copilot/memorix/plugin.json +1 -1
  40. package/plugins/copilot/memorix/skills/memorix-memory/SKILL.md +2 -2
  41. package/plugins/cursor/memorix/skills/memorix-memory/SKILL.md +2 -2
  42. package/plugins/gemini/memorix/GEMINI.md +1 -1
  43. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  44. package/plugins/omp/memorix/package.json +1 -1
  45. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  46. package/plugins/pi/memorix/package.json +1 -1
  47. package/plugins/pi/memorix/skills/memorix-memory/SKILL.md +2 -2
  48. package/server.json +2 -2
  49. package/src/cli/capability-map.ts +2 -0
  50. package/src/cli/commands/config-migrate.ts +7 -0
  51. package/src/cli/commands/context.ts +11 -0
  52. package/src/cli/commands/doctor.ts +5 -1
  53. package/src/cli/commands/evidence.ts +56 -0
  54. package/src/cli/commands/feedback.ts +58 -0
  55. package/src/cli/commands/init.ts +17 -0
  56. package/src/cli/commands/resume.ts +2 -0
  57. package/src/cli/commands/serve-http.ts +201 -12
  58. package/src/cli/commands/status.ts +2 -0
  59. package/src/cli/fallback-budget.ts +59 -0
  60. package/src/cli/index.ts +5 -1
  61. package/src/compact/engine.ts +26 -1
  62. package/src/config/dotenv-loader.ts +17 -0
  63. package/src/config/resolved-config.ts +110 -12
  64. package/src/config/toml-loader.ts +8 -0
  65. package/src/config/yaml-loader.ts +9 -0
  66. package/src/dashboard/config-provenance.ts +17 -0
  67. package/src/dashboard/evidence.ts +63 -0
  68. package/src/dashboard/server.ts +93 -68
  69. package/src/dashboard/static/index.html +10 -0
  70. package/src/dashboard/static/style.css +86 -0
  71. package/src/hooks/installers/index.ts +1 -1
  72. package/src/hooks/official-skills.ts +3 -3
  73. package/src/hooks/rules/memorix-agent-rules.md +1 -1
  74. package/src/llm/provider.ts +33 -7
  75. package/src/memory/feedback-model.ts +4 -0
  76. package/src/memory/feedback.ts +220 -0
  77. package/src/memory/observations.ts +19 -0
  78. package/src/memory/retention.ts +38 -5
  79. package/src/multimodal/image-loader.ts +6 -3
  80. package/src/rerank/http-provider.ts +125 -0
  81. package/src/rerank/index.ts +81 -0
  82. package/src/rerank/policy.ts +88 -0
  83. package/src/server/mcp-binding-store.ts +79 -0
  84. package/src/server/mcp-diagnostics.ts +40 -0
  85. package/src/server/tool-profile.ts +4 -0
  86. package/src/server.ts +121 -1
  87. package/src/store/evidence-store.ts +261 -0
  88. package/src/store/orama-store.ts +79 -53
  89. package/src/store/sqlite-db.ts +104 -0
  90. package/src/types.ts +8 -0
@@ -338,7 +338,7 @@ function closeAllDatabases() {
338
338
  _dbCache.delete(key);
339
339
  }
340
340
  }
341
- var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
341
+ var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_EVIDENCE_CARDS_TABLE, CREATE_EVIDENCE_CARD_EVENTS_TABLE, CREATE_MEMORY_FEEDBACK_STATES_TABLE, CREATE_MEMORY_FEEDBACK_EVENTS_TABLE, CREATE_MCP_BINDINGS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
342
342
  var init_sqlite_db = __esm({
343
343
  "src/store/sqlite-db.ts"() {
344
344
  "use strict";
@@ -984,6 +984,79 @@ CREATE TABLE IF NOT EXISTS compaction_checkpoints (
984
984
  created_at TEXT NOT NULL,
985
985
  updated_at TEXT NOT NULL
986
986
  );
987
+ `;
988
+ CREATE_EVIDENCE_CARDS_TABLE = `
989
+ CREATE TABLE IF NOT EXISTS evidence_cards (
990
+ id TEXT PRIMARY KEY,
991
+ project_id TEXT NOT NULL,
992
+ candidate_kind TEXT NOT NULL,
993
+ candidate_id TEXT NOT NULL,
994
+ title TEXT NOT NULL,
995
+ summary TEXT NOT NULL DEFAULT '',
996
+ entity_name TEXT,
997
+ source_kind TEXT NOT NULL,
998
+ source_ref TEXT NOT NULL,
999
+ locator TEXT,
1000
+ captured_hash TEXT,
1001
+ session_id TEXT,
1002
+ status TEXT NOT NULL DEFAULT 'active',
1003
+ verification TEXT NOT NULL DEFAULT 'unverified',
1004
+ freshness TEXT NOT NULL DEFAULT 'current',
1005
+ stale_reason TEXT,
1006
+ files_json TEXT NOT NULL DEFAULT '[]',
1007
+ related_entities_json TEXT NOT NULL DEFAULT '[]',
1008
+ created_at TEXT NOT NULL,
1009
+ updated_at TEXT NOT NULL,
1010
+ UNIQUE(project_id, candidate_kind, candidate_id)
1011
+ );
1012
+ `;
1013
+ CREATE_EVIDENCE_CARD_EVENTS_TABLE = `
1014
+ CREATE TABLE IF NOT EXISTS evidence_card_events (
1015
+ id TEXT PRIMARY KEY,
1016
+ project_id TEXT NOT NULL,
1017
+ card_id TEXT NOT NULL,
1018
+ kind TEXT NOT NULL,
1019
+ detail TEXT,
1020
+ created_at TEXT NOT NULL,
1021
+ FOREIGN KEY (card_id) REFERENCES evidence_cards(id) ON DELETE CASCADE
1022
+ );
1023
+ `;
1024
+ CREATE_MEMORY_FEEDBACK_STATES_TABLE = `
1025
+ CREATE TABLE IF NOT EXISTS memory_feedback_states (
1026
+ project_id TEXT NOT NULL,
1027
+ candidate_kind TEXT NOT NULL,
1028
+ candidate_id TEXT NOT NULL,
1029
+ weight REAL NOT NULL DEFAULT 1.0,
1030
+ lifecycle_status TEXT NOT NULL DEFAULT 'active',
1031
+ updated_at TEXT NOT NULL,
1032
+ PRIMARY KEY (project_id, candidate_kind, candidate_id)
1033
+ );
1034
+ `;
1035
+ CREATE_MEMORY_FEEDBACK_EVENTS_TABLE = `
1036
+ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1037
+ id TEXT PRIMARY KEY,
1038
+ project_id TEXT NOT NULL,
1039
+ candidate_kind TEXT NOT NULL,
1040
+ candidate_id TEXT NOT NULL,
1041
+ signal TEXT NOT NULL,
1042
+ actor TEXT,
1043
+ note TEXT,
1044
+ source_ref TEXT NOT NULL,
1045
+ target_event_id TEXT,
1046
+ delta REAL NOT NULL DEFAULT 0,
1047
+ observed_at TEXT NOT NULL
1048
+ );
1049
+ `;
1050
+ CREATE_MCP_BINDINGS_TABLE = `
1051
+ CREATE TABLE IF NOT EXISTS mcp_bindings (
1052
+ handle_id TEXT PRIMARY KEY,
1053
+ project_id TEXT NOT NULL,
1054
+ project_root TEXT NOT NULL,
1055
+ data_dir TEXT NOT NULL,
1056
+ created_at TEXT NOT NULL,
1057
+ last_used_at TEXT NOT NULL,
1058
+ expires_at TEXT
1059
+ );
987
1060
  `;
988
1061
  CREATE_INDEXES = `
989
1062
  CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
@@ -1046,6 +1119,12 @@ CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_target
1046
1119
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
1047
1120
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
1048
1121
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
1122
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC);
1123
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id);
1124
+ CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1125
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1126
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1127
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1049
1128
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1050
1129
  ON maintenance_jobs(project_id, kind, dedupe_key)
1051
1130
  WHERE status IN ('pending', 'running', 'retry');
@@ -1201,6 +1280,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1201
1280
  }
1202
1281
  db2.exec("CREATE INDEX IF NOT EXISTS idx_media_jobs_source_asset ON media_jobs(project_id, source_asset_id, status)");
1203
1282
  }
1283
+ },
1284
+ {
1285
+ id: "1.8-context-control-plane",
1286
+ apply: (db2) => {
1287
+ db2.exec(CREATE_EVIDENCE_CARDS_TABLE);
1288
+ db2.exec(CREATE_EVIDENCE_CARD_EVENTS_TABLE);
1289
+ db2.exec(CREATE_MEMORY_FEEDBACK_STATES_TABLE);
1290
+ db2.exec(CREATE_MEMORY_FEEDBACK_EVENTS_TABLE);
1291
+ db2.exec(CREATE_MCP_BINDINGS_TABLE);
1292
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC)");
1293
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id)");
1294
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at)");
1295
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1296
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC)");
1297
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1298
+ }
1204
1299
  }
1205
1300
  ];
1206
1301
  _dbCache = /* @__PURE__ */ new Map();
@@ -1246,6 +1341,8 @@ function loadEnvFile(filePath) {
1246
1341
  const parsed = parse(readFileSync2(filePath, "utf-8"));
1247
1342
  for (const [key, value] of Object.entries(parsed)) {
1248
1343
  if (!(key in process.env)) {
1344
+ loadedEnvKeys.add(key);
1345
+ loadedEnvValues.set(key, value);
1249
1346
  process.env[key] = value;
1250
1347
  injectedKeys.add(key);
1251
1348
  }
@@ -1258,6 +1355,8 @@ function loadDotenv(projectRoot, options = {}) {
1258
1355
  resetDotenv();
1259
1356
  }
1260
1357
  loadedEnvFiles.length = 0;
1358
+ loadedEnvKeys.clear();
1359
+ loadedEnvValues.clear();
1261
1360
  if (projectRoot) {
1262
1361
  loadEnvFile(getProjectDotenvPath(projectRoot));
1263
1362
  }
@@ -1273,11 +1372,12 @@ function resetDotenv() {
1273
1372
  dotenvLoaded = false;
1274
1373
  dotenvProjectRoot = null;
1275
1374
  loadedEnvFiles.length = 0;
1375
+ loadedEnvKeys.clear();
1276
1376
  }
1277
1377
  function getLoadedEnvFiles() {
1278
1378
  return loadedEnvFiles;
1279
1379
  }
1280
- var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, injectedKeys;
1380
+ var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
1281
1381
  var init_dotenv_loader = __esm({
1282
1382
  "src/config/dotenv-loader.ts"() {
1283
1383
  "use strict";
@@ -1286,6 +1386,8 @@ var init_dotenv_loader = __esm({
1286
1386
  dotenvLoaded = false;
1287
1387
  dotenvProjectRoot = null;
1288
1388
  loadedEnvFiles = [];
1389
+ loadedEnvKeys = /* @__PURE__ */ new Set();
1390
+ loadedEnvValues = /* @__PURE__ */ new Map();
1289
1391
  injectedKeys = /* @__PURE__ */ new Set();
1290
1392
  }
1291
1393
  });
@@ -1327,6 +1429,7 @@ function loadYamlConfig(projectRoot) {
1327
1429
  llm: { ...userConfig.llm, ...projectConfig.llm },
1328
1430
  agent: { ...userConfig.agent, ...projectConfig.agent },
1329
1431
  embedding: { ...userConfig.embedding, ...projectConfig.embedding },
1432
+ rerank: { ...userConfig.rerank, ...projectConfig.rerank },
1330
1433
  git: { ...userConfig.git, ...projectConfig.git },
1331
1434
  codegraph: { ...userConfig.codegraph, ...projectConfig.codegraph },
1332
1435
  behavior: { ...userConfig.behavior, ...projectConfig.behavior },
@@ -1563,6 +1666,7 @@ function mergeTomlConfig(base, override) {
1563
1666
  llm: { ...base.memory?.llm, ...override.memory?.llm }
1564
1667
  },
1565
1668
  embedding: { ...base.embedding, ...override.embedding },
1669
+ rerank: { ...base.rerank, ...override.rerank },
1566
1670
  hooks: { ...base.hooks, ...override.hooks },
1567
1671
  git: { ...base.git, ...override.git },
1568
1672
  codegraph: { ...base.codegraph, ...override.codegraph },
@@ -1659,6 +1763,7 @@ __export(resolved_config_exports, {
1659
1763
  getResolvedConfigForCwd: () => getResolvedConfigForCwd,
1660
1764
  getResolvedEmbeddingLane: () => getResolvedEmbeddingLane,
1661
1765
  getResolvedMemoryLane: () => getResolvedMemoryLane,
1766
+ getResolvedRerankLane: () => getResolvedRerankLane,
1662
1767
  resetResolvedConfigCache: () => resetResolvedConfigCache
1663
1768
  });
1664
1769
  import { homedir as homedir5 } from "os";
@@ -1691,6 +1796,16 @@ function getResolvedConfig(options = {}) {
1691
1796
  legacy.llm?.baseUrl
1692
1797
  );
1693
1798
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
1799
+ const memoryLlmApiKey = first(
1800
+ process.env.MEMORIX_LLM_API_KEY,
1801
+ process.env.MEMORIX_API_KEY,
1802
+ toml.memory?.llm?.api_key,
1803
+ yaml.llm?.apiKey,
1804
+ legacy.llm?.apiKey,
1805
+ process.env.OPENAI_API_KEY,
1806
+ process.env.ANTHROPIC_API_KEY,
1807
+ openRouterMemoryLlmApiKey
1808
+ );
1694
1809
  const resolved = {
1695
1810
  agent: {
1696
1811
  provider: first(
@@ -1731,16 +1846,7 @@ function getResolvedConfig(options = {}) {
1731
1846
  provider: memoryLlmProvider,
1732
1847
  model: memoryLlmModel,
1733
1848
  baseUrl: memoryLlmBaseUrl,
1734
- apiKey: first(
1735
- process.env.MEMORIX_LLM_API_KEY,
1736
- process.env.MEMORIX_API_KEY,
1737
- toml.memory?.llm?.api_key,
1738
- yaml.llm?.apiKey,
1739
- legacy.llm?.apiKey,
1740
- process.env.OPENAI_API_KEY,
1741
- process.env.ANTHROPIC_API_KEY,
1742
- openRouterMemoryLlmApiKey
1743
- )
1849
+ apiKey: memoryLlmApiKey
1744
1850
  }
1745
1851
  },
1746
1852
  embedding: {
@@ -1750,6 +1856,12 @@ function getResolvedConfig(options = {}) {
1750
1856
  apiKey: first(process.env.MEMORIX_EMBEDDING_API_KEY, toml.embedding?.api_key, yaml.embedding?.apiKey, legacy.embeddingApi?.apiKey, openRouterEmbeddingApiKey),
1751
1857
  dimensions: firstNumber(parseNumber(process.env.MEMORIX_EMBEDDING_DIMENSIONS), toml.embedding?.dimensions, yaml.embedding?.dimensions, legacy.embeddingApi?.dimensions)
1752
1858
  },
1859
+ rerank: resolveRerankLane({
1860
+ toml,
1861
+ yaml,
1862
+ memoryLlmApiKey,
1863
+ memoryLlmBaseUrl
1864
+ }),
1753
1865
  git: {
1754
1866
  autoHook: firstBool(toml.git?.auto_hook, yaml.git?.autoHook),
1755
1867
  ingestOnCommit: firstBool(toml.git?.ingest_on_commit, yaml.git?.ingestOnCommit),
@@ -1819,6 +1931,9 @@ function getResolvedMemoryLane(options = {}) {
1819
1931
  function getResolvedEmbeddingLane(options = {}) {
1820
1932
  return getResolvedConfig(options).embedding;
1821
1933
  }
1934
+ function getResolvedRerankLane(options = {}) {
1935
+ return getResolvedConfig(options).rerank;
1936
+ }
1822
1937
  function resetResolvedConfigCache() {
1823
1938
  }
1824
1939
  function first(...values) {
@@ -1861,6 +1976,10 @@ function getEnvSourceNames() {
1861
1976
  "MEMORIX_EMBEDDING_BASE_URL",
1862
1977
  "MEMORIX_EMBEDDING_MODEL",
1863
1978
  "MEMORIX_EMBEDDING_DIMENSIONS",
1979
+ "MEMORIX_RERANK_PROVIDER",
1980
+ "MEMORIX_RERANK_MODEL",
1981
+ "MEMORIX_RERANK_BASE_URL",
1982
+ "MEMORIX_RERANK_API_KEY",
1864
1983
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
1865
1984
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
1866
1985
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
@@ -1884,6 +2003,63 @@ function normalizeExternalContext(value) {
1884
2003
  if (normalized === "auto" || normalized === "off") return normalized;
1885
2004
  return void 0;
1886
2005
  }
2006
+ function normalizeRerankProvider(value) {
2007
+ const normalized = value?.trim().toLowerCase();
2008
+ if (normalized === "http") return "http";
2009
+ return "off";
2010
+ }
2011
+ function resolveRerankLane(args) {
2012
+ const provider2 = normalizeRerankProvider(first(
2013
+ process.env.MEMORIX_RERANK_PROVIDER,
2014
+ args.toml.rerank?.provider,
2015
+ args.yaml.rerank?.provider,
2016
+ "off"
2017
+ ));
2018
+ const model = first(
2019
+ process.env.MEMORIX_RERANK_MODEL,
2020
+ args.toml.rerank?.model,
2021
+ args.yaml.rerank?.model
2022
+ );
2023
+ const explicitBaseUrl = first(
2024
+ process.env.MEMORIX_RERANK_BASE_URL,
2025
+ args.toml.rerank?.base_url,
2026
+ args.yaml.rerank?.baseUrl
2027
+ );
2028
+ const baseUrl = provider2 === "http" ? first(explicitBaseUrl, args.memoryLlmBaseUrl) : explicitBaseUrl;
2029
+ const dedicatedApiKey = first(
2030
+ process.env.MEMORIX_RERANK_API_KEY,
2031
+ args.toml.rerank?.api_key,
2032
+ args.yaml.rerank?.apiKey
2033
+ );
2034
+ const sameEndpoint = provider2 === "http" && sameTrustedEndpoint(baseUrl, args.memoryLlmBaseUrl);
2035
+ const inheritedApiKey = sameEndpoint ? args.memoryLlmApiKey : void 0;
2036
+ const apiKey = first(dedicatedApiKey, inheritedApiKey);
2037
+ const canSendRequest = provider2 === "http" && Boolean(baseUrl) && (Boolean(apiKey) || !args.memoryLlmApiKey || sameEndpoint);
2038
+ return { provider: provider2, model, baseUrl, apiKey, canSendRequest };
2039
+ }
2040
+ function sameTrustedEndpoint(left, right) {
2041
+ const a = normalizeTrustedEndpoint(left);
2042
+ const b = normalizeTrustedEndpoint(right);
2043
+ return Boolean(a && b && a === b);
2044
+ }
2045
+ function normalizeTrustedEndpoint(value) {
2046
+ const trimmed = value?.trim();
2047
+ if (!trimmed) return void 0;
2048
+ try {
2049
+ const url = new URL(trimmed);
2050
+ const protocol = url.protocol.toLowerCase();
2051
+ const hostname = url.hostname.toLowerCase();
2052
+ const port = url.port;
2053
+ let pathname = url.pathname.replace(/\/+$/, "");
2054
+ if (pathname.toLowerCase().endsWith("/rerank")) {
2055
+ pathname = pathname.slice(0, -"/rerank".length).replace(/\/+$/, "");
2056
+ }
2057
+ return `${protocol}//${hostname}${port ? `:${port}` : ""}${pathname}`;
2058
+ } catch {
2059
+ const fallback = trimmed.replace(/\/+$/, "").replace(/\/rerank$/i, "").replace(/\/+$/, "");
2060
+ return fallback.toLowerCase() || void 0;
2061
+ }
2062
+ }
1887
2063
  var init_resolved_config = __esm({
1888
2064
  "src/config/resolved-config.ts"() {
1889
2065
  "use strict";
@@ -2020,9 +2196,27 @@ __export(provider_exports, {
2020
2196
  getLLMConfig: () => getLLMConfig,
2021
2197
  initLLM: () => initLLM,
2022
2198
  isLLMEnabled: () => isLLMEnabled,
2199
+ normalizeOpenAICompatibleBaseUrl: () => normalizeOpenAICompatibleBaseUrl,
2023
2200
  parseLLMTimeoutMs: () => parseLLMTimeoutMs,
2024
2201
  setLLMConfig: () => setLLMConfig
2025
2202
  });
2203
+ function normalizeOpenAICompatibleBaseUrl(baseUrl) {
2204
+ const base = baseUrl.trim().replace(/\/+$/, "");
2205
+ if (!base) return base;
2206
+ let pathname = "";
2207
+ try {
2208
+ pathname = new URL(base).pathname.replace(/\/+$/, "");
2209
+ } catch {
2210
+ pathname = base.split(/[?#]/, 1)[0].replace(/\/+$/, "");
2211
+ }
2212
+ const lastSegment = pathname.split("/").filter(Boolean).at(-1);
2213
+ if (lastSegment && OPENAI_API_VERSION_SEGMENT.test(lastSegment)) {
2214
+ return base;
2215
+ }
2216
+ const queryOrHash = base.match(/[?#].*$/)?.[0] ?? "";
2217
+ const pathWithoutQuery = base.slice(0, base.length - queryOrHash.length).replace(/\/+$/, "");
2218
+ return `${pathWithoutQuery}/v1${queryOrHash}`;
2219
+ }
2026
2220
  function parseLLMTimeoutMs(raw) {
2027
2221
  if (raw === void 0 || raw.trim() === "") return LLM_TIMEOUT_DEFAULT_MS;
2028
2222
  const parsed = Number(raw);
@@ -2126,8 +2320,7 @@ async function callLLM(systemPrompt, userMessage, signal) {
2126
2320
  }
2127
2321
  async function callOpenAICompatible(systemPrompt, userMessage, signal) {
2128
2322
  const config = currentConfig;
2129
- let base = config.baseUrl.replace(/\/+$/, "");
2130
- if (!base.endsWith("/v1")) base += "/v1";
2323
+ const base = normalizeOpenAICompatibleBaseUrl(config.baseUrl);
2131
2324
  const url = `${base}/chat/completions`;
2132
2325
  const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
2133
2326
  const response = await fetch(url, {
@@ -2206,8 +2399,7 @@ async function callLLMWithTools(messages, tools, signal) {
2206
2399
  }
2207
2400
  async function callOpenAIWithTools(messages, tools, signal) {
2208
2401
  const config = currentConfig;
2209
- let base = config.baseUrl.replace(/\/+$/, "");
2210
- if (!base.endsWith("/v1")) base += "/v1";
2402
+ const base = normalizeOpenAICompatibleBaseUrl(config.baseUrl);
2211
2403
  const url = `${base}/chat/completions`;
2212
2404
  const openaiMessages = messages.map((msg) => {
2213
2405
  if (msg.role === "tool") {
@@ -2357,8 +2549,7 @@ async function callAnthropicWithTools(messages, tools, signal) {
2357
2549
  }
2358
2550
  async function* callOpenAIWithToolsStream(messages, tools) {
2359
2551
  const config = currentConfig;
2360
- let base = config.baseUrl.replace(/\/+$/, "");
2361
- if (!base.endsWith("/v1")) base += "/v1";
2552
+ const base = normalizeOpenAICompatibleBaseUrl(config.baseUrl);
2362
2553
  const url = `${base}/chat/completions`;
2363
2554
  const openaiMessages = messages.map((msg) => {
2364
2555
  if (msg.role === "tool") {
@@ -2606,7 +2797,7 @@ async function* callAnthropicWithToolsStream(messages, tools) {
2606
2797
  }
2607
2798
  };
2608
2799
  }
2609
- var LLM_TIMEOUT_DEFAULT_MS, LLM_TIMEOUT_MIN_MS, LLM_TIMEOUT_MAX_MS, MAX_LLM_RESPONSE_BYTES, LLM_CALL_TIMEOUT_MS, PROVIDER_DEFAULTS, currentConfig;
2800
+ var LLM_TIMEOUT_DEFAULT_MS, LLM_TIMEOUT_MIN_MS, LLM_TIMEOUT_MAX_MS, MAX_LLM_RESPONSE_BYTES, OPENAI_API_VERSION_SEGMENT, LLM_CALL_TIMEOUT_MS, PROVIDER_DEFAULTS, currentConfig;
2610
2801
  var init_provider = __esm({
2611
2802
  "src/llm/provider.ts"() {
2612
2803
  "use strict";
@@ -2616,6 +2807,7 @@ var init_provider = __esm({
2616
2807
  LLM_TIMEOUT_MIN_MS = 1e3;
2617
2808
  LLM_TIMEOUT_MAX_MS = 3e5;
2618
2809
  MAX_LLM_RESPONSE_BYTES = 2 * 1024 * 1024;
2810
+ OPENAI_API_VERSION_SEGMENT = /^v\d+(?:[a-z][a-z0-9-]*)?$/i;
2619
2811
  LLM_CALL_TIMEOUT_MS = parseLLMTimeoutMs(process.env.MEMORIX_LLM_TIMEOUT_MS);
2620
2812
  PROVIDER_DEFAULTS = {
2621
2813
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
@@ -5544,6 +5736,48 @@ Rules:
5544
5736
  }
5545
5737
  });
5546
5738
 
5739
+ // src/rerank/policy.ts
5740
+ function parseRerankTimeoutMs(raw) {
5741
+ const value = raw?.trim();
5742
+ if (!value) return DEFAULT_RERANK_TIMEOUT_MS;
5743
+ const parsed = Number(value);
5744
+ if (!Number.isInteger(parsed) || Number.isNaN(parsed)) {
5745
+ console.warn(
5746
+ `[memorix] MEMORIX_RERANK_TIMEOUT_MS="${raw}" is invalid (must be a positive integer between ${RERANK_TIMEOUT_MIN_MS}-${RERANK_TIMEOUT_MAX_MS}ms). Using default ${DEFAULT_RERANK_TIMEOUT_MS}ms.`
5747
+ );
5748
+ return DEFAULT_RERANK_TIMEOUT_MS;
5749
+ }
5750
+ if (parsed < RERANK_TIMEOUT_MIN_MS) return RERANK_TIMEOUT_MIN_MS;
5751
+ if (parsed > RERANK_TIMEOUT_MAX_MS) return RERANK_TIMEOUT_MAX_MS;
5752
+ return parsed;
5753
+ }
5754
+ function resolveRerankTimeoutMs(explicitTimeoutMs) {
5755
+ if (typeof explicitTimeoutMs === "number" && Number.isFinite(explicitTimeoutMs) && explicitTimeoutMs > 0) {
5756
+ return explicitTimeoutMs;
5757
+ }
5758
+ return parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
5759
+ }
5760
+ function shouldAttemptHttpRerank(input) {
5761
+ return input.quality === "thorough" && input.hasQuery && input.candidateCount >= MIN_RERANK_CANDIDATES;
5762
+ }
5763
+ function shouldAttemptLlmRerank(input) {
5764
+ if (input.httpRerankConfigured) return false;
5765
+ if (input.quality !== "thorough" || input.tier !== "heavy" || !input.hasQuery) return false;
5766
+ if (input.candidateCount < MIN_RERANK_CANDIDATES) return false;
5767
+ return input.topScore > 0 && input.secondScore / input.topScore > 0.7;
5768
+ }
5769
+ var DEFAULT_RERANK_TIMEOUT_MS, RERANK_TIMEOUT_MIN_MS, RERANK_TIMEOUT_MAX_MS, MIN_RERANK_CANDIDATES;
5770
+ var init_policy = __esm({
5771
+ "src/rerank/policy.ts"() {
5772
+ "use strict";
5773
+ init_esm_shims();
5774
+ DEFAULT_RERANK_TIMEOUT_MS = 3e4;
5775
+ RERANK_TIMEOUT_MIN_MS = 1e3;
5776
+ RERANK_TIMEOUT_MAX_MS = 3e5;
5777
+ MIN_RERANK_CANDIDATES = 3;
5778
+ }
5779
+ });
5780
+
5547
5781
  // src/timeout.ts
5548
5782
  function withTimeout(promise, ms, label) {
5549
5783
  return new Promise((resolve, reject) => {
@@ -5829,6 +6063,148 @@ var init_aliases = __esm({
5829
6063
  }
5830
6064
  });
5831
6065
 
6066
+ // src/rerank/http-provider.ts
6067
+ function buildRerankUrl(baseUrl) {
6068
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
6069
+ if (trimmed.endsWith("/rerank")) return trimmed;
6070
+ return `${trimmed}/rerank`;
6071
+ }
6072
+ function parseRerankResponse(payload) {
6073
+ const rows = extractRows(payload);
6074
+ if (rows.length === 0) {
6075
+ throw new Error("Neural rerank response contained no ranked results");
6076
+ }
6077
+ return rows.sort((a, b) => b.score - a.score);
6078
+ }
6079
+ function extractRows(payload) {
6080
+ if (Array.isArray(payload)) {
6081
+ return payload.map(rowFromUnknown).filter((row) => row !== null);
6082
+ }
6083
+ if (payload && typeof payload === "object" && "results" in payload) {
6084
+ const results = payload.results;
6085
+ if (Array.isArray(results)) {
6086
+ return results.map(rowFromUnknown).filter((row) => row !== null);
6087
+ }
6088
+ }
6089
+ throw new Error("Neural rerank response was not Cohere or TEI shaped");
6090
+ }
6091
+ function rowFromUnknown(value) {
6092
+ if (!value || typeof value !== "object") return null;
6093
+ const row = value;
6094
+ if (typeof row.index !== "number" || !Number.isFinite(row.index)) return null;
6095
+ const score = typeof row.relevance_score === "number" ? row.relevance_score : typeof row.score === "number" ? row.score : null;
6096
+ if (score === null || !Number.isFinite(score)) return null;
6097
+ return { index: row.index, score };
6098
+ }
6099
+ function buildRerankBody(query, documents, model) {
6100
+ const body = {
6101
+ query,
6102
+ documents,
6103
+ top_n: documents.length,
6104
+ return_documents: false
6105
+ };
6106
+ const trimmed = model?.trim();
6107
+ if (trimmed) body.model = trimmed;
6108
+ return body;
6109
+ }
6110
+ async function rerankViaHttp(query, documents, config) {
6111
+ if (documents.length === 0) return [];
6112
+ const url = buildRerankUrl(config.baseUrl);
6113
+ const headers = {
6114
+ "content-type": "application/json"
6115
+ };
6116
+ if (config.apiKey) {
6117
+ headers.authorization = `Bearer ${config.apiKey}`;
6118
+ }
6119
+ const controller = new AbortController();
6120
+ const timeoutMs = resolveRerankTimeoutMs(config.timeoutMs);
6121
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
6122
+ try {
6123
+ const fetchImpl = config.fetchImpl ?? fetch;
6124
+ const response = await fetchImpl(url, {
6125
+ method: "POST",
6126
+ headers,
6127
+ signal: controller.signal,
6128
+ body: JSON.stringify(buildRerankBody(query, documents, config.model))
6129
+ });
6130
+ if (!response.ok) {
6131
+ throw new Error(`Neural rerank HTTP ${response.status} from ${url}`);
6132
+ }
6133
+ return parseRerankResponse(await response.json());
6134
+ } finally {
6135
+ clearTimeout(timer);
6136
+ }
6137
+ }
6138
+ var init_http_provider = __esm({
6139
+ "src/rerank/http-provider.ts"() {
6140
+ "use strict";
6141
+ init_esm_shims();
6142
+ init_policy();
6143
+ }
6144
+ });
6145
+
6146
+ // src/rerank/index.ts
6147
+ var rerank_exports = {};
6148
+ __export(rerank_exports, {
6149
+ DEFAULT_RERANK_TIMEOUT_MS: () => DEFAULT_RERANK_TIMEOUT_MS,
6150
+ MIN_RERANK_CANDIDATES: () => MIN_RERANK_CANDIDATES,
6151
+ isNeuralRerankEnabled: () => isNeuralRerankEnabled,
6152
+ neuralRerankCandidates: () => neuralRerankCandidates,
6153
+ parseRerankTimeoutMs: () => parseRerankTimeoutMs,
6154
+ resolveRerankTimeoutMs: () => resolveRerankTimeoutMs,
6155
+ shouldAttemptHttpRerank: () => shouldAttemptHttpRerank,
6156
+ shouldAttemptLlmRerank: () => shouldAttemptLlmRerank
6157
+ });
6158
+ function isNeuralRerankEnabled(options = {}) {
6159
+ const lane = getResolvedRerankLane(options);
6160
+ return lane.provider === "http" && Boolean(lane.baseUrl);
6161
+ }
6162
+ async function neuralRerankCandidates(query, candidates, options = {}) {
6163
+ if (!isNeuralRerankEnabled(options) || candidates.length === 0) return null;
6164
+ const lane = getResolvedRerankLane(options);
6165
+ if (!lane.canSendRequest || !lane.baseUrl) return null;
6166
+ const toRerank = candidates.slice(0, MAX_RERANK);
6167
+ const rest = candidates.slice(MAX_RERANK);
6168
+ const documents = toRerank.map((candidate) => candidateText(candidate));
6169
+ const ranked = await rerankViaHttp(query, documents, {
6170
+ provider: "http",
6171
+ model: lane.model,
6172
+ baseUrl: lane.baseUrl,
6173
+ apiKey: lane.apiKey,
6174
+ timeoutMs: resolveRerankTimeoutMs()
6175
+ });
6176
+ const seen = /* @__PURE__ */ new Set();
6177
+ const reranked = [];
6178
+ for (const row of ranked) {
6179
+ const candidate = toRerank[row.index];
6180
+ if (!candidate || seen.has(row.index)) continue;
6181
+ reranked.push(candidate);
6182
+ seen.add(row.index);
6183
+ }
6184
+ for (const [index, candidate] of toRerank.entries()) {
6185
+ if (!seen.has(index)) reranked.push(candidate);
6186
+ }
6187
+ reranked.push(...rest);
6188
+ return reranked;
6189
+ }
6190
+ function candidateText(candidate) {
6191
+ const narrative = candidate.narrative?.trim();
6192
+ if (narrative) return `${candidate.title} \u2014 ${narrative.slice(0, 200)}`;
6193
+ return candidate.title;
6194
+ }
6195
+ var MAX_RERANK;
6196
+ var init_rerank = __esm({
6197
+ "src/rerank/index.ts"() {
6198
+ "use strict";
6199
+ init_esm_shims();
6200
+ init_resolved_config();
6201
+ init_http_provider();
6202
+ init_policy();
6203
+ init_policy();
6204
+ MAX_RERANK = 10;
6205
+ }
6206
+ });
6207
+
5832
6208
  // src/llm/quality.ts
5833
6209
  var quality_exports = {};
5834
6210
  __export(quality_exports, {
@@ -5868,9 +6244,9 @@ async function rerankResults(query, candidates) {
5868
6244
  if (!isLLMEnabled() || candidates.length <= 2) {
5869
6245
  return { reranked: candidates, usedLLM: false };
5870
6246
  }
5871
- const MAX_RERANK = 10;
5872
- const toRerank = candidates.slice(0, MAX_RERANK);
5873
- const rest = candidates.slice(MAX_RERANK);
6247
+ const MAX_RERANK2 = 10;
6248
+ const toRerank = candidates.slice(0, MAX_RERANK2);
6249
+ const rest = candidates.slice(MAX_RERANK2);
5874
6250
  try {
5875
6251
  const candidateList = toRerank.map(
5876
6252
  (c) => `[ID: ${c.id}] (${c.type}) ${c.title}${c.narrative ? ` \u2014 ${c.narrative.substring(0, 100)}` : ""}`
@@ -6763,46 +7139,78 @@ async function searchObservations(options) {
6763
7139
  }
6764
7140
  }
6765
7141
  }
6766
- const shouldRerank = quality === "thorough" && tier === "heavy" && hasQuery && intermediate.length > 2 && (() => {
6767
- const top = intermediate[0]?.score ?? 0;
6768
- const second = intermediate[1]?.score ?? 0;
6769
- return top > 0 && second / top > 0.7;
6770
- })();
6771
- if (shouldRerank) {
6772
- try {
6773
- const { rerankResults: rerankResults2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
6774
- const narrativeMap = /* @__PURE__ */ new Map();
6775
- for (const hit of results.hits) {
6776
- const doc = hit.document;
6777
- narrativeMap.set(makeEntryKey(doc.projectId, doc.observationId), doc.narrative);
6778
- }
6779
- const RERANK_TOP_K = 5;
6780
- const toRerank = intermediate.slice(0, RERANK_TOP_K);
6781
- const candidates = toRerank.map((e2, index) => ({
6782
- id: `r${index + 1}`,
6783
- title: e2.title,
6784
- type: e2.type,
6785
- score: e2.score,
6786
- narrative: narrativeMap.get(makeEntryKey(e2.projectId, e2.id))
6787
- }));
6788
- const _parsedRerank = parseInt(process.env.MEMORIX_RERANK_TIMEOUT_MS || "", 10);
6789
- const RERANK_TIMEOUT_MS = Number.isFinite(_parsedRerank) && _parsedRerank > 0 ? _parsedRerank : 5e3;
6790
- const { reranked, usedLLM } = await withTimeout(
6791
- rerankResults2(originalQuery, candidates),
6792
- RERANK_TIMEOUT_MS,
6793
- "LLM rerank"
6794
- );
6795
- mark(`rerank(usedLLM=${usedLLM})`);
6796
- if (usedLLM) {
6797
- lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
6798
- const candidateMap = new Map(candidates.map((candidate, index) => [candidate.id, toRerank[index]]));
6799
- const rerankedTop = reranked.map((r2) => candidateMap.get(r2.id)).filter((e2) => e2 != null);
6800
- if (rerankedTop.length > 0) {
6801
- intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
7142
+ const httpGate = shouldAttemptHttpRerank({
7143
+ quality,
7144
+ hasQuery: Boolean(hasQuery),
7145
+ candidateCount: intermediate.length
7146
+ });
7147
+ const llmGateWithoutHttp = shouldAttemptLlmRerank({
7148
+ quality,
7149
+ tier,
7150
+ hasQuery: Boolean(hasQuery),
7151
+ candidateCount: intermediate.length,
7152
+ topScore: intermediate[0]?.score ?? 0,
7153
+ secondScore: intermediate[1]?.score ?? 0,
7154
+ httpRerankConfigured: false
7155
+ });
7156
+ if (httpGate || llmGateWithoutHttp) {
7157
+ const narrativeMap = /* @__PURE__ */ new Map();
7158
+ for (const hit of results.hits) {
7159
+ const doc = hit.document;
7160
+ narrativeMap.set(makeEntryKey(doc.projectId, doc.observationId), doc.narrative);
7161
+ }
7162
+ const RERANK_TOP_K = 5;
7163
+ const toRerank = intermediate.slice(0, RERANK_TOP_K);
7164
+ const candidates = toRerank.map((e2, index) => ({
7165
+ id: `r${index + 1}`,
7166
+ title: e2.title,
7167
+ type: e2.type,
7168
+ score: e2.score,
7169
+ narrative: narrativeMap.get(makeEntryKey(e2.projectId, e2.id))
7170
+ }));
7171
+ const RERANK_TIMEOUT_MS = parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
7172
+ const candidateMap = new Map(candidates.map((candidate, index) => [candidate.id, toRerank[index]]));
7173
+ const { isNeuralRerankEnabled: isNeuralRerankEnabled2, neuralRerankCandidates: neuralRerankCandidates2 } = await Promise.resolve().then(() => (init_rerank(), rerank_exports));
7174
+ const httpConfigured = isNeuralRerankEnabled2();
7175
+ if (httpConfigured && httpGate) {
7176
+ try {
7177
+ const neural = await withTimeout(
7178
+ neuralRerankCandidates2(originalQuery, candidates),
7179
+ RERANK_TIMEOUT_MS,
7180
+ "neural rerank"
7181
+ );
7182
+ if (neural && neural.length > 0) {
7183
+ const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
7184
+ if (rerankedTop.length > 0) {
7185
+ intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
7186
+ lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
7187
+ mark("rerank(usedNeural=true)");
7188
+ }
6802
7189
  }
7190
+ } catch {
7191
+ console.error("[memorix] neural rerank failed or timed out; keeping original order");
6803
7192
  }
6804
- } catch (error) {
6805
- console.error("[memorix] LLM rerank failed or timed out, using original order");
7193
+ } else if (!httpConfigured && llmGateWithoutHttp) {
7194
+ try {
7195
+ const { rerankResults: rerankResults2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
7196
+ const { reranked, usedLLM } = await withTimeout(
7197
+ rerankResults2(originalQuery, candidates),
7198
+ RERANK_TIMEOUT_MS,
7199
+ "LLM rerank"
7200
+ );
7201
+ mark(`rerank(usedLLM=${usedLLM})`);
7202
+ if (usedLLM) {
7203
+ lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
7204
+ const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
7205
+ if (rerankedTop.length > 0) {
7206
+ intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
7207
+ }
7208
+ }
7209
+ } catch {
7210
+ console.error("[memorix] LLM rerank failed or timed out, using original order");
7211
+ }
7212
+ } else {
7213
+ mark(`rerank(skipped,tier=${tier})`);
6806
7214
  }
6807
7215
  } else {
6808
7216
  mark(`rerank(skipped,tier=${tier})`);
@@ -6983,6 +7391,7 @@ var init_orama_store = __esm({
6983
7391
  init_project_affinity();
6984
7392
  init_intent_detector();
6985
7393
  init_query_expansion();
7394
+ init_policy();
6986
7395
  init_timeout();
6987
7396
  db = null;
6988
7397
  dbInitPromise = null;
@@ -7674,6 +8083,226 @@ var init_lifecycle = __esm({
7674
8083
  }
7675
8084
  });
7676
8085
 
8086
+ // src/store/evidence-store.ts
8087
+ import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
8088
+ function safeJson(value, fallback) {
8089
+ if (typeof value !== "string" || value.length === 0) return fallback;
8090
+ try {
8091
+ return JSON.parse(value);
8092
+ } catch {
8093
+ return fallback;
8094
+ }
8095
+ }
8096
+ function rowToCard(row) {
8097
+ return {
8098
+ id: String(row.id),
8099
+ projectId: String(row.project_id),
8100
+ candidateKind: row.candidate_kind,
8101
+ candidateId: String(row.candidate_id),
8102
+ title: String(row.title),
8103
+ summary: String(row.summary ?? ""),
8104
+ ...row.entity_name ? { entityName: String(row.entity_name) } : {},
8105
+ sourceKind: String(row.source_kind),
8106
+ sourceRef: String(row.source_ref),
8107
+ ...row.locator ? { locator: String(row.locator) } : {},
8108
+ ...row.captured_hash ? { capturedHash: String(row.captured_hash) } : {},
8109
+ ...row.session_id ? { sessionId: String(row.session_id) } : {},
8110
+ status: String(row.status ?? "active"),
8111
+ verification: row.verification,
8112
+ freshness: row.freshness,
8113
+ ...row.stale_reason ? { staleReason: String(row.stale_reason) } : {},
8114
+ files: safeJson(row.files_json, []),
8115
+ relatedEntities: safeJson(row.related_entities_json, []),
8116
+ createdAt: String(row.created_at),
8117
+ updatedAt: String(row.updated_at)
8118
+ };
8119
+ }
8120
+ function observationHash(observation) {
8121
+ return createHash3("sha256").update(JSON.stringify({
8122
+ projectId: observation.projectId,
8123
+ id: observation.id,
8124
+ title: observation.title,
8125
+ narrative: observation.narrative,
8126
+ facts: observation.facts,
8127
+ files: observation.filesModified,
8128
+ relatedEntities: observation.relatedEntities
8129
+ })).digest("hex");
8130
+ }
8131
+ function sourceKind(observation) {
8132
+ if (observation.source === "git" || observation.sourceDetail === "git-ingest") return "git";
8133
+ if (observation.sourceDetail === "hook") return "hook";
8134
+ return observation.source ?? "agent";
8135
+ }
8136
+ var EvidenceCardStore;
8137
+ var init_evidence_store = __esm({
8138
+ "src/store/evidence-store.ts"() {
8139
+ "use strict";
8140
+ init_esm_shims();
8141
+ init_sqlite_db();
8142
+ EvidenceCardStore = class {
8143
+ db = null;
8144
+ async init(dataDir) {
8145
+ this.db = getDatabase(dataDir);
8146
+ }
8147
+ requireDb() {
8148
+ if (!this.db) throw new Error("EvidenceCardStore is not initialized.");
8149
+ return this.db;
8150
+ }
8151
+ upsertObservation(observation) {
8152
+ const db2 = this.requireDb();
8153
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
8154
+ const card = {
8155
+ id: `evidence:observation:${observation.projectId}:${observation.id}`,
8156
+ projectId: observation.projectId,
8157
+ candidateKind: "observation",
8158
+ candidateId: String(observation.id),
8159
+ title: observation.title,
8160
+ summary: observation.narrative.slice(0, 500),
8161
+ entityName: observation.entityName,
8162
+ sourceKind: sourceKind(observation),
8163
+ sourceRef: `observation:${observation.id}`,
8164
+ locator: observation.filesModified?.join(", ") || `observation:${observation.id}`,
8165
+ capturedHash: observationHash(observation),
8166
+ ...observation.sessionId ? { sessionId: observation.sessionId } : {},
8167
+ status: observation.status ?? "active",
8168
+ verification: observation.status === "resolved" ? "verified" : "unverified",
8169
+ freshness: "current",
8170
+ files: observation.filesModified ?? [],
8171
+ relatedEntities: observation.relatedEntities ?? [],
8172
+ createdAt: observation.createdAt,
8173
+ updatedAt: now3
8174
+ };
8175
+ db2.prepare(`
8176
+ INSERT INTO evidence_cards (
8177
+ id, project_id, candidate_kind, candidate_id, title, summary, entity_name,
8178
+ source_kind, source_ref, locator, captured_hash, session_id, status,
8179
+ verification, freshness, stale_reason, files_json, related_entities_json,
8180
+ created_at, updated_at
8181
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?)
8182
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
8183
+ title = excluded.title,
8184
+ summary = excluded.summary,
8185
+ entity_name = excluded.entity_name,
8186
+ source_kind = excluded.source_kind,
8187
+ source_ref = excluded.source_ref,
8188
+ locator = excluded.locator,
8189
+ captured_hash = excluded.captured_hash,
8190
+ session_id = excluded.session_id,
8191
+ status = excluded.status,
8192
+ freshness = 'current',
8193
+ stale_reason = NULL,
8194
+ files_json = excluded.files_json,
8195
+ related_entities_json = excluded.related_entities_json,
8196
+ updated_at = excluded.updated_at
8197
+ `).run(
8198
+ card.id,
8199
+ card.projectId,
8200
+ card.candidateKind,
8201
+ card.candidateId,
8202
+ card.title,
8203
+ card.summary,
8204
+ card.entityName ?? null,
8205
+ card.sourceKind,
8206
+ card.sourceRef,
8207
+ card.locator ?? null,
8208
+ card.capturedHash ?? null,
8209
+ card.sessionId ?? null,
8210
+ card.status,
8211
+ card.verification,
8212
+ card.freshness,
8213
+ JSON.stringify(card.files),
8214
+ JSON.stringify(card.relatedEntities),
8215
+ card.createdAt,
8216
+ card.updatedAt
8217
+ );
8218
+ return this.get(card.projectId, card.candidateKind, card.candidateId) ?? card;
8219
+ }
8220
+ get(projectId, candidateKind, candidateId) {
8221
+ const row = this.requireDb().prepare(`
8222
+ SELECT * FROM evidence_cards WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
8223
+ `).get(projectId, candidateKind, candidateId);
8224
+ return row ? rowToCard(row) : void 0;
8225
+ }
8226
+ list(projectId, options = {}) {
8227
+ const limit2 = Math.min(100, Math.max(1, Math.floor(options.limit ?? 20)));
8228
+ const clauses = ["project_id = ?"];
8229
+ const args = [projectId];
8230
+ if (options.freshness) {
8231
+ clauses.push("freshness = ?");
8232
+ args.push(options.freshness);
8233
+ }
8234
+ if (options.status) {
8235
+ clauses.push("status = ?");
8236
+ args.push(options.status);
8237
+ }
8238
+ args.push(limit2);
8239
+ const rows = this.requireDb().prepare(`
8240
+ SELECT * FROM evidence_cards WHERE ${clauses.join(" AND ")} ORDER BY updated_at DESC, id DESC LIMIT ?
8241
+ `).all(...args);
8242
+ return rows.map(rowToCard);
8243
+ }
8244
+ syncObservations(observations2) {
8245
+ return observations2.map((observation) => this.upsertObservation(observation));
8246
+ }
8247
+ markStaleForPaths(projectId, paths, reason = "source file changed") {
8248
+ const normalized = new Set(paths.map((value) => value.replace(/\\/g, "/").toLowerCase()));
8249
+ if (normalized.size === 0) return 0;
8250
+ const db2 = this.requireDb();
8251
+ const rows = db2.prepare(`SELECT * FROM evidence_cards WHERE project_id = ? AND freshness != 'stale'`).all(projectId);
8252
+ const update2 = db2.prepare(`
8253
+ UPDATE evidence_cards SET freshness = 'stale', stale_reason = ?, updated_at = ?
8254
+ WHERE id = ?
8255
+ `);
8256
+ let changed = 0;
8257
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
8258
+ const run = db2.transaction(() => {
8259
+ for (const row of rows) {
8260
+ const files = safeJson(row.files_json, []);
8261
+ if (!files.some((file) => normalized.has(file.replace(/\\/g, "/").toLowerCase()))) continue;
8262
+ update2.run(reason, now3, row.id);
8263
+ changed++;
8264
+ }
8265
+ });
8266
+ run();
8267
+ return changed;
8268
+ }
8269
+ setVerification(projectId, cardId, verification) {
8270
+ this.requireDb().prepare(`
8271
+ UPDATE evidence_cards SET verification = ?, updated_at = ? WHERE project_id = ? AND id = ?
8272
+ `).run(verification, (/* @__PURE__ */ new Date()).toISOString(), projectId, cardId);
8273
+ }
8274
+ recordEvent(projectId, cardId, kind, detail) {
8275
+ const event = {
8276
+ id: randomUUID3(),
8277
+ projectId,
8278
+ cardId,
8279
+ kind,
8280
+ ...detail ? { detail: detail.slice(0, 2e3) } : {},
8281
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
8282
+ };
8283
+ this.requireDb().prepare(`
8284
+ INSERT INTO evidence_card_events (id, project_id, card_id, kind, detail, created_at)
8285
+ VALUES (?, ?, ?, ?, ?, ?)
8286
+ `).run(event.id, event.projectId, event.cardId, event.kind, event.detail ?? null, event.createdAt);
8287
+ return event;
8288
+ }
8289
+ listEvents(cardId, limit2 = 50) {
8290
+ const rows = this.requireDb().prepare(`
8291
+ SELECT * FROM evidence_card_events WHERE card_id = ? ORDER BY created_at DESC LIMIT ?
8292
+ `).all(cardId, Math.min(200, Math.max(1, Math.floor(limit2))));
8293
+ return rows.map((row) => ({
8294
+ id: String(row.id),
8295
+ projectId: String(row.project_id),
8296
+ cardId: String(row.card_id),
8297
+ kind: String(row.kind),
8298
+ ...row.detail ? { detail: String(row.detail) } : {},
8299
+ createdAt: String(row.created_at)
8300
+ }));
8301
+ }
8302
+ };
8303
+ }
8304
+ });
8305
+
7677
8306
  // src/runtime/isolated-maintenance.ts
7678
8307
  import { spawn } from "child_process";
7679
8308
  import { existsSync as existsSync8 } from "fs";
@@ -7705,7 +8334,7 @@ var init_isolated_maintenance = __esm({
7705
8334
  });
7706
8335
 
7707
8336
  // src/knowledge/workspace-store.ts
7708
- import { randomUUID as randomUUID3 } from "crypto";
8337
+ import { randomUUID as randomUUID4 } from "crypto";
7709
8338
  function arrayValue(raw) {
7710
8339
  if (typeof raw !== "string" || !raw) return [];
7711
8340
  try {
@@ -7846,7 +8475,7 @@ var init_workspace_store = __esm({
7846
8475
  }
7847
8476
  createProposal(input) {
7848
8477
  const proposal = {
7849
- id: input.id ?? randomUUID3(),
8478
+ id: input.id ?? randomUUID4(),
7850
8479
  ...input,
7851
8480
  status: input.status ?? "pending",
7852
8481
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -7899,13 +8528,13 @@ __export(workspace_exports, {
7899
8528
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
7900
8529
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
7901
8530
  });
7902
- import { createHash as createHash3 } from "crypto";
8531
+ import { createHash as createHash4 } from "crypto";
7903
8532
  import { execFile } from "child_process";
7904
8533
  import { promises as fs6 } from "fs";
7905
8534
  import path10 from "path";
7906
8535
  import { promisify } from "util";
7907
8536
  function hash(value) {
7908
- return createHash3("sha256").update(value).digest("hex");
8537
+ return createHash4("sha256").update(value).digest("hex");
7909
8538
  }
7910
8539
  function workspaceId(projectId, mode) {
7911
8540
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -8501,7 +9130,7 @@ Respond in JSON only:
8501
9130
  });
8502
9131
 
8503
9132
  // src/media/media-store.ts
8504
- import { randomUUID as randomUUID4 } from "crypto";
9133
+ import { randomUUID as randomUUID5 } from "crypto";
8505
9134
  function asOptionalText(value) {
8506
9135
  return typeof value === "string" && value.length > 0 ? value : void 0;
8507
9136
  }
@@ -8671,7 +9300,7 @@ var init_media_store = __esm({
8671
9300
  return { asset: this.getAsset(input.projectId, existing.id), deduplicated: true };
8672
9301
  }
8673
9302
  const asset = {
8674
- id: randomUUID4(),
9303
+ id: randomUUID5(),
8675
9304
  ...input,
8676
9305
  createdAt: now3,
8677
9306
  updatedAt: now3
@@ -8751,7 +9380,7 @@ var init_media_store = __esm({
8751
9380
  `).get(input.assetId, input.observationId ?? null, input.role);
8752
9381
  if (existing) return rowToLink(existing);
8753
9382
  const link = {
8754
- id: randomUUID4(),
9383
+ id: randomUUID5(),
8755
9384
  assetId: input.assetId,
8756
9385
  projectId: input.projectId,
8757
9386
  ...input.observationId !== void 0 ? { observationId: input.observationId } : {},
@@ -8790,7 +9419,7 @@ var init_media_store = __esm({
8790
9419
  return rowToDerivation(this.db.prepare("SELECT * FROM media_derivations WHERE id = ?").get(existing.id));
8791
9420
  }
8792
9421
  const derivation = {
8793
- id: randomUUID4(),
9422
+ id: randomUUID5(),
8794
9423
  ...input,
8795
9424
  content,
8796
9425
  ...error ? { error } : {},
@@ -8877,7 +9506,7 @@ var init_media_store = __esm({
8877
9506
  createJob(input) {
8878
9507
  const now3 = Date.now();
8879
9508
  const job = {
8880
- id: randomUUID4(),
9509
+ id: randomUUID5(),
8881
9510
  projectId: input.projectId,
8882
9511
  kind: input.kind,
8883
9512
  status: "queued",
@@ -9039,14 +9668,14 @@ var init_types2 = __esm({
9039
9668
  });
9040
9669
 
9041
9670
  // src/media/asset-store.ts
9042
- import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
9671
+ import { createHash as createHash5, randomUUID as randomUUID6 } from "crypto";
9043
9672
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
9044
9673
  import path11 from "path";
9045
9674
  function mediaRoot(dataDir) {
9046
9675
  return path11.resolve(dataDir, "media");
9047
9676
  }
9048
9677
  function projectStorageKey(projectId) {
9049
- return createHash4("sha256").update(projectId).digest("hex").slice(0, 16);
9678
+ return createHash5("sha256").update(projectId).digest("hex").slice(0, 16);
9050
9679
  }
9051
9680
  function validateUnderRoot(root, candidate) {
9052
9681
  const resolved = path11.resolve(candidate);
@@ -9110,7 +9739,7 @@ async function destinationExists(destination) {
9110
9739
  async function persistBuffer(bytes, destination) {
9111
9740
  await mkdir3(path11.dirname(destination), { recursive: true });
9112
9741
  if (await destinationExists(destination)) return false;
9113
- const temporary = `${destination}.${randomUUID5()}.tmp`;
9742
+ const temporary = `${destination}.${randomUUID6()}.tmp`;
9114
9743
  try {
9115
9744
  await writeFile3(temporary, bytes, { flag: "wx" });
9116
9745
  await rename2(temporary, destination);
@@ -9155,7 +9784,7 @@ async function importMediaBuffer(input) {
9155
9784
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
9156
9785
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
9157
9786
  const detected = detectMedia(input.bytes.subarray(0, 32));
9158
- const sha256 = createHash4("sha256").update(input.bytes).digest("hex");
9787
+ const sha256 = createHash5("sha256").update(input.bytes).digest("hex");
9159
9788
  return importKnownMedia({
9160
9789
  dataDir: input.dataDir,
9161
9790
  projectId: input.projectId,
@@ -9173,7 +9802,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
9173
9802
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
9174
9803
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
9175
9804
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
9176
- const sha256 = createHash4("sha256").update(bytes).digest("hex");
9805
+ const sha256 = createHash5("sha256").update(bytes).digest("hex");
9177
9806
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
9178
9807
  return bytes;
9179
9808
  }
@@ -61924,7 +62553,7 @@ var require_gaxios = __commonJS({
61924
62553
  var retry_js_1 = require_retry3();
61925
62554
  var stream_1 = __require("stream");
61926
62555
  var interceptor_js_1 = require_interceptor();
61927
- var randomUUID14 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
62556
+ var randomUUID16 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
61928
62557
  var HTTP_STATUS_NO_CONTENT = 204;
61929
62558
  var Gaxios = class {
61930
62559
  agentCache = /* @__PURE__ */ new Map();
@@ -62197,7 +62826,7 @@ var require_gaxios = __commonJS({
62197
62826
  */
62198
62827
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
62199
62828
  if (opts.multipart?.length) {
62200
- const boundary = await randomUUID14();
62829
+ const boundary = await randomUUID16();
62201
62830
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
62202
62831
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
62203
62832
  } else if (shouldDirectlyPassData) {
@@ -74424,7 +75053,7 @@ var require_websocket = __commonJS({
74424
75053
  var http3 = __require("http");
74425
75054
  var net = __require("net");
74426
75055
  var tls = __require("tls");
74427
- var { randomBytes, createHash: createHash13 } = __require("crypto");
75056
+ var { randomBytes, createHash: createHash14 } = __require("crypto");
74428
75057
  var { Duplex, Readable: Readable2 } = __require("stream");
74429
75058
  var { URL: URL2 } = __require("url");
74430
75059
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -75092,7 +75721,7 @@ var require_websocket = __commonJS({
75092
75721
  abortHandshake(websocket, socket, "Invalid Upgrade header");
75093
75722
  return;
75094
75723
  }
75095
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
75724
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
75096
75725
  if (res.headers["sec-websocket-accept"] !== digest2) {
75097
75726
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
75098
75727
  return;
@@ -75464,7 +76093,7 @@ var require_websocket_server = __commonJS({
75464
76093
  var EventEmitter = __require("events");
75465
76094
  var http3 = __require("http");
75466
76095
  var { Duplex } = __require("stream");
75467
- var { createHash: createHash13 } = __require("crypto");
76096
+ var { createHash: createHash14 } = __require("crypto");
75468
76097
  var extension3 = require_extension();
75469
76098
  var PerMessageDeflate2 = require_permessage_deflate();
75470
76099
  var subprotocol2 = require_subprotocol();
@@ -75771,7 +76400,7 @@ var require_websocket_server = __commonJS({
75771
76400
  );
75772
76401
  }
75773
76402
  if (this._state > RUNNING) return abortHandshake(socket, 503);
75774
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
76403
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
75775
76404
  const headers = [
75776
76405
  "HTTP/1.1 101 Switching Protocols",
75777
76406
  "Upgrade: websocket",
@@ -142909,6 +143538,186 @@ var init_audio_jobs = __esm({
142909
143538
  }
142910
143539
  });
142911
143540
 
143541
+ // src/memory/feedback-model.ts
143542
+ function feedbackWeightMultiplier(weight) {
143543
+ return Math.max(0.35, Math.min(1.4, 0.35 + Math.max(0, Math.min(2, weight)) * 0.525));
143544
+ }
143545
+ var init_feedback_model = __esm({
143546
+ "src/memory/feedback-model.ts"() {
143547
+ "use strict";
143548
+ init_esm_shims();
143549
+ }
143550
+ });
143551
+
143552
+ // src/memory/feedback.ts
143553
+ var feedback_exports = {};
143554
+ __export(feedback_exports, {
143555
+ FEEDBACK_DELTAS: () => FEEDBACK_DELTAS,
143556
+ MemoryFeedbackStore: () => MemoryFeedbackStore,
143557
+ applyMemoryFeedback: () => applyMemoryFeedback,
143558
+ feedbackWeightMultiplier: () => feedbackWeightMultiplier
143559
+ });
143560
+ import { randomUUID as randomUUID7 } from "crypto";
143561
+ function applyMemoryFeedback(current, event) {
143562
+ const delta = event.signal === "revoke" ? 0 : FEEDBACK_DELTAS[event.signal];
143563
+ const weight = Math.max(0, Math.min(2, current.weight + delta));
143564
+ const status = (event.signal === "code-conflict" || event.signal === "user-correction" || event.signal === "weaken") && weight < 0.5 ? "archived" : current.status;
143565
+ return { weight, status, audit: [...current.audit, { ...event }] };
143566
+ }
143567
+ function rowToEvent(row) {
143568
+ return {
143569
+ id: String(row.id),
143570
+ projectId: String(row.project_id),
143571
+ candidateKind: row.candidate_kind,
143572
+ candidateId: String(row.candidate_id),
143573
+ signal: row.signal,
143574
+ ...row.observed_at ? { at: String(row.observed_at) } : { at: (/* @__PURE__ */ new Date()).toISOString() },
143575
+ ...row.actor ? { actor: String(row.actor) } : {},
143576
+ ...row.note ? { note: String(row.note) } : {},
143577
+ ...row.source_ref ? { sourceRef: String(row.source_ref) } : {},
143578
+ ...row.target_event_id ? { targetEventId: String(row.target_event_id) } : {},
143579
+ delta: Number(row.delta ?? 0)
143580
+ };
143581
+ }
143582
+ var FEEDBACK_DELTAS, MemoryFeedbackStore;
143583
+ var init_feedback = __esm({
143584
+ "src/memory/feedback.ts"() {
143585
+ "use strict";
143586
+ init_esm_shims();
143587
+ init_sqlite_db();
143588
+ init_feedback_model();
143589
+ FEEDBACK_DELTAS = {
143590
+ "user-correction": -0.35,
143591
+ "verification-success": 0.25,
143592
+ "verification-failure": -0.25,
143593
+ "code-change": -0.15,
143594
+ used: 0.1,
143595
+ "not-used": -0.05,
143596
+ "code-conflict": -0.4,
143597
+ strengthen: 0.2,
143598
+ weaken: -0.2
143599
+ };
143600
+ MemoryFeedbackStore = class {
143601
+ db = null;
143602
+ async init(dataDir) {
143603
+ this.db = getDatabase(dataDir);
143604
+ }
143605
+ requireDb() {
143606
+ if (!this.db) throw new Error("MemoryFeedbackStore is not initialized.");
143607
+ return this.db;
143608
+ }
143609
+ loadEvents(projectId, candidateKind, candidateId) {
143610
+ return this.requireDb().prepare(`
143611
+ SELECT * FROM memory_feedback_events
143612
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143613
+ ORDER BY observed_at ASC, rowid ASC
143614
+ `).all(projectId, candidateKind, candidateId).map(rowToEvent);
143615
+ }
143616
+ rebuild(projectId, candidateKind, candidateId) {
143617
+ const events = this.loadEvents(projectId, candidateKind, candidateId);
143618
+ const revoked = new Set(events.filter((event) => event.signal === "revoke" && event.targetEventId).map((event) => event.targetEventId));
143619
+ let state = { weight: 1, status: "active", audit: [] };
143620
+ for (const event of events) {
143621
+ if (event.signal === "revoke" || revoked.has(event.id)) {
143622
+ state.audit.push(event);
143623
+ continue;
143624
+ }
143625
+ state = applyMemoryFeedback(state, event);
143626
+ }
143627
+ this.requireDb().prepare(`
143628
+ INSERT INTO memory_feedback_states (project_id, candidate_kind, candidate_id, weight, lifecycle_status, updated_at)
143629
+ VALUES (?, ?, ?, ?, ?, ?)
143630
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
143631
+ weight = excluded.weight, lifecycle_status = excluded.lifecycle_status, updated_at = excluded.updated_at
143632
+ `).run(projectId, candidateKind, candidateId, state.weight, state.status, (/* @__PURE__ */ new Date()).toISOString());
143633
+ return state;
143634
+ }
143635
+ getState(projectId, candidateKind, candidateId) {
143636
+ const row = this.requireDb().prepare(`
143637
+ SELECT weight, lifecycle_status FROM memory_feedback_states
143638
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143639
+ `).get(projectId, candidateKind, candidateId);
143640
+ if (!row) return { weight: 1, status: "active", audit: [] };
143641
+ return {
143642
+ weight: Number(row.weight ?? 1),
143643
+ status: row.lifecycle_status ?? "active",
143644
+ audit: this.loadEvents(projectId, candidateKind, candidateId)
143645
+ };
143646
+ }
143647
+ listStates(projectId, candidateKind = "observation") {
143648
+ return this.requireDb().prepare(`
143649
+ SELECT candidate_id, weight, lifecycle_status, updated_at
143650
+ FROM memory_feedback_states WHERE project_id = ? AND candidate_kind = ?
143651
+ ORDER BY updated_at DESC
143652
+ `).all(projectId, candidateKind).map((row) => ({
143653
+ candidateId: String(row.candidate_id),
143654
+ weight: Number(row.weight),
143655
+ status: row.lifecycle_status,
143656
+ updatedAt: String(row.updated_at)
143657
+ }));
143658
+ }
143659
+ weights(projectId, candidateKind = "observation") {
143660
+ const states = /* @__PURE__ */ new Map();
143661
+ for (const row of this.listStates(projectId, candidateKind)) {
143662
+ states.set(row.candidateId, this.getState(projectId, candidateKind, row.candidateId));
143663
+ }
143664
+ return states;
143665
+ }
143666
+ record(input) {
143667
+ if (input.signal === "revoke" && !input.targetEventId) {
143668
+ throw new Error("A revoke feedback event requires targetEventId.");
143669
+ }
143670
+ if (input.signal === "revoke") {
143671
+ const target = this.requireDb().prepare(`
143672
+ SELECT 1 FROM memory_feedback_events
143673
+ WHERE id = ? AND project_id = ? AND candidate_kind = ? AND candidate_id = ?
143674
+ `).get(input.targetEventId, input.projectId, input.candidateKind, input.candidateId);
143675
+ if (!target) throw new Error("The feedback event to revoke was not found in the same candidate scope.");
143676
+ }
143677
+ const event = {
143678
+ id: randomUUID7(),
143679
+ projectId: input.projectId,
143680
+ candidateKind: input.candidateKind,
143681
+ candidateId: input.candidateId,
143682
+ signal: input.signal,
143683
+ sourceRef: input.sourceRef,
143684
+ ...input.actor ? { actor: input.actor } : {},
143685
+ ...input.note ? { note: input.note.slice(0, 2e3) } : {},
143686
+ ...input.targetEventId ? { targetEventId: input.targetEventId } : {},
143687
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
143688
+ delta: input.signal === "revoke" ? 0 : FEEDBACK_DELTAS[input.signal]
143689
+ };
143690
+ this.requireDb().prepare(`
143691
+ INSERT INTO memory_feedback_events (
143692
+ id, project_id, candidate_kind, candidate_id, signal, actor, note,
143693
+ source_ref, target_event_id, delta, observed_at
143694
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
143695
+ `).run(
143696
+ event.id,
143697
+ event.projectId,
143698
+ event.candidateKind,
143699
+ event.candidateId,
143700
+ event.signal,
143701
+ event.actor ?? null,
143702
+ event.note ?? null,
143703
+ event.sourceRef,
143704
+ event.targetEventId ?? null,
143705
+ event.delta,
143706
+ event.at
143707
+ );
143708
+ return { event, state: this.rebuild(input.projectId, input.candidateKind, input.candidateId) };
143709
+ }
143710
+ audit(projectId, candidateKind, candidateId, limit2 = 100) {
143711
+ return this.requireDb().prepare(`
143712
+ SELECT * FROM memory_feedback_events
143713
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
143714
+ ORDER BY observed_at DESC, rowid DESC LIMIT ?
143715
+ `).all(projectId, candidateKind, candidateId, Math.min(200, Math.max(1, Math.floor(limit2)))).map(rowToEvent);
143716
+ }
143717
+ };
143718
+ }
143719
+ });
143720
+
142912
143721
  // src/memory/retention.ts
142913
143722
  var retention_exports = {};
142914
143723
  __export(retention_exports, {
@@ -142950,6 +143759,7 @@ function getEffectiveRetentionDays(doc) {
142950
143759
  }
142951
143760
  function isImmune(doc) {
142952
143761
  if (doc.type === "probe") return false;
143762
+ if (doc.feedbackStatus === "archived") return false;
142953
143763
  if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
142954
143764
  if (doc.valueCategory === "core") return true;
142955
143765
  const importance = getImportanceLevel(doc);
@@ -142982,7 +143792,8 @@ function calculateRelevance(doc, referenceTime) {
142982
143792
  const decayFactor = Math.exp(-ageDays / retention);
142983
143793
  const accessCount = doc.accessCount ?? 0;
142984
143794
  const accessBoost = Math.min(2, 1 + 0.1 * accessCount);
142985
- let totalScore = base * decayFactor * accessBoost;
143795
+ const feedbackMultiplier = doc.feedbackWeight === void 0 ? 1 : feedbackWeightMultiplier(doc.feedbackWeight);
143796
+ let totalScore = base * decayFactor * accessBoost * feedbackMultiplier;
142986
143797
  const immune = isImmune(doc);
142987
143798
  if (immune) {
142988
143799
  totalScore = Math.max(totalScore, 0.5);
@@ -142994,7 +143805,8 @@ function calculateRelevance(doc, referenceTime) {
142994
143805
  decayFactor,
142995
143806
  accessBoost,
142996
143807
  ageDays,
142997
- isImmune: immune
143808
+ isImmune: immune,
143809
+ feedbackMultiplier
142998
143810
  };
142999
143811
  }
143000
143812
  function rankByRelevance(docs, referenceTime) {
@@ -143002,6 +143814,7 @@ function rankByRelevance(docs, referenceTime) {
143002
143814
  }
143003
143815
  function getRetentionZone(doc, referenceTime) {
143004
143816
  const now3 = referenceTime ?? /* @__PURE__ */ new Date();
143817
+ if (doc.feedbackStatus === "archived") return "archive-candidate";
143005
143818
  const importance = getImportanceLevel(doc);
143006
143819
  const retention = getEffectiveRetentionDays(doc);
143007
143820
  const createdAt = new Date(doc.createdAt);
@@ -143073,7 +143886,7 @@ function explainRetention(doc, referenceTime) {
143073
143886
  summary: parts.join(" ; ")
143074
143887
  };
143075
143888
  }
143076
- function toRetentionDocument(obs, accessMap) {
143889
+ function toRetentionDocument(obs, accessMap, feedback) {
143077
143890
  const access2 = accessMap?.get(obs.id);
143078
143891
  return {
143079
143892
  id: `obs-${obs.id}`,
@@ -143095,11 +143908,12 @@ function toRetentionDocument(obs, accessMap) {
143095
143908
  sourceDetail: obs.sourceDetail ?? "",
143096
143909
  valueCategory: obs.valueCategory ?? "",
143097
143910
  admissionState: obs.admissionState ?? "",
143098
- admissionReason: obs.admissionReason ?? ""
143911
+ admissionReason: obs.admissionReason ?? "",
143912
+ ...feedback ? { feedbackWeight: feedback.weight, feedbackStatus: feedback.status } : {}
143099
143913
  };
143100
143914
  }
143101
143915
  function projectObservationRetention(observation, options = {}) {
143102
- const document2 = toRetentionDocument(observation, options.accessMap);
143916
+ const document2 = toRetentionDocument(observation, options.accessMap, options.feedback);
143103
143917
  const relevance = calculateRelevance(document2, options.referenceTime);
143104
143918
  const explanation = explainRetention(document2, options.referenceTime);
143105
143919
  return {
@@ -143124,7 +143938,17 @@ function summarizeRetentionProjections(projections) {
143124
143938
  }
143125
143939
  return { active, stale, archiveCandidates, immune };
143126
143940
  }
143127
- async function archiveExpiredBatch(_projectDir, options) {
143941
+ async function loadFeedbackStore(dataDir) {
143942
+ try {
143943
+ const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
143944
+ const store = new MemoryFeedbackStore2();
143945
+ await store.init(dataDir);
143946
+ return store;
143947
+ } catch {
143948
+ return void 0;
143949
+ }
143950
+ }
143951
+ async function archiveExpiredBatch(projectDir2, options) {
143128
143952
  const store = getObservationStore();
143129
143953
  const limit2 = Math.min(500, Math.max(1, Math.floor(options.limit ?? 100)));
143130
143954
  const afterId = Math.max(0, Math.floor(options.afterId ?? 0));
@@ -143133,11 +143957,13 @@ async function archiveExpiredBatch(_projectDir, options) {
143133
143957
  afterId,
143134
143958
  limit: limit2 + 1
143135
143959
  });
143960
+ const feedback = await loadFeedbackStore(projectDir2);
143136
143961
  const hasMore = page.length > limit2;
143137
143962
  const scanned = hasMore ? page.slice(0, limit2) : page;
143138
143963
  const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => projectObservationRetention(observation, {
143139
143964
  accessMap: options.accessMap,
143140
- referenceTime: options.referenceTime
143965
+ referenceTime: options.referenceTime,
143966
+ ...feedback ? { feedback: feedback.getState(options.projectId, "observation", String(observation.id)) } : {}
143141
143967
  }).zone === "archive-candidate").map((observation) => observation.id);
143142
143968
  const archived = candidateIds.length === 0 ? 0 : await store.atomic(async (tx) => {
143143
143969
  const updates = await Promise.all(
@@ -143168,6 +143994,7 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
143168
143994
  const remaining = reader ? remainingObservations.filter((observation) => canManageObservation(observation, reader)).length : remainingObservations.length;
143169
143995
  return { archived, remaining };
143170
143996
  }
143997
+ const feedback = await loadFeedbackStore(projectDir2);
143171
143998
  return await store.atomic(async (tx) => {
143172
143999
  const allObs = await tx.loadAll();
143173
144000
  const activeObs = allObs.filter(
@@ -143175,7 +144002,11 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
143175
144002
  );
143176
144003
  const archivedIds = [];
143177
144004
  for (const obs of activeObs) {
143178
- const retention = projectObservationRetention(obs, { accessMap, referenceTime });
144005
+ const retention = projectObservationRetention(obs, {
144006
+ accessMap,
144007
+ referenceTime,
144008
+ ...feedback ? { feedback: feedback.getState(obs.projectId, "observation", String(obs.id)) } : {}
144009
+ });
143179
144010
  if (retention.zone === "archive-candidate") {
143180
144011
  archivedIds.push(obs.id);
143181
144012
  }
@@ -143195,6 +144026,7 @@ var init_retention = __esm({
143195
144026
  init_esm_shims();
143196
144027
  init_obs_store();
143197
144028
  init_visibility();
144029
+ init_feedback_model();
143198
144030
  RETENTION_DAYS = {
143199
144031
  critical: 365,
143200
144032
  high: 180,
@@ -143231,7 +144063,7 @@ var init_retention = __esm({
143231
144063
  });
143232
144064
 
143233
144065
  // src/memory/owner.ts
143234
- import { randomUUID as randomUUID6 } from "crypto";
144066
+ import { randomUUID as randomUUID8 } from "crypto";
143235
144067
  import { promises as fs9 } from "fs";
143236
144068
  import path17 from "path";
143237
144069
  function ownerPath(dataDir) {
@@ -143261,7 +144093,7 @@ async function resolveLocalMemoryOwner(dataDir, options = {}) {
143261
144093
  await fs9.mkdir(path17.dirname(filePath), { recursive: true });
143262
144094
  const created = {
143263
144095
  version: 1,
143264
- id: "local-" + randomUUID6(),
144096
+ id: "local-" + randomUUID8(),
143265
144097
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
143266
144098
  };
143267
144099
  try {
@@ -143284,7 +144116,7 @@ var init_owner = __esm({
143284
144116
  });
143285
144117
 
143286
144118
  // src/memory/long-term-store.ts
143287
- import { randomUUID as randomUUID7 } from "crypto";
144119
+ import { randomUUID as randomUUID9 } from "crypto";
143288
144120
  function optionalText2(value) {
143289
144121
  return typeof value === "string" && value.trim() ? value : void 0;
143290
144122
  }
@@ -143335,7 +144167,7 @@ function rowToEvidence(row) {
143335
144167
  createdAt: row.createdAt
143336
144168
  };
143337
144169
  }
143338
- function rowToEvent(row) {
144170
+ function rowToEvent2(row) {
143339
144171
  return {
143340
144172
  id: row.id,
143341
144173
  memoryId: row.memoryId,
@@ -143444,7 +144276,7 @@ var init_long_term_store = __esm({
143444
144276
  }
143445
144277
  insertEvidence(input) {
143446
144278
  const evidence = {
143447
- id: randomUUID7(),
144279
+ id: randomUUID9(),
143448
144280
  ...input,
143449
144281
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
143450
144282
  };
@@ -143471,7 +144303,7 @@ var init_long_term_store = __esm({
143471
144303
  }
143472
144304
  recordEvent(input) {
143473
144305
  const event = {
143474
- id: randomUUID7(),
144306
+ id: randomUUID9(),
143475
144307
  ...input,
143476
144308
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
143477
144309
  };
@@ -143492,7 +144324,7 @@ var init_long_term_store = __esm({
143492
144324
  listEvents(memoryId) {
143493
144325
  return this.db.prepare(`
143494
144326
  SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
143495
- `).all(memoryId).map(rowToEvent);
144327
+ `).all(memoryId).map(rowToEvent2);
143496
144328
  }
143497
144329
  markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
143498
144330
  const unique = [...new Set(ids.filter(Boolean))];
@@ -143561,7 +144393,7 @@ __export(long_term_exports, {
143561
144393
  selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
143562
144394
  supersedeLongTermMemory: () => supersedeLongTermMemory
143563
144395
  });
143564
- import { randomUUID as randomUUID8 } from "crypto";
144396
+ import { randomUUID as randomUUID10 } from "crypto";
143565
144397
  function isOneOf(value, values) {
143566
144398
  return typeof value === "string" && values.includes(value);
143567
144399
  }
@@ -143743,7 +144575,7 @@ async function createManualLongTermMemory(input) {
143743
144575
  const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
143744
144576
  if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
143745
144577
  assertScope(input.scope);
143746
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
144578
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
143747
144579
  return createLongTermMemoryWithDataDir({
143748
144580
  dataDir: input.dataDir,
143749
144581
  originProjectId: input.projectId,
@@ -143798,7 +144630,7 @@ async function createLongTermMemoryWithDataDir(input) {
143798
144630
  assertPortableEvidence(input.scope, portability, evidenceInputs);
143799
144631
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
143800
144632
  const memory = {
143801
- id: randomUUID8(),
144633
+ id: randomUUID10(),
143802
144634
  originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
143803
144635
  ownerId: compactText(input.ownerId, "owner id", 160),
143804
144636
  scope: input.scope,
@@ -144116,9 +144948,9 @@ var init_long_term = __esm({
144116
144948
  });
144117
144949
 
144118
144950
  // src/codegraph/ids.ts
144119
- import { createHash as createHash5 } from "crypto";
144951
+ import { createHash as createHash6 } from "crypto";
144120
144952
  function digest(input) {
144121
- return createHash5("sha256").update(input).digest("hex").slice(0, 16);
144953
+ return createHash6("sha256").update(input).digest("hex").slice(0, 16);
144122
144954
  }
144123
144955
  function normalizeCodePath(path22) {
144124
144956
  return path22.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -144153,7 +144985,7 @@ var store_exports = {};
144153
144985
  __export(store_exports, {
144154
144986
  CodeGraphStore: () => CodeGraphStore
144155
144987
  });
144156
- import { randomUUID as randomUUID9 } from "crypto";
144988
+ import { randomUUID as randomUUID11 } from "crypto";
144157
144989
  import { posix as pathPosix } from "path";
144158
144990
  function rowToFile(row) {
144159
144991
  return {
@@ -144870,7 +145702,7 @@ var init_store = __esm({
144870
145702
  * so an interrupted scan cannot be advertised as complete.
144871
145703
  */
144872
145704
  recordCodeStateSnapshot(input) {
144873
- const id = randomUUID9();
145705
+ const id = randomUUID11();
144874
145706
  let snapshot;
144875
145707
  const tx = this.db.transaction(() => {
144876
145708
  const previous = this.db.prepare(
@@ -144948,7 +145780,7 @@ var init_store = __esm({
144948
145780
 
144949
145781
  // src/codegraph/code-state.ts
144950
145782
  import { execFile as execFile2 } from "child_process";
144951
- import { createHash as createHash6 } from "crypto";
145783
+ import { createHash as createHash7 } from "crypto";
144952
145784
  import { promisify as promisify3 } from "util";
144953
145785
  async function runGit(rootPath, args) {
144954
145786
  try {
@@ -144977,7 +145809,7 @@ function changedPathCount(status) {
144977
145809
  return count2;
144978
145810
  }
144979
145811
  function fingerprint(baseRevision, status) {
144980
- return createHash6("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
145812
+ return createHash7("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
144981
145813
  }
144982
145814
  async function collectCodeStateSnapshot(input) {
144983
145815
  const [revision, status] = await Promise.all([
@@ -145077,11 +145909,11 @@ __export(lite_provider_exports, {
145077
145909
  indexProjectLite: () => indexProjectLite,
145078
145910
  refreshProjectLite: () => refreshProjectLite
145079
145911
  });
145080
- import { createHash as createHash7 } from "crypto";
145912
+ import { createHash as createHash8 } from "crypto";
145081
145913
  import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
145082
145914
  import { join as join4, relative } from "path";
145083
145915
  function hashText(text2) {
145084
- return createHash7("sha256").update(text2).digest("hex");
145916
+ return createHash8("sha256").update(text2).digest("hex");
145085
145917
  }
145086
145918
  function extension2(path22) {
145087
145919
  const index = path22.lastIndexOf(".");
@@ -145648,7 +146480,7 @@ var claim_store_exports = {};
145648
146480
  __export(claim_store_exports, {
145649
146481
  ClaimStore: () => ClaimStore
145650
146482
  });
145651
- import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
146483
+ import { createHash as createHash9, randomUUID as randomUUID12 } from "crypto";
145652
146484
  function optionalText3(value) {
145653
146485
  return typeof value === "string" && value ? value : void 0;
145654
146486
  }
@@ -145687,7 +146519,7 @@ function rowToEvidence2(row) {
145687
146519
  createdAt: row.createdAt
145688
146520
  };
145689
146521
  }
145690
- function rowToEvent2(row) {
146522
+ function rowToEvent3(row) {
145691
146523
  return {
145692
146524
  id: row.id,
145693
146525
  projectId: row.projectId,
@@ -145701,7 +146533,7 @@ function rowToEvent2(row) {
145701
146533
  };
145702
146534
  }
145703
146535
  function evidenceKey(input) {
145704
- return createHash8("sha256").update(JSON.stringify([
146536
+ return createHash9("sha256").update(JSON.stringify([
145705
146537
  input.evidenceKind,
145706
146538
  input.evidenceId,
145707
146539
  input.relation,
@@ -145832,7 +146664,7 @@ var init_claim_store = __esm({
145832
146664
  locator, capturedHash, evidenceKey, createdAt
145833
146665
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
145834
146666
  `).run(
145835
- randomUUID10(),
146667
+ randomUUID12(),
145836
146668
  input.claimId,
145837
146669
  input.evidenceKind,
145838
146670
  input.evidenceId,
@@ -145854,7 +146686,7 @@ var init_claim_store = __esm({
145854
146686
  }
145855
146687
  recordEvent(input) {
145856
146688
  const event = {
145857
- id: randomUUID10(),
146689
+ id: randomUUID12(),
145858
146690
  ...input,
145859
146691
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
145860
146692
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -145882,7 +146714,7 @@ var init_claim_store = __esm({
145882
146714
  SELECT * FROM knowledge_claim_events
145883
146715
  WHERE claimId = ?
145884
146716
  ORDER BY createdAt ASC, id
145885
- `).all(claimId).map(rowToEvent2);
146717
+ `).all(claimId).map(rowToEvent3);
145886
146718
  }
145887
146719
  };
145888
146720
  }
@@ -145899,7 +146731,7 @@ __export(claims_exports, {
145899
146731
  supersedeClaim: () => supersedeClaim,
145900
146732
  writeClaim: () => writeClaim
145901
146733
  });
145902
- import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
146734
+ import { createHash as createHash10, randomUUID as randomUUID13 } from "crypto";
145903
146735
  function compactText2(value, field) {
145904
146736
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
145905
146737
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -145911,7 +146743,7 @@ function normalizedTerm(value) {
145911
146743
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
145912
146744
  }
145913
146745
  function hashIdentity(parts) {
145914
- return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
146746
+ return createHash10("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
145915
146747
  }
145916
146748
  function buildClaimIdentity(input) {
145917
146749
  const subject = normalizedTerm(input.subject);
@@ -145985,7 +146817,7 @@ function normalizeClaimInput(input) {
145985
146817
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
145986
146818
  return {
145987
146819
  claim: {
145988
- id: randomUUID11(),
146820
+ id: randomUUID13(),
145989
146821
  projectId,
145990
146822
  subject,
145991
146823
  predicate,
@@ -146152,7 +146984,7 @@ function supersedeClaim(store, input) {
146152
146984
  });
146153
146985
  }
146154
146986
  function hashObservation(observation) {
146155
- return createHash9("sha256").update(JSON.stringify([
146987
+ return createHash10("sha256").update(JSON.stringify([
146156
146988
  observation.title,
146157
146989
  observation.narrative,
146158
146990
  observation.facts,
@@ -146396,11 +147228,11 @@ var init_frontmatter = __esm({
146396
147228
  });
146397
147229
 
146398
147230
  // src/knowledge/markdown.ts
146399
- import { createHash as createHash10 } from "crypto";
147231
+ import { createHash as createHash11 } from "crypto";
146400
147232
  import { promises as fs10 } from "fs";
146401
147233
  import path18 from "path";
146402
147234
  function hashContent(content) {
146403
- return createHash10("sha256").update(content).digest("hex");
147235
+ return createHash11("sha256").update(content).digest("hex");
146404
147236
  }
146405
147237
  function stringField(data, key) {
146406
147238
  const value = data[key];
@@ -146526,11 +147358,11 @@ __export(wiki_exports, {
146526
147358
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
146527
147359
  readKnowledgePage: () => readKnowledgePage
146528
147360
  });
146529
- import { createHash as createHash11 } from "crypto";
147361
+ import { createHash as createHash12 } from "crypto";
146530
147362
  import { promises as fs11 } from "fs";
146531
147363
  import path19 from "path";
146532
147364
  function hash2(value) {
146533
- return createHash11("sha256").update(value).digest("hex");
147365
+ return createHash12("sha256").update(value).digest("hex");
146534
147366
  }
146535
147367
  function now() {
146536
147368
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -147070,7 +147902,7 @@ var init_workflow_sync = __esm({
147070
147902
  });
147071
147903
 
147072
147904
  // src/knowledge/workflow-store.ts
147073
- import { randomUUID as randomUUID12 } from "crypto";
147905
+ import { randomUUID as randomUUID14 } from "crypto";
147074
147906
  function parseJson(value, fallback) {
147075
147907
  if (typeof value !== "string" || !value) return fallback;
147076
147908
  try {
@@ -147170,7 +148002,7 @@ var init_workflow_store = __esm({
147170
148002
  }
147171
148003
  recordRun(input) {
147172
148004
  const run = {
147173
- id: randomUUID12(),
148005
+ id: randomUUID14(),
147174
148006
  workflowId: input.workflowId,
147175
148007
  projectId: input.projectId,
147176
148008
  task: input.task.trim(),
@@ -147238,7 +148070,7 @@ var init_outcome_types = __esm({
147238
148070
  });
147239
148071
 
147240
148072
  // src/knowledge/outcome-store.ts
147241
- import { randomUUID as randomUUID13 } from "crypto";
148073
+ import { randomUUID as randomUUID15 } from "crypto";
147242
148074
  function optionalText5(value) {
147243
148075
  return typeof value === "string" && value.trim() ? value : void 0;
147244
148076
  }
@@ -147282,7 +148114,7 @@ var init_outcome_store = __esm({
147282
148114
  }
147283
148115
  record(input) {
147284
148116
  const signal = {
147285
- id: randomUUID13(),
148117
+ id: randomUUID15(),
147286
148118
  projectId: text(input.projectId, "project id"),
147287
148119
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
147288
148120
  candidateId: text(input.candidateId, "candidate id"),
@@ -147387,11 +148219,11 @@ __export(workflows_exports, {
147387
148219
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
147388
148220
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
147389
148221
  });
147390
- import { createHash as createHash12 } from "crypto";
148222
+ import { createHash as createHash13 } from "crypto";
147391
148223
  import { promises as fs12 } from "fs";
147392
148224
  import path20 from "path";
147393
148225
  function hash3(value) {
147394
- return createHash12("sha256").update(value).digest("hex");
148226
+ return createHash13("sha256").update(value).digest("hex");
147395
148227
  }
147396
148228
  function now2() {
147397
148229
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -148773,6 +149605,16 @@ async function bindObservationCodeRefsBestEffort(observation) {
148773
149605
  } catch {
148774
149606
  }
148775
149607
  }
149608
+ async function persistEvidenceCard(observation) {
149609
+ if (!projectDir) return;
149610
+ try {
149611
+ const evidence = new EvidenceCardStore();
149612
+ await evidence.init(projectDir);
149613
+ evidence.upsertObservation(observation);
149614
+ } catch (error) {
149615
+ console.error(`[memorix] Evidence card projection failed for obs-${observation.id}: ${error instanceof Error ? error.message : String(error)}`);
149616
+ }
149617
+ }
148776
149618
  function queueClaimDerivation(observation) {
148777
149619
  const dataDir = projectDir;
148778
149620
  const isExplicit = observation.sourceDetail === "explicit";
@@ -149119,6 +149961,7 @@ async function storeObservation(input) {
149119
149961
  }
149120
149962
  if (!persistedCallbackInvoked) await input.onPersisted?.(observation);
149121
149963
  await bindObservationCodeRefsBestEffort(observation);
149964
+ await persistEvidenceCard(observation);
149122
149965
  queueObservationQualification(observation);
149123
149966
  if (resolveObservationVisibility(observation) === "project") {
149124
149967
  queueClaimDerivation(observation);
@@ -149211,6 +150054,7 @@ async function upsertObservation(existing, input, now3) {
149211
150054
  await store.update(existing);
149212
150055
  }
149213
150056
  await bindObservationCodeRefsBestEffort(existing);
150057
+ await persistEvidenceCard(existing);
149214
150058
  queueObservationQualification(existing);
149215
150059
  if (resolveObservationVisibility(existing) === "project") {
149216
150060
  queueClaimDerivation(existing);
@@ -149344,6 +150188,7 @@ async function resolveObservations(ids, status = "resolved") {
149344
150188
  await Promise.all(changed.map((observation) => tx.update(observation)));
149345
150189
  });
149346
150190
  }
150191
+ for (const observation of changed) await persistEvidenceCard(observation);
149347
150192
  return { resolved, notFound };
149348
150193
  }
149349
150194
  function getProjectObservations(projectId) {
@@ -149681,6 +150526,7 @@ var init_observations = __esm({
149681
150526
  init_secret_filter();
149682
150527
  init_lifecycle();
149683
150528
  init_visibility();
150529
+ init_evidence_store();
149684
150530
  observations = [];
149685
150531
  nextId = 1;
149686
150532
  projectDir = null;