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
@@ -95,6 +95,8 @@ function loadEnvFile(filePath) {
95
95
  const parsed = parse(readFileSync(filePath, "utf-8"));
96
96
  for (const [key, value] of Object.entries(parsed)) {
97
97
  if (!(key in process.env)) {
98
+ loadedEnvKeys.add(key);
99
+ loadedEnvValues.set(key, value);
98
100
  process.env[key] = value;
99
101
  injectedKeys.add(key);
100
102
  }
@@ -107,6 +109,8 @@ function loadDotenv(projectRoot, options = {}) {
107
109
  resetDotenv();
108
110
  }
109
111
  loadedEnvFiles.length = 0;
112
+ loadedEnvKeys.clear();
113
+ loadedEnvValues.clear();
110
114
  if (projectRoot) {
111
115
  loadEnvFile(getProjectDotenvPath(projectRoot));
112
116
  }
@@ -122,11 +126,12 @@ function resetDotenv() {
122
126
  dotenvLoaded = false;
123
127
  dotenvProjectRoot = null;
124
128
  loadedEnvFiles.length = 0;
129
+ loadedEnvKeys.clear();
125
130
  }
126
131
  function getLoadedEnvFiles() {
127
132
  return loadedEnvFiles;
128
133
  }
129
- var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, injectedKeys;
134
+ var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
130
135
  var init_dotenv_loader = __esm({
131
136
  "src/config/dotenv-loader.ts"() {
132
137
  "use strict";
@@ -135,6 +140,8 @@ var init_dotenv_loader = __esm({
135
140
  dotenvLoaded = false;
136
141
  dotenvProjectRoot = null;
137
142
  loadedEnvFiles = [];
143
+ loadedEnvKeys = /* @__PURE__ */ new Set();
144
+ loadedEnvValues = /* @__PURE__ */ new Map();
138
145
  injectedKeys = /* @__PURE__ */ new Set();
139
146
  }
140
147
  });
@@ -176,6 +183,7 @@ function loadYamlConfig(projectRoot) {
176
183
  llm: { ...userConfig.llm, ...projectConfig.llm },
177
184
  agent: { ...userConfig.agent, ...projectConfig.agent },
178
185
  embedding: { ...userConfig.embedding, ...projectConfig.embedding },
186
+ rerank: { ...userConfig.rerank, ...projectConfig.rerank },
179
187
  git: { ...userConfig.git, ...projectConfig.git },
180
188
  codegraph: { ...userConfig.codegraph, ...projectConfig.codegraph },
181
189
  behavior: { ...userConfig.behavior, ...projectConfig.behavior },
@@ -517,7 +525,7 @@ function closeAllDatabases() {
517
525
  _dbCache.delete(key);
518
526
  }
519
527
  }
520
- var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
528
+ var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_EVIDENCE_CARDS_TABLE, CREATE_EVIDENCE_CARD_EVENTS_TABLE, CREATE_MEMORY_FEEDBACK_STATES_TABLE, CREATE_MEMORY_FEEDBACK_EVENTS_TABLE, CREATE_MCP_BINDINGS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
521
529
  var init_sqlite_db = __esm({
522
530
  "src/store/sqlite-db.ts"() {
523
531
  "use strict";
@@ -1163,6 +1171,79 @@ CREATE TABLE IF NOT EXISTS compaction_checkpoints (
1163
1171
  created_at TEXT NOT NULL,
1164
1172
  updated_at TEXT NOT NULL
1165
1173
  );
1174
+ `;
1175
+ CREATE_EVIDENCE_CARDS_TABLE = `
1176
+ CREATE TABLE IF NOT EXISTS evidence_cards (
1177
+ id TEXT PRIMARY KEY,
1178
+ project_id TEXT NOT NULL,
1179
+ candidate_kind TEXT NOT NULL,
1180
+ candidate_id TEXT NOT NULL,
1181
+ title TEXT NOT NULL,
1182
+ summary TEXT NOT NULL DEFAULT '',
1183
+ entity_name TEXT,
1184
+ source_kind TEXT NOT NULL,
1185
+ source_ref TEXT NOT NULL,
1186
+ locator TEXT,
1187
+ captured_hash TEXT,
1188
+ session_id TEXT,
1189
+ status TEXT NOT NULL DEFAULT 'active',
1190
+ verification TEXT NOT NULL DEFAULT 'unverified',
1191
+ freshness TEXT NOT NULL DEFAULT 'current',
1192
+ stale_reason TEXT,
1193
+ files_json TEXT NOT NULL DEFAULT '[]',
1194
+ related_entities_json TEXT NOT NULL DEFAULT '[]',
1195
+ created_at TEXT NOT NULL,
1196
+ updated_at TEXT NOT NULL,
1197
+ UNIQUE(project_id, candidate_kind, candidate_id)
1198
+ );
1199
+ `;
1200
+ CREATE_EVIDENCE_CARD_EVENTS_TABLE = `
1201
+ CREATE TABLE IF NOT EXISTS evidence_card_events (
1202
+ id TEXT PRIMARY KEY,
1203
+ project_id TEXT NOT NULL,
1204
+ card_id TEXT NOT NULL,
1205
+ kind TEXT NOT NULL,
1206
+ detail TEXT,
1207
+ created_at TEXT NOT NULL,
1208
+ FOREIGN KEY (card_id) REFERENCES evidence_cards(id) ON DELETE CASCADE
1209
+ );
1210
+ `;
1211
+ CREATE_MEMORY_FEEDBACK_STATES_TABLE = `
1212
+ CREATE TABLE IF NOT EXISTS memory_feedback_states (
1213
+ project_id TEXT NOT NULL,
1214
+ candidate_kind TEXT NOT NULL,
1215
+ candidate_id TEXT NOT NULL,
1216
+ weight REAL NOT NULL DEFAULT 1.0,
1217
+ lifecycle_status TEXT NOT NULL DEFAULT 'active',
1218
+ updated_at TEXT NOT NULL,
1219
+ PRIMARY KEY (project_id, candidate_kind, candidate_id)
1220
+ );
1221
+ `;
1222
+ CREATE_MEMORY_FEEDBACK_EVENTS_TABLE = `
1223
+ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1224
+ id TEXT PRIMARY KEY,
1225
+ project_id TEXT NOT NULL,
1226
+ candidate_kind TEXT NOT NULL,
1227
+ candidate_id TEXT NOT NULL,
1228
+ signal TEXT NOT NULL,
1229
+ actor TEXT,
1230
+ note TEXT,
1231
+ source_ref TEXT NOT NULL,
1232
+ target_event_id TEXT,
1233
+ delta REAL NOT NULL DEFAULT 0,
1234
+ observed_at TEXT NOT NULL
1235
+ );
1236
+ `;
1237
+ CREATE_MCP_BINDINGS_TABLE = `
1238
+ CREATE TABLE IF NOT EXISTS mcp_bindings (
1239
+ handle_id TEXT PRIMARY KEY,
1240
+ project_id TEXT NOT NULL,
1241
+ project_root TEXT NOT NULL,
1242
+ data_dir TEXT NOT NULL,
1243
+ created_at TEXT NOT NULL,
1244
+ last_used_at TEXT NOT NULL,
1245
+ expires_at TEXT
1246
+ );
1166
1247
  `;
1167
1248
  CREATE_INDEXES = `
1168
1249
  CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
@@ -1225,6 +1306,12 @@ CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_target
1225
1306
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
1226
1307
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
1227
1308
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
1309
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC);
1310
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id);
1311
+ CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1312
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1313
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1314
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1228
1315
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1229
1316
  ON maintenance_jobs(project_id, kind, dedupe_key)
1230
1317
  WHERE status IN ('pending', 'running', 'retry');
@@ -1380,6 +1467,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1380
1467
  }
1381
1468
  db2.exec("CREATE INDEX IF NOT EXISTS idx_media_jobs_source_asset ON media_jobs(project_id, source_asset_id, status)");
1382
1469
  }
1470
+ },
1471
+ {
1472
+ id: "1.8-context-control-plane",
1473
+ apply: (db2) => {
1474
+ db2.exec(CREATE_EVIDENCE_CARDS_TABLE);
1475
+ db2.exec(CREATE_EVIDENCE_CARD_EVENTS_TABLE);
1476
+ db2.exec(CREATE_MEMORY_FEEDBACK_STATES_TABLE);
1477
+ db2.exec(CREATE_MEMORY_FEEDBACK_EVENTS_TABLE);
1478
+ db2.exec(CREATE_MCP_BINDINGS_TABLE);
1479
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC)");
1480
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id)");
1481
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at)");
1482
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1483
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC)");
1484
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1485
+ }
1383
1486
  }
1384
1487
  ];
1385
1488
  _dbCache = /* @__PURE__ */ new Map();
@@ -1836,6 +1939,7 @@ function mergeTomlConfig(base, override) {
1836
1939
  llm: { ...base.memory?.llm, ...override.memory?.llm }
1837
1940
  },
1838
1941
  embedding: { ...base.embedding, ...override.embedding },
1942
+ rerank: { ...base.rerank, ...override.rerank },
1839
1943
  hooks: { ...base.hooks, ...override.hooks },
1840
1944
  git: { ...base.git, ...override.git },
1841
1945
  codegraph: { ...base.codegraph, ...override.codegraph },
@@ -1932,6 +2036,7 @@ __export(resolved_config_exports, {
1932
2036
  getResolvedConfigForCwd: () => getResolvedConfigForCwd,
1933
2037
  getResolvedEmbeddingLane: () => getResolvedEmbeddingLane,
1934
2038
  getResolvedMemoryLane: () => getResolvedMemoryLane,
2039
+ getResolvedRerankLane: () => getResolvedRerankLane,
1935
2040
  resetResolvedConfigCache: () => resetResolvedConfigCache
1936
2041
  });
1937
2042
  import { homedir as homedir5 } from "os";
@@ -1964,6 +2069,16 @@ function getResolvedConfig(options = {}) {
1964
2069
  legacy.llm?.baseUrl
1965
2070
  );
1966
2071
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
2072
+ const memoryLlmApiKey = first(
2073
+ process.env.MEMORIX_LLM_API_KEY,
2074
+ process.env.MEMORIX_API_KEY,
2075
+ toml.memory?.llm?.api_key,
2076
+ yaml.llm?.apiKey,
2077
+ legacy.llm?.apiKey,
2078
+ process.env.OPENAI_API_KEY,
2079
+ process.env.ANTHROPIC_API_KEY,
2080
+ openRouterMemoryLlmApiKey
2081
+ );
1967
2082
  const resolved = {
1968
2083
  agent: {
1969
2084
  provider: first(
@@ -2004,16 +2119,7 @@ function getResolvedConfig(options = {}) {
2004
2119
  provider: memoryLlmProvider,
2005
2120
  model: memoryLlmModel,
2006
2121
  baseUrl: memoryLlmBaseUrl,
2007
- apiKey: first(
2008
- process.env.MEMORIX_LLM_API_KEY,
2009
- process.env.MEMORIX_API_KEY,
2010
- toml.memory?.llm?.api_key,
2011
- yaml.llm?.apiKey,
2012
- legacy.llm?.apiKey,
2013
- process.env.OPENAI_API_KEY,
2014
- process.env.ANTHROPIC_API_KEY,
2015
- openRouterMemoryLlmApiKey
2016
- )
2122
+ apiKey: memoryLlmApiKey
2017
2123
  }
2018
2124
  },
2019
2125
  embedding: {
@@ -2023,6 +2129,12 @@ function getResolvedConfig(options = {}) {
2023
2129
  apiKey: first(process.env.MEMORIX_EMBEDDING_API_KEY, toml.embedding?.api_key, yaml.embedding?.apiKey, legacy.embeddingApi?.apiKey, openRouterEmbeddingApiKey),
2024
2130
  dimensions: firstNumber(parseNumber(process.env.MEMORIX_EMBEDDING_DIMENSIONS), toml.embedding?.dimensions, yaml.embedding?.dimensions, legacy.embeddingApi?.dimensions)
2025
2131
  },
2132
+ rerank: resolveRerankLane({
2133
+ toml,
2134
+ yaml,
2135
+ memoryLlmApiKey,
2136
+ memoryLlmBaseUrl
2137
+ }),
2026
2138
  git: {
2027
2139
  autoHook: firstBool(toml.git?.auto_hook, yaml.git?.autoHook),
2028
2140
  ingestOnCommit: firstBool(toml.git?.ingest_on_commit, yaml.git?.ingestOnCommit),
@@ -2092,6 +2204,9 @@ function getResolvedMemoryLane(options = {}) {
2092
2204
  function getResolvedEmbeddingLane(options = {}) {
2093
2205
  return getResolvedConfig(options).embedding;
2094
2206
  }
2207
+ function getResolvedRerankLane(options = {}) {
2208
+ return getResolvedConfig(options).rerank;
2209
+ }
2095
2210
  function resetResolvedConfigCache() {
2096
2211
  }
2097
2212
  function first(...values) {
@@ -2134,6 +2249,10 @@ function getEnvSourceNames() {
2134
2249
  "MEMORIX_EMBEDDING_BASE_URL",
2135
2250
  "MEMORIX_EMBEDDING_MODEL",
2136
2251
  "MEMORIX_EMBEDDING_DIMENSIONS",
2252
+ "MEMORIX_RERANK_PROVIDER",
2253
+ "MEMORIX_RERANK_MODEL",
2254
+ "MEMORIX_RERANK_BASE_URL",
2255
+ "MEMORIX_RERANK_API_KEY",
2137
2256
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
2138
2257
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
2139
2258
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
@@ -2157,6 +2276,63 @@ function normalizeExternalContext(value) {
2157
2276
  if (normalized === "auto" || normalized === "off") return normalized;
2158
2277
  return void 0;
2159
2278
  }
2279
+ function normalizeRerankProvider(value) {
2280
+ const normalized = value?.trim().toLowerCase();
2281
+ if (normalized === "http") return "http";
2282
+ return "off";
2283
+ }
2284
+ function resolveRerankLane(args) {
2285
+ const provider2 = normalizeRerankProvider(first(
2286
+ process.env.MEMORIX_RERANK_PROVIDER,
2287
+ args.toml.rerank?.provider,
2288
+ args.yaml.rerank?.provider,
2289
+ "off"
2290
+ ));
2291
+ const model = first(
2292
+ process.env.MEMORIX_RERANK_MODEL,
2293
+ args.toml.rerank?.model,
2294
+ args.yaml.rerank?.model
2295
+ );
2296
+ const explicitBaseUrl = first(
2297
+ process.env.MEMORIX_RERANK_BASE_URL,
2298
+ args.toml.rerank?.base_url,
2299
+ args.yaml.rerank?.baseUrl
2300
+ );
2301
+ const baseUrl = provider2 === "http" ? first(explicitBaseUrl, args.memoryLlmBaseUrl) : explicitBaseUrl;
2302
+ const dedicatedApiKey = first(
2303
+ process.env.MEMORIX_RERANK_API_KEY,
2304
+ args.toml.rerank?.api_key,
2305
+ args.yaml.rerank?.apiKey
2306
+ );
2307
+ const sameEndpoint = provider2 === "http" && sameTrustedEndpoint(baseUrl, args.memoryLlmBaseUrl);
2308
+ const inheritedApiKey = sameEndpoint ? args.memoryLlmApiKey : void 0;
2309
+ const apiKey = first(dedicatedApiKey, inheritedApiKey);
2310
+ const canSendRequest = provider2 === "http" && Boolean(baseUrl) && (Boolean(apiKey) || !args.memoryLlmApiKey || sameEndpoint);
2311
+ return { provider: provider2, model, baseUrl, apiKey, canSendRequest };
2312
+ }
2313
+ function sameTrustedEndpoint(left, right) {
2314
+ const a = normalizeTrustedEndpoint(left);
2315
+ const b = normalizeTrustedEndpoint(right);
2316
+ return Boolean(a && b && a === b);
2317
+ }
2318
+ function normalizeTrustedEndpoint(value) {
2319
+ const trimmed = value?.trim();
2320
+ if (!trimmed) return void 0;
2321
+ try {
2322
+ const url = new URL(trimmed);
2323
+ const protocol = url.protocol.toLowerCase();
2324
+ const hostname = url.hostname.toLowerCase();
2325
+ const port = url.port;
2326
+ let pathname = url.pathname.replace(/\/+$/, "");
2327
+ if (pathname.toLowerCase().endsWith("/rerank")) {
2328
+ pathname = pathname.slice(0, -"/rerank".length).replace(/\/+$/, "");
2329
+ }
2330
+ return `${protocol}//${hostname}${port ? `:${port}` : ""}${pathname}`;
2331
+ } catch {
2332
+ const fallback = trimmed.replace(/\/+$/, "").replace(/\/rerank$/i, "").replace(/\/+$/, "");
2333
+ return fallback.toLowerCase() || void 0;
2334
+ }
2335
+ }
2160
2336
  var init_resolved_config = __esm({
2161
2337
  "src/config/resolved-config.ts"() {
2162
2338
  "use strict";
@@ -4392,9 +4568,27 @@ __export(provider_exports2, {
4392
4568
  getLLMConfig: () => getLLMConfig,
4393
4569
  initLLM: () => initLLM,
4394
4570
  isLLMEnabled: () => isLLMEnabled,
4571
+ normalizeOpenAICompatibleBaseUrl: () => normalizeOpenAICompatibleBaseUrl,
4395
4572
  parseLLMTimeoutMs: () => parseLLMTimeoutMs,
4396
4573
  setLLMConfig: () => setLLMConfig
4397
4574
  });
4575
+ function normalizeOpenAICompatibleBaseUrl(baseUrl) {
4576
+ const base = baseUrl.trim().replace(/\/+$/, "");
4577
+ if (!base) return base;
4578
+ let pathname = "";
4579
+ try {
4580
+ pathname = new URL(base).pathname.replace(/\/+$/, "");
4581
+ } catch {
4582
+ pathname = base.split(/[?#]/, 1)[0].replace(/\/+$/, "");
4583
+ }
4584
+ const lastSegment = pathname.split("/").filter(Boolean).at(-1);
4585
+ if (lastSegment && OPENAI_API_VERSION_SEGMENT.test(lastSegment)) {
4586
+ return base;
4587
+ }
4588
+ const queryOrHash = base.match(/[?#].*$/)?.[0] ?? "";
4589
+ const pathWithoutQuery = base.slice(0, base.length - queryOrHash.length).replace(/\/+$/, "");
4590
+ return `${pathWithoutQuery}/v1${queryOrHash}`;
4591
+ }
4398
4592
  function parseLLMTimeoutMs(raw) {
4399
4593
  if (raw === void 0 || raw.trim() === "") return LLM_TIMEOUT_DEFAULT_MS;
4400
4594
  const parsed = Number(raw);
@@ -4498,8 +4692,7 @@ async function callLLM(systemPrompt, userMessage, signal) {
4498
4692
  }
4499
4693
  async function callOpenAICompatible(systemPrompt, userMessage, signal) {
4500
4694
  const config = currentConfig;
4501
- let base = config.baseUrl.replace(/\/+$/, "");
4502
- if (!base.endsWith("/v1")) base += "/v1";
4695
+ const base = normalizeOpenAICompatibleBaseUrl(config.baseUrl);
4503
4696
  const url = `${base}/chat/completions`;
4504
4697
  const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
4505
4698
  const response = await fetch(url, {
@@ -4578,8 +4771,7 @@ async function callLLMWithTools(messages, tools, signal) {
4578
4771
  }
4579
4772
  async function callOpenAIWithTools(messages, tools, signal) {
4580
4773
  const config = currentConfig;
4581
- let base = config.baseUrl.replace(/\/+$/, "");
4582
- if (!base.endsWith("/v1")) base += "/v1";
4774
+ const base = normalizeOpenAICompatibleBaseUrl(config.baseUrl);
4583
4775
  const url = `${base}/chat/completions`;
4584
4776
  const openaiMessages = messages.map((msg) => {
4585
4777
  if (msg.role === "tool") {
@@ -4729,8 +4921,7 @@ async function callAnthropicWithTools(messages, tools, signal) {
4729
4921
  }
4730
4922
  async function* callOpenAIWithToolsStream(messages, tools) {
4731
4923
  const config = currentConfig;
4732
- let base = config.baseUrl.replace(/\/+$/, "");
4733
- if (!base.endsWith("/v1")) base += "/v1";
4924
+ const base = normalizeOpenAICompatibleBaseUrl(config.baseUrl);
4734
4925
  const url = `${base}/chat/completions`;
4735
4926
  const openaiMessages = messages.map((msg) => {
4736
4927
  if (msg.role === "tool") {
@@ -4978,7 +5169,7 @@ async function* callAnthropicWithToolsStream(messages, tools) {
4978
5169
  }
4979
5170
  };
4980
5171
  }
4981
- var LLM_TIMEOUT_DEFAULT_MS, LLM_TIMEOUT_MIN_MS, LLM_TIMEOUT_MAX_MS, MAX_LLM_RESPONSE_BYTES, LLM_CALL_TIMEOUT_MS, PROVIDER_DEFAULTS, currentConfig;
5172
+ 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;
4982
5173
  var init_provider2 = __esm({
4983
5174
  "src/llm/provider.ts"() {
4984
5175
  "use strict";
@@ -4988,6 +5179,7 @@ var init_provider2 = __esm({
4988
5179
  LLM_TIMEOUT_MIN_MS = 1e3;
4989
5180
  LLM_TIMEOUT_MAX_MS = 3e5;
4990
5181
  MAX_LLM_RESPONSE_BYTES = 2 * 1024 * 1024;
5182
+ OPENAI_API_VERSION_SEGMENT = /^v\d+(?:[a-z][a-z0-9-]*)?$/i;
4991
5183
  LLM_CALL_TIMEOUT_MS = parseLLMTimeoutMs(process.env.MEMORIX_LLM_TIMEOUT_MS);
4992
5184
  PROVIDER_DEFAULTS = {
4993
5185
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
@@ -5047,6 +5239,48 @@ Rules:
5047
5239
  }
5048
5240
  });
5049
5241
 
5242
+ // src/rerank/policy.ts
5243
+ function parseRerankTimeoutMs(raw) {
5244
+ const value = raw?.trim();
5245
+ if (!value) return DEFAULT_RERANK_TIMEOUT_MS;
5246
+ const parsed = Number(value);
5247
+ if (!Number.isInteger(parsed) || Number.isNaN(parsed)) {
5248
+ console.warn(
5249
+ `[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.`
5250
+ );
5251
+ return DEFAULT_RERANK_TIMEOUT_MS;
5252
+ }
5253
+ if (parsed < RERANK_TIMEOUT_MIN_MS) return RERANK_TIMEOUT_MIN_MS;
5254
+ if (parsed > RERANK_TIMEOUT_MAX_MS) return RERANK_TIMEOUT_MAX_MS;
5255
+ return parsed;
5256
+ }
5257
+ function resolveRerankTimeoutMs(explicitTimeoutMs) {
5258
+ if (typeof explicitTimeoutMs === "number" && Number.isFinite(explicitTimeoutMs) && explicitTimeoutMs > 0) {
5259
+ return explicitTimeoutMs;
5260
+ }
5261
+ return parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
5262
+ }
5263
+ function shouldAttemptHttpRerank(input) {
5264
+ return input.quality === "thorough" && input.hasQuery && input.candidateCount >= MIN_RERANK_CANDIDATES;
5265
+ }
5266
+ function shouldAttemptLlmRerank(input) {
5267
+ if (input.httpRerankConfigured) return false;
5268
+ if (input.quality !== "thorough" || input.tier !== "heavy" || !input.hasQuery) return false;
5269
+ if (input.candidateCount < MIN_RERANK_CANDIDATES) return false;
5270
+ return input.topScore > 0 && input.secondScore / input.topScore > 0.7;
5271
+ }
5272
+ var DEFAULT_RERANK_TIMEOUT_MS, RERANK_TIMEOUT_MIN_MS, RERANK_TIMEOUT_MAX_MS, MIN_RERANK_CANDIDATES;
5273
+ var init_policy = __esm({
5274
+ "src/rerank/policy.ts"() {
5275
+ "use strict";
5276
+ init_esm_shims();
5277
+ DEFAULT_RERANK_TIMEOUT_MS = 3e4;
5278
+ RERANK_TIMEOUT_MIN_MS = 1e3;
5279
+ RERANK_TIMEOUT_MAX_MS = 3e5;
5280
+ MIN_RERANK_CANDIDATES = 3;
5281
+ }
5282
+ });
5283
+
5050
5284
  // src/timeout.ts
5051
5285
  function withTimeout(promise, ms, label) {
5052
5286
  return new Promise((resolve, reject) => {
@@ -5332,6 +5566,148 @@ var init_aliases = __esm({
5332
5566
  }
5333
5567
  });
5334
5568
 
5569
+ // src/rerank/http-provider.ts
5570
+ function buildRerankUrl(baseUrl) {
5571
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
5572
+ if (trimmed.endsWith("/rerank")) return trimmed;
5573
+ return `${trimmed}/rerank`;
5574
+ }
5575
+ function parseRerankResponse(payload) {
5576
+ const rows = extractRows(payload);
5577
+ if (rows.length === 0) {
5578
+ throw new Error("Neural rerank response contained no ranked results");
5579
+ }
5580
+ return rows.sort((a, b) => b.score - a.score);
5581
+ }
5582
+ function extractRows(payload) {
5583
+ if (Array.isArray(payload)) {
5584
+ return payload.map(rowFromUnknown).filter((row) => row !== null);
5585
+ }
5586
+ if (payload && typeof payload === "object" && "results" in payload) {
5587
+ const results = payload.results;
5588
+ if (Array.isArray(results)) {
5589
+ return results.map(rowFromUnknown).filter((row) => row !== null);
5590
+ }
5591
+ }
5592
+ throw new Error("Neural rerank response was not Cohere or TEI shaped");
5593
+ }
5594
+ function rowFromUnknown(value) {
5595
+ if (!value || typeof value !== "object") return null;
5596
+ const row = value;
5597
+ if (typeof row.index !== "number" || !Number.isFinite(row.index)) return null;
5598
+ const score = typeof row.relevance_score === "number" ? row.relevance_score : typeof row.score === "number" ? row.score : null;
5599
+ if (score === null || !Number.isFinite(score)) return null;
5600
+ return { index: row.index, score };
5601
+ }
5602
+ function buildRerankBody(query, documents, model) {
5603
+ const body = {
5604
+ query,
5605
+ documents,
5606
+ top_n: documents.length,
5607
+ return_documents: false
5608
+ };
5609
+ const trimmed = model?.trim();
5610
+ if (trimmed) body.model = trimmed;
5611
+ return body;
5612
+ }
5613
+ async function rerankViaHttp(query, documents, config) {
5614
+ if (documents.length === 0) return [];
5615
+ const url = buildRerankUrl(config.baseUrl);
5616
+ const headers = {
5617
+ "content-type": "application/json"
5618
+ };
5619
+ if (config.apiKey) {
5620
+ headers.authorization = `Bearer ${config.apiKey}`;
5621
+ }
5622
+ const controller = new AbortController();
5623
+ const timeoutMs = resolveRerankTimeoutMs(config.timeoutMs);
5624
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
5625
+ try {
5626
+ const fetchImpl = config.fetchImpl ?? fetch;
5627
+ const response = await fetchImpl(url, {
5628
+ method: "POST",
5629
+ headers,
5630
+ signal: controller.signal,
5631
+ body: JSON.stringify(buildRerankBody(query, documents, config.model))
5632
+ });
5633
+ if (!response.ok) {
5634
+ throw new Error(`Neural rerank HTTP ${response.status} from ${url}`);
5635
+ }
5636
+ return parseRerankResponse(await response.json());
5637
+ } finally {
5638
+ clearTimeout(timer);
5639
+ }
5640
+ }
5641
+ var init_http_provider = __esm({
5642
+ "src/rerank/http-provider.ts"() {
5643
+ "use strict";
5644
+ init_esm_shims();
5645
+ init_policy();
5646
+ }
5647
+ });
5648
+
5649
+ // src/rerank/index.ts
5650
+ var rerank_exports = {};
5651
+ __export(rerank_exports, {
5652
+ DEFAULT_RERANK_TIMEOUT_MS: () => DEFAULT_RERANK_TIMEOUT_MS,
5653
+ MIN_RERANK_CANDIDATES: () => MIN_RERANK_CANDIDATES,
5654
+ isNeuralRerankEnabled: () => isNeuralRerankEnabled,
5655
+ neuralRerankCandidates: () => neuralRerankCandidates,
5656
+ parseRerankTimeoutMs: () => parseRerankTimeoutMs,
5657
+ resolveRerankTimeoutMs: () => resolveRerankTimeoutMs,
5658
+ shouldAttemptHttpRerank: () => shouldAttemptHttpRerank,
5659
+ shouldAttemptLlmRerank: () => shouldAttemptLlmRerank
5660
+ });
5661
+ function isNeuralRerankEnabled(options = {}) {
5662
+ const lane = getResolvedRerankLane(options);
5663
+ return lane.provider === "http" && Boolean(lane.baseUrl);
5664
+ }
5665
+ async function neuralRerankCandidates(query, candidates, options = {}) {
5666
+ if (!isNeuralRerankEnabled(options) || candidates.length === 0) return null;
5667
+ const lane = getResolvedRerankLane(options);
5668
+ if (!lane.canSendRequest || !lane.baseUrl) return null;
5669
+ const toRerank = candidates.slice(0, MAX_RERANK);
5670
+ const rest = candidates.slice(MAX_RERANK);
5671
+ const documents = toRerank.map((candidate) => candidateText(candidate));
5672
+ const ranked = await rerankViaHttp(query, documents, {
5673
+ provider: "http",
5674
+ model: lane.model,
5675
+ baseUrl: lane.baseUrl,
5676
+ apiKey: lane.apiKey,
5677
+ timeoutMs: resolveRerankTimeoutMs()
5678
+ });
5679
+ const seen = /* @__PURE__ */ new Set();
5680
+ const reranked = [];
5681
+ for (const row of ranked) {
5682
+ const candidate = toRerank[row.index];
5683
+ if (!candidate || seen.has(row.index)) continue;
5684
+ reranked.push(candidate);
5685
+ seen.add(row.index);
5686
+ }
5687
+ for (const [index, candidate] of toRerank.entries()) {
5688
+ if (!seen.has(index)) reranked.push(candidate);
5689
+ }
5690
+ reranked.push(...rest);
5691
+ return reranked;
5692
+ }
5693
+ function candidateText(candidate) {
5694
+ const narrative = candidate.narrative?.trim();
5695
+ if (narrative) return `${candidate.title} \u2014 ${narrative.slice(0, 200)}`;
5696
+ return candidate.title;
5697
+ }
5698
+ var MAX_RERANK;
5699
+ var init_rerank = __esm({
5700
+ "src/rerank/index.ts"() {
5701
+ "use strict";
5702
+ init_esm_shims();
5703
+ init_resolved_config();
5704
+ init_http_provider();
5705
+ init_policy();
5706
+ init_policy();
5707
+ MAX_RERANK = 10;
5708
+ }
5709
+ });
5710
+
5335
5711
  // src/llm/quality.ts
5336
5712
  var quality_exports = {};
5337
5713
  __export(quality_exports, {
@@ -5371,9 +5747,9 @@ async function rerankResults(query, candidates) {
5371
5747
  if (!isLLMEnabled() || candidates.length <= 2) {
5372
5748
  return { reranked: candidates, usedLLM: false };
5373
5749
  }
5374
- const MAX_RERANK = 10;
5375
- const toRerank = candidates.slice(0, MAX_RERANK);
5376
- const rest = candidates.slice(MAX_RERANK);
5750
+ const MAX_RERANK2 = 10;
5751
+ const toRerank = candidates.slice(0, MAX_RERANK2);
5752
+ const rest = candidates.slice(MAX_RERANK2);
5377
5753
  try {
5378
5754
  const candidateList = toRerank.map(
5379
5755
  (c) => `[ID: ${c.id}] (${c.type}) ${c.title}${c.narrative ? ` \u2014 ${c.narrative.substring(0, 100)}` : ""}`
@@ -6266,46 +6642,78 @@ async function searchObservations(options) {
6266
6642
  }
6267
6643
  }
6268
6644
  }
6269
- const shouldRerank = quality === "thorough" && tier === "heavy" && hasQuery && intermediate.length > 2 && (() => {
6270
- const top = intermediate[0]?.score ?? 0;
6271
- const second = intermediate[1]?.score ?? 0;
6272
- return top > 0 && second / top > 0.7;
6273
- })();
6274
- if (shouldRerank) {
6275
- try {
6276
- const { rerankResults: rerankResults2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
6277
- const narrativeMap = /* @__PURE__ */ new Map();
6278
- for (const hit of results.hits) {
6279
- const doc = hit.document;
6280
- narrativeMap.set(makeEntryKey(doc.projectId, doc.observationId), doc.narrative);
6281
- }
6282
- const RERANK_TOP_K = 5;
6283
- const toRerank = intermediate.slice(0, RERANK_TOP_K);
6284
- const candidates = toRerank.map((e2, index) => ({
6285
- id: `r${index + 1}`,
6286
- title: e2.title,
6287
- type: e2.type,
6288
- score: e2.score,
6289
- narrative: narrativeMap.get(makeEntryKey(e2.projectId, e2.id))
6290
- }));
6291
- const _parsedRerank = parseInt(process.env.MEMORIX_RERANK_TIMEOUT_MS || "", 10);
6292
- const RERANK_TIMEOUT_MS = Number.isFinite(_parsedRerank) && _parsedRerank > 0 ? _parsedRerank : 5e3;
6293
- const { reranked, usedLLM } = await withTimeout(
6294
- rerankResults2(originalQuery, candidates),
6295
- RERANK_TIMEOUT_MS,
6296
- "LLM rerank"
6297
- );
6298
- mark(`rerank(usedLLM=${usedLLM})`);
6299
- if (usedLLM) {
6300
- lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
6301
- const candidateMap = new Map(candidates.map((candidate, index) => [candidate.id, toRerank[index]]));
6302
- const rerankedTop = reranked.map((r2) => candidateMap.get(r2.id)).filter((e2) => e2 != null);
6303
- if (rerankedTop.length > 0) {
6304
- intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
6645
+ const httpGate = shouldAttemptHttpRerank({
6646
+ quality,
6647
+ hasQuery: Boolean(hasQuery),
6648
+ candidateCount: intermediate.length
6649
+ });
6650
+ const llmGateWithoutHttp = shouldAttemptLlmRerank({
6651
+ quality,
6652
+ tier,
6653
+ hasQuery: Boolean(hasQuery),
6654
+ candidateCount: intermediate.length,
6655
+ topScore: intermediate[0]?.score ?? 0,
6656
+ secondScore: intermediate[1]?.score ?? 0,
6657
+ httpRerankConfigured: false
6658
+ });
6659
+ if (httpGate || llmGateWithoutHttp) {
6660
+ const narrativeMap = /* @__PURE__ */ new Map();
6661
+ for (const hit of results.hits) {
6662
+ const doc = hit.document;
6663
+ narrativeMap.set(makeEntryKey(doc.projectId, doc.observationId), doc.narrative);
6664
+ }
6665
+ const RERANK_TOP_K = 5;
6666
+ const toRerank = intermediate.slice(0, RERANK_TOP_K);
6667
+ const candidates = toRerank.map((e2, index) => ({
6668
+ id: `r${index + 1}`,
6669
+ title: e2.title,
6670
+ type: e2.type,
6671
+ score: e2.score,
6672
+ narrative: narrativeMap.get(makeEntryKey(e2.projectId, e2.id))
6673
+ }));
6674
+ const RERANK_TIMEOUT_MS = parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
6675
+ const candidateMap = new Map(candidates.map((candidate, index) => [candidate.id, toRerank[index]]));
6676
+ const { isNeuralRerankEnabled: isNeuralRerankEnabled2, neuralRerankCandidates: neuralRerankCandidates2 } = await Promise.resolve().then(() => (init_rerank(), rerank_exports));
6677
+ const httpConfigured = isNeuralRerankEnabled2();
6678
+ if (httpConfigured && httpGate) {
6679
+ try {
6680
+ const neural = await withTimeout(
6681
+ neuralRerankCandidates2(originalQuery, candidates),
6682
+ RERANK_TIMEOUT_MS,
6683
+ "neural rerank"
6684
+ );
6685
+ if (neural && neural.length > 0) {
6686
+ const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
6687
+ if (rerankedTop.length > 0) {
6688
+ intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
6689
+ lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
6690
+ mark("rerank(usedNeural=true)");
6691
+ }
6305
6692
  }
6693
+ } catch {
6694
+ console.error("[memorix] neural rerank failed or timed out; keeping original order");
6306
6695
  }
6307
- } catch (error) {
6308
- console.error("[memorix] LLM rerank failed or timed out, using original order");
6696
+ } else if (!httpConfigured && llmGateWithoutHttp) {
6697
+ try {
6698
+ const { rerankResults: rerankResults2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
6699
+ const { reranked, usedLLM } = await withTimeout(
6700
+ rerankResults2(originalQuery, candidates),
6701
+ RERANK_TIMEOUT_MS,
6702
+ "LLM rerank"
6703
+ );
6704
+ mark(`rerank(usedLLM=${usedLLM})`);
6705
+ if (usedLLM) {
6706
+ lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
6707
+ const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
6708
+ if (rerankedTop.length > 0) {
6709
+ intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
6710
+ }
6711
+ }
6712
+ } catch {
6713
+ console.error("[memorix] LLM rerank failed or timed out, using original order");
6714
+ }
6715
+ } else {
6716
+ mark(`rerank(skipped,tier=${tier})`);
6309
6717
  }
6310
6718
  } else {
6311
6719
  mark(`rerank(skipped,tier=${tier})`);
@@ -6486,6 +6894,7 @@ var init_orama_store = __esm({
6486
6894
  init_project_affinity();
6487
6895
  init_intent_detector();
6488
6896
  init_query_expansion();
6897
+ init_policy();
6489
6898
  init_timeout();
6490
6899
  db = null;
6491
6900
  dbInitPromise = null;
@@ -7674,10 +8083,230 @@ 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/codegraph/ids.ts
7678
- import { createHash as createHash3 } from "crypto";
8307
+ import { createHash as createHash4 } from "crypto";
7679
8308
  function digest(input) {
7680
- return createHash3("sha256").update(input).digest("hex").slice(0, 16);
8309
+ return createHash4("sha256").update(input).digest("hex").slice(0, 16);
7681
8310
  }
7682
8311
  function normalizeCodePath(path21) {
7683
8312
  return path21.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -7712,7 +8341,7 @@ var store_exports = {};
7712
8341
  __export(store_exports, {
7713
8342
  CodeGraphStore: () => CodeGraphStore
7714
8343
  });
7715
- import { randomUUID as randomUUID3 } from "crypto";
8344
+ import { randomUUID as randomUUID4 } from "crypto";
7716
8345
  import { posix as pathPosix } from "path";
7717
8346
  function rowToFile(row) {
7718
8347
  return {
@@ -8429,7 +9058,7 @@ var init_store = __esm({
8429
9058
  * so an interrupted scan cannot be advertised as complete.
8430
9059
  */
8431
9060
  recordCodeStateSnapshot(input) {
8432
- const id = randomUUID3();
9061
+ const id = randomUUID4();
8433
9062
  let snapshot;
8434
9063
  const tx = this.db.transaction(() => {
8435
9064
  const previous = this.db.prepare(
@@ -8761,6 +9390,16 @@ async function bindObservationCodeRefsBestEffort(observation) {
8761
9390
  } catch {
8762
9391
  }
8763
9392
  }
9393
+ async function persistEvidenceCard(observation) {
9394
+ if (!projectDir) return;
9395
+ try {
9396
+ const evidence = new EvidenceCardStore();
9397
+ await evidence.init(projectDir);
9398
+ evidence.upsertObservation(observation);
9399
+ } catch (error) {
9400
+ console.error(`[memorix] Evidence card projection failed for obs-${observation.id}: ${error instanceof Error ? error.message : String(error)}`);
9401
+ }
9402
+ }
8764
9403
  function queueClaimDerivation(observation) {
8765
9404
  const dataDir = projectDir;
8766
9405
  const isExplicit = observation.sourceDetail === "explicit";
@@ -9107,6 +9746,7 @@ async function storeObservation(input) {
9107
9746
  }
9108
9747
  if (!persistedCallbackInvoked) await input.onPersisted?.(observation);
9109
9748
  await bindObservationCodeRefsBestEffort(observation);
9749
+ await persistEvidenceCard(observation);
9110
9750
  queueObservationQualification(observation);
9111
9751
  if (resolveObservationVisibility(observation) === "project") {
9112
9752
  queueClaimDerivation(observation);
@@ -9199,6 +9839,7 @@ async function upsertObservation(existing, input, now3) {
9199
9839
  await store.update(existing);
9200
9840
  }
9201
9841
  await bindObservationCodeRefsBestEffort(existing);
9842
+ await persistEvidenceCard(existing);
9202
9843
  queueObservationQualification(existing);
9203
9844
  if (resolveObservationVisibility(existing) === "project") {
9204
9845
  queueClaimDerivation(existing);
@@ -9332,6 +9973,7 @@ async function resolveObservations(ids, status = "resolved") {
9332
9973
  await Promise.all(changed.map((observation) => tx.update(observation)));
9333
9974
  });
9334
9975
  }
9976
+ for (const observation of changed) await persistEvidenceCard(observation);
9335
9977
  return { resolved, notFound };
9336
9978
  }
9337
9979
  function getProjectObservations(projectId) {
@@ -9669,6 +10311,7 @@ var init_observations = __esm({
9669
10311
  init_secret_filter();
9670
10312
  init_lifecycle();
9671
10313
  init_visibility();
10314
+ init_evidence_store();
9672
10315
  observations = [];
9673
10316
  nextId = 1;
9674
10317
  projectDir = null;
@@ -9686,7 +10329,7 @@ var init_observations = __esm({
9686
10329
  });
9687
10330
 
9688
10331
  // src/knowledge/workspace-store.ts
9689
- import { randomUUID as randomUUID4 } from "crypto";
10332
+ import { randomUUID as randomUUID5 } from "crypto";
9690
10333
  function arrayValue(raw) {
9691
10334
  if (typeof raw !== "string" || !raw) return [];
9692
10335
  try {
@@ -9827,7 +10470,7 @@ var init_workspace_store = __esm({
9827
10470
  }
9828
10471
  createProposal(input) {
9829
10472
  const proposal = {
9830
- id: input.id ?? randomUUID4(),
10473
+ id: input.id ?? randomUUID5(),
9831
10474
  ...input,
9832
10475
  status: input.status ?? "pending",
9833
10476
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -9880,13 +10523,13 @@ __export(workspace_exports, {
9880
10523
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
9881
10524
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
9882
10525
  });
9883
- import { createHash as createHash4 } from "crypto";
10526
+ import { createHash as createHash5 } from "crypto";
9884
10527
  import { execFile } from "child_process";
9885
10528
  import { promises as fs6 } from "fs";
9886
10529
  import path9 from "path";
9887
10530
  import { promisify } from "util";
9888
10531
  function hash(value) {
9889
- return createHash4("sha256").update(value).digest("hex");
10532
+ return createHash5("sha256").update(value).digest("hex");
9890
10533
  }
9891
10534
  function workspaceId(projectId, mode) {
9892
10535
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -10482,7 +11125,7 @@ Respond in JSON only:
10482
11125
  });
10483
11126
 
10484
11127
  // src/media/media-store.ts
10485
- import { randomUUID as randomUUID5 } from "crypto";
11128
+ import { randomUUID as randomUUID6 } from "crypto";
10486
11129
  function asOptionalText(value) {
10487
11130
  return typeof value === "string" && value.length > 0 ? value : void 0;
10488
11131
  }
@@ -10652,7 +11295,7 @@ var init_media_store = __esm({
10652
11295
  return { asset: this.getAsset(input.projectId, existing.id), deduplicated: true };
10653
11296
  }
10654
11297
  const asset = {
10655
- id: randomUUID5(),
11298
+ id: randomUUID6(),
10656
11299
  ...input,
10657
11300
  createdAt: now3,
10658
11301
  updatedAt: now3
@@ -10732,7 +11375,7 @@ var init_media_store = __esm({
10732
11375
  `).get(input.assetId, input.observationId ?? null, input.role);
10733
11376
  if (existing) return rowToLink(existing);
10734
11377
  const link = {
10735
- id: randomUUID5(),
11378
+ id: randomUUID6(),
10736
11379
  assetId: input.assetId,
10737
11380
  projectId: input.projectId,
10738
11381
  ...input.observationId !== void 0 ? { observationId: input.observationId } : {},
@@ -10771,7 +11414,7 @@ var init_media_store = __esm({
10771
11414
  return rowToDerivation(this.db.prepare("SELECT * FROM media_derivations WHERE id = ?").get(existing.id));
10772
11415
  }
10773
11416
  const derivation = {
10774
- id: randomUUID5(),
11417
+ id: randomUUID6(),
10775
11418
  ...input,
10776
11419
  content,
10777
11420
  ...error ? { error } : {},
@@ -10858,7 +11501,7 @@ var init_media_store = __esm({
10858
11501
  createJob(input) {
10859
11502
  const now3 = Date.now();
10860
11503
  const job = {
10861
- id: randomUUID5(),
11504
+ id: randomUUID6(),
10862
11505
  projectId: input.projectId,
10863
11506
  kind: input.kind,
10864
11507
  status: "queued",
@@ -11020,14 +11663,14 @@ var init_types2 = __esm({
11020
11663
  });
11021
11664
 
11022
11665
  // src/media/asset-store.ts
11023
- import { createHash as createHash5, randomUUID as randomUUID6 } from "crypto";
11666
+ import { createHash as createHash6, randomUUID as randomUUID7 } from "crypto";
11024
11667
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
11025
11668
  import path10 from "path";
11026
11669
  function mediaRoot(dataDir) {
11027
11670
  return path10.resolve(dataDir, "media");
11028
11671
  }
11029
11672
  function projectStorageKey(projectId) {
11030
- return createHash5("sha256").update(projectId).digest("hex").slice(0, 16);
11673
+ return createHash6("sha256").update(projectId).digest("hex").slice(0, 16);
11031
11674
  }
11032
11675
  function validateUnderRoot(root, candidate) {
11033
11676
  const resolved = path10.resolve(candidate);
@@ -11091,7 +11734,7 @@ async function destinationExists(destination) {
11091
11734
  async function persistBuffer(bytes, destination) {
11092
11735
  await mkdir3(path10.dirname(destination), { recursive: true });
11093
11736
  if (await destinationExists(destination)) return false;
11094
- const temporary = `${destination}.${randomUUID6()}.tmp`;
11737
+ const temporary = `${destination}.${randomUUID7()}.tmp`;
11095
11738
  try {
11096
11739
  await writeFile3(temporary, bytes, { flag: "wx" });
11097
11740
  await rename2(temporary, destination);
@@ -11136,7 +11779,7 @@ async function importMediaBuffer(input) {
11136
11779
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
11137
11780
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
11138
11781
  const detected = detectMedia(input.bytes.subarray(0, 32));
11139
- const sha256 = createHash5("sha256").update(input.bytes).digest("hex");
11782
+ const sha256 = createHash6("sha256").update(input.bytes).digest("hex");
11140
11783
  return importKnownMedia({
11141
11784
  dataDir: input.dataDir,
11142
11785
  projectId: input.projectId,
@@ -11154,7 +11797,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
11154
11797
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
11155
11798
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
11156
11799
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
11157
- const sha256 = createHash5("sha256").update(bytes).digest("hex");
11800
+ const sha256 = createHash6("sha256").update(bytes).digest("hex");
11158
11801
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
11159
11802
  return bytes;
11160
11803
  }
@@ -63905,7 +64548,7 @@ var require_gaxios = __commonJS({
63905
64548
  var retry_js_1 = require_retry3();
63906
64549
  var stream_1 = __require("stream");
63907
64550
  var interceptor_js_1 = require_interceptor();
63908
- var randomUUID14 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
64551
+ var randomUUID16 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
63909
64552
  var HTTP_STATUS_NO_CONTENT = 204;
63910
64553
  var Gaxios = class {
63911
64554
  agentCache = /* @__PURE__ */ new Map();
@@ -64178,7 +64821,7 @@ var require_gaxios = __commonJS({
64178
64821
  */
64179
64822
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
64180
64823
  if (opts.multipart?.length) {
64181
- const boundary = await randomUUID14();
64824
+ const boundary = await randomUUID16();
64182
64825
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
64183
64826
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
64184
64827
  } else if (shouldDirectlyPassData) {
@@ -76405,7 +77048,7 @@ var require_websocket = __commonJS({
76405
77048
  var http3 = __require("http");
76406
77049
  var net = __require("net");
76407
77050
  var tls = __require("tls");
76408
- var { randomBytes, createHash: createHash13 } = __require("crypto");
77051
+ var { randomBytes, createHash: createHash14 } = __require("crypto");
76409
77052
  var { Duplex, Readable: Readable2 } = __require("stream");
76410
77053
  var { URL: URL2 } = __require("url");
76411
77054
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -77073,7 +77716,7 @@ var require_websocket = __commonJS({
77073
77716
  abortHandshake(websocket, socket, "Invalid Upgrade header");
77074
77717
  return;
77075
77718
  }
77076
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
77719
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
77077
77720
  if (res.headers["sec-websocket-accept"] !== digest2) {
77078
77721
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
77079
77722
  return;
@@ -77445,7 +78088,7 @@ var require_websocket_server = __commonJS({
77445
78088
  var EventEmitter = __require("events");
77446
78089
  var http3 = __require("http");
77447
78090
  var { Duplex } = __require("stream");
77448
- var { createHash: createHash13 } = __require("crypto");
78091
+ var { createHash: createHash14 } = __require("crypto");
77449
78092
  var extension3 = require_extension();
77450
78093
  var PerMessageDeflate2 = require_permessage_deflate();
77451
78094
  var subprotocol2 = require_subprotocol();
@@ -77752,7 +78395,7 @@ var require_websocket_server = __commonJS({
77752
78395
  );
77753
78396
  }
77754
78397
  if (this._state > RUNNING) return abortHandshake(socket, 503);
77755
- const digest2 = createHash13("sha1").update(key + GUID).digest("base64");
78398
+ const digest2 = createHash14("sha1").update(key + GUID).digest("base64");
77756
78399
  const headers = [
77757
78400
  "HTTP/1.1 101 Switching Protocols",
77758
78401
  "Upgrade: websocket",
@@ -144890,6 +145533,186 @@ var init_audio_jobs = __esm({
144890
145533
  }
144891
145534
  });
144892
145535
 
145536
+ // src/memory/feedback-model.ts
145537
+ function feedbackWeightMultiplier(weight) {
145538
+ return Math.max(0.35, Math.min(1.4, 0.35 + Math.max(0, Math.min(2, weight)) * 0.525));
145539
+ }
145540
+ var init_feedback_model = __esm({
145541
+ "src/memory/feedback-model.ts"() {
145542
+ "use strict";
145543
+ init_esm_shims();
145544
+ }
145545
+ });
145546
+
145547
+ // src/memory/feedback.ts
145548
+ var feedback_exports = {};
145549
+ __export(feedback_exports, {
145550
+ FEEDBACK_DELTAS: () => FEEDBACK_DELTAS,
145551
+ MemoryFeedbackStore: () => MemoryFeedbackStore,
145552
+ applyMemoryFeedback: () => applyMemoryFeedback,
145553
+ feedbackWeightMultiplier: () => feedbackWeightMultiplier
145554
+ });
145555
+ import { randomUUID as randomUUID8 } from "crypto";
145556
+ function applyMemoryFeedback(current, event) {
145557
+ const delta = event.signal === "revoke" ? 0 : FEEDBACK_DELTAS[event.signal];
145558
+ const weight = Math.max(0, Math.min(2, current.weight + delta));
145559
+ const status = (event.signal === "code-conflict" || event.signal === "user-correction" || event.signal === "weaken") && weight < 0.5 ? "archived" : current.status;
145560
+ return { weight, status, audit: [...current.audit, { ...event }] };
145561
+ }
145562
+ function rowToEvent(row) {
145563
+ return {
145564
+ id: String(row.id),
145565
+ projectId: String(row.project_id),
145566
+ candidateKind: row.candidate_kind,
145567
+ candidateId: String(row.candidate_id),
145568
+ signal: row.signal,
145569
+ ...row.observed_at ? { at: String(row.observed_at) } : { at: (/* @__PURE__ */ new Date()).toISOString() },
145570
+ ...row.actor ? { actor: String(row.actor) } : {},
145571
+ ...row.note ? { note: String(row.note) } : {},
145572
+ ...row.source_ref ? { sourceRef: String(row.source_ref) } : {},
145573
+ ...row.target_event_id ? { targetEventId: String(row.target_event_id) } : {},
145574
+ delta: Number(row.delta ?? 0)
145575
+ };
145576
+ }
145577
+ var FEEDBACK_DELTAS, MemoryFeedbackStore;
145578
+ var init_feedback = __esm({
145579
+ "src/memory/feedback.ts"() {
145580
+ "use strict";
145581
+ init_esm_shims();
145582
+ init_sqlite_db();
145583
+ init_feedback_model();
145584
+ FEEDBACK_DELTAS = {
145585
+ "user-correction": -0.35,
145586
+ "verification-success": 0.25,
145587
+ "verification-failure": -0.25,
145588
+ "code-change": -0.15,
145589
+ used: 0.1,
145590
+ "not-used": -0.05,
145591
+ "code-conflict": -0.4,
145592
+ strengthen: 0.2,
145593
+ weaken: -0.2
145594
+ };
145595
+ MemoryFeedbackStore = class {
145596
+ db = null;
145597
+ async init(dataDir) {
145598
+ this.db = getDatabase(dataDir);
145599
+ }
145600
+ requireDb() {
145601
+ if (!this.db) throw new Error("MemoryFeedbackStore is not initialized.");
145602
+ return this.db;
145603
+ }
145604
+ loadEvents(projectId, candidateKind, candidateId) {
145605
+ return this.requireDb().prepare(`
145606
+ SELECT * FROM memory_feedback_events
145607
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
145608
+ ORDER BY observed_at ASC, rowid ASC
145609
+ `).all(projectId, candidateKind, candidateId).map(rowToEvent);
145610
+ }
145611
+ rebuild(projectId, candidateKind, candidateId) {
145612
+ const events = this.loadEvents(projectId, candidateKind, candidateId);
145613
+ const revoked = new Set(events.filter((event) => event.signal === "revoke" && event.targetEventId).map((event) => event.targetEventId));
145614
+ let state = { weight: 1, status: "active", audit: [] };
145615
+ for (const event of events) {
145616
+ if (event.signal === "revoke" || revoked.has(event.id)) {
145617
+ state.audit.push(event);
145618
+ continue;
145619
+ }
145620
+ state = applyMemoryFeedback(state, event);
145621
+ }
145622
+ this.requireDb().prepare(`
145623
+ INSERT INTO memory_feedback_states (project_id, candidate_kind, candidate_id, weight, lifecycle_status, updated_at)
145624
+ VALUES (?, ?, ?, ?, ?, ?)
145625
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
145626
+ weight = excluded.weight, lifecycle_status = excluded.lifecycle_status, updated_at = excluded.updated_at
145627
+ `).run(projectId, candidateKind, candidateId, state.weight, state.status, (/* @__PURE__ */ new Date()).toISOString());
145628
+ return state;
145629
+ }
145630
+ getState(projectId, candidateKind, candidateId) {
145631
+ const row = this.requireDb().prepare(`
145632
+ SELECT weight, lifecycle_status FROM memory_feedback_states
145633
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
145634
+ `).get(projectId, candidateKind, candidateId);
145635
+ if (!row) return { weight: 1, status: "active", audit: [] };
145636
+ return {
145637
+ weight: Number(row.weight ?? 1),
145638
+ status: row.lifecycle_status ?? "active",
145639
+ audit: this.loadEvents(projectId, candidateKind, candidateId)
145640
+ };
145641
+ }
145642
+ listStates(projectId, candidateKind = "observation") {
145643
+ return this.requireDb().prepare(`
145644
+ SELECT candidate_id, weight, lifecycle_status, updated_at
145645
+ FROM memory_feedback_states WHERE project_id = ? AND candidate_kind = ?
145646
+ ORDER BY updated_at DESC
145647
+ `).all(projectId, candidateKind).map((row) => ({
145648
+ candidateId: String(row.candidate_id),
145649
+ weight: Number(row.weight),
145650
+ status: row.lifecycle_status,
145651
+ updatedAt: String(row.updated_at)
145652
+ }));
145653
+ }
145654
+ weights(projectId, candidateKind = "observation") {
145655
+ const states = /* @__PURE__ */ new Map();
145656
+ for (const row of this.listStates(projectId, candidateKind)) {
145657
+ states.set(row.candidateId, this.getState(projectId, candidateKind, row.candidateId));
145658
+ }
145659
+ return states;
145660
+ }
145661
+ record(input) {
145662
+ if (input.signal === "revoke" && !input.targetEventId) {
145663
+ throw new Error("A revoke feedback event requires targetEventId.");
145664
+ }
145665
+ if (input.signal === "revoke") {
145666
+ const target = this.requireDb().prepare(`
145667
+ SELECT 1 FROM memory_feedback_events
145668
+ WHERE id = ? AND project_id = ? AND candidate_kind = ? AND candidate_id = ?
145669
+ `).get(input.targetEventId, input.projectId, input.candidateKind, input.candidateId);
145670
+ if (!target) throw new Error("The feedback event to revoke was not found in the same candidate scope.");
145671
+ }
145672
+ const event = {
145673
+ id: randomUUID8(),
145674
+ projectId: input.projectId,
145675
+ candidateKind: input.candidateKind,
145676
+ candidateId: input.candidateId,
145677
+ signal: input.signal,
145678
+ sourceRef: input.sourceRef,
145679
+ ...input.actor ? { actor: input.actor } : {},
145680
+ ...input.note ? { note: input.note.slice(0, 2e3) } : {},
145681
+ ...input.targetEventId ? { targetEventId: input.targetEventId } : {},
145682
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
145683
+ delta: input.signal === "revoke" ? 0 : FEEDBACK_DELTAS[input.signal]
145684
+ };
145685
+ this.requireDb().prepare(`
145686
+ INSERT INTO memory_feedback_events (
145687
+ id, project_id, candidate_kind, candidate_id, signal, actor, note,
145688
+ source_ref, target_event_id, delta, observed_at
145689
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
145690
+ `).run(
145691
+ event.id,
145692
+ event.projectId,
145693
+ event.candidateKind,
145694
+ event.candidateId,
145695
+ event.signal,
145696
+ event.actor ?? null,
145697
+ event.note ?? null,
145698
+ event.sourceRef,
145699
+ event.targetEventId ?? null,
145700
+ event.delta,
145701
+ event.at
145702
+ );
145703
+ return { event, state: this.rebuild(input.projectId, input.candidateKind, input.candidateId) };
145704
+ }
145705
+ audit(projectId, candidateKind, candidateId, limit2 = 100) {
145706
+ return this.requireDb().prepare(`
145707
+ SELECT * FROM memory_feedback_events
145708
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
145709
+ ORDER BY observed_at DESC, rowid DESC LIMIT ?
145710
+ `).all(projectId, candidateKind, candidateId, Math.min(200, Math.max(1, Math.floor(limit2)))).map(rowToEvent);
145711
+ }
145712
+ };
145713
+ }
145714
+ });
145715
+
144893
145716
  // src/memory/retention.ts
144894
145717
  var retention_exports = {};
144895
145718
  __export(retention_exports, {
@@ -144931,6 +145754,7 @@ function getEffectiveRetentionDays(doc) {
144931
145754
  }
144932
145755
  function isImmune(doc) {
144933
145756
  if (doc.type === "probe") return false;
145757
+ if (doc.feedbackStatus === "archived") return false;
144934
145758
  if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
144935
145759
  if (doc.valueCategory === "core") return true;
144936
145760
  const importance = getImportanceLevel(doc);
@@ -144963,7 +145787,8 @@ function calculateRelevance(doc, referenceTime) {
144963
145787
  const decayFactor = Math.exp(-ageDays / retention);
144964
145788
  const accessCount = doc.accessCount ?? 0;
144965
145789
  const accessBoost = Math.min(2, 1 + 0.1 * accessCount);
144966
- let totalScore = base * decayFactor * accessBoost;
145790
+ const feedbackMultiplier = doc.feedbackWeight === void 0 ? 1 : feedbackWeightMultiplier(doc.feedbackWeight);
145791
+ let totalScore = base * decayFactor * accessBoost * feedbackMultiplier;
144967
145792
  const immune = isImmune(doc);
144968
145793
  if (immune) {
144969
145794
  totalScore = Math.max(totalScore, 0.5);
@@ -144975,7 +145800,8 @@ function calculateRelevance(doc, referenceTime) {
144975
145800
  decayFactor,
144976
145801
  accessBoost,
144977
145802
  ageDays,
144978
- isImmune: immune
145803
+ isImmune: immune,
145804
+ feedbackMultiplier
144979
145805
  };
144980
145806
  }
144981
145807
  function rankByRelevance(docs, referenceTime) {
@@ -144983,6 +145809,7 @@ function rankByRelevance(docs, referenceTime) {
144983
145809
  }
144984
145810
  function getRetentionZone(doc, referenceTime) {
144985
145811
  const now3 = referenceTime ?? /* @__PURE__ */ new Date();
145812
+ if (doc.feedbackStatus === "archived") return "archive-candidate";
144986
145813
  const importance = getImportanceLevel(doc);
144987
145814
  const retention = getEffectiveRetentionDays(doc);
144988
145815
  const createdAt = new Date(doc.createdAt);
@@ -145054,7 +145881,7 @@ function explainRetention(doc, referenceTime) {
145054
145881
  summary: parts.join(" ; ")
145055
145882
  };
145056
145883
  }
145057
- function toRetentionDocument(obs, accessMap) {
145884
+ function toRetentionDocument(obs, accessMap, feedback) {
145058
145885
  const access2 = accessMap?.get(obs.id);
145059
145886
  return {
145060
145887
  id: `obs-${obs.id}`,
@@ -145076,11 +145903,12 @@ function toRetentionDocument(obs, accessMap) {
145076
145903
  sourceDetail: obs.sourceDetail ?? "",
145077
145904
  valueCategory: obs.valueCategory ?? "",
145078
145905
  admissionState: obs.admissionState ?? "",
145079
- admissionReason: obs.admissionReason ?? ""
145906
+ admissionReason: obs.admissionReason ?? "",
145907
+ ...feedback ? { feedbackWeight: feedback.weight, feedbackStatus: feedback.status } : {}
145080
145908
  };
145081
145909
  }
145082
145910
  function projectObservationRetention(observation, options = {}) {
145083
- const document2 = toRetentionDocument(observation, options.accessMap);
145911
+ const document2 = toRetentionDocument(observation, options.accessMap, options.feedback);
145084
145912
  const relevance = calculateRelevance(document2, options.referenceTime);
145085
145913
  const explanation = explainRetention(document2, options.referenceTime);
145086
145914
  return {
@@ -145105,7 +145933,17 @@ function summarizeRetentionProjections(projections) {
145105
145933
  }
145106
145934
  return { active, stale, archiveCandidates, immune };
145107
145935
  }
145108
- async function archiveExpiredBatch(_projectDir, options) {
145936
+ async function loadFeedbackStore(dataDir) {
145937
+ try {
145938
+ const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
145939
+ const store = new MemoryFeedbackStore2();
145940
+ await store.init(dataDir);
145941
+ return store;
145942
+ } catch {
145943
+ return void 0;
145944
+ }
145945
+ }
145946
+ async function archiveExpiredBatch(projectDir2, options) {
145109
145947
  const store = getObservationStore();
145110
145948
  const limit2 = Math.min(500, Math.max(1, Math.floor(options.limit ?? 100)));
145111
145949
  const afterId = Math.max(0, Math.floor(options.afterId ?? 0));
@@ -145114,11 +145952,13 @@ async function archiveExpiredBatch(_projectDir, options) {
145114
145952
  afterId,
145115
145953
  limit: limit2 + 1
145116
145954
  });
145955
+ const feedback = await loadFeedbackStore(projectDir2);
145117
145956
  const hasMore = page.length > limit2;
145118
145957
  const scanned = hasMore ? page.slice(0, limit2) : page;
145119
145958
  const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => projectObservationRetention(observation, {
145120
145959
  accessMap: options.accessMap,
145121
- referenceTime: options.referenceTime
145960
+ referenceTime: options.referenceTime,
145961
+ ...feedback ? { feedback: feedback.getState(options.projectId, "observation", String(observation.id)) } : {}
145122
145962
  }).zone === "archive-candidate").map((observation) => observation.id);
145123
145963
  const archived = candidateIds.length === 0 ? 0 : await store.atomic(async (tx) => {
145124
145964
  const updates = await Promise.all(
@@ -145149,6 +145989,7 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
145149
145989
  const remaining = reader ? remainingObservations.filter((observation) => canManageObservation(observation, reader)).length : remainingObservations.length;
145150
145990
  return { archived, remaining };
145151
145991
  }
145992
+ const feedback = await loadFeedbackStore(projectDir2);
145152
145993
  return await store.atomic(async (tx) => {
145153
145994
  const allObs = await tx.loadAll();
145154
145995
  const activeObs = allObs.filter(
@@ -145156,7 +145997,11 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
145156
145997
  );
145157
145998
  const archivedIds = [];
145158
145999
  for (const obs of activeObs) {
145159
- const retention = projectObservationRetention(obs, { accessMap, referenceTime });
146000
+ const retention = projectObservationRetention(obs, {
146001
+ accessMap,
146002
+ referenceTime,
146003
+ ...feedback ? { feedback: feedback.getState(obs.projectId, "observation", String(obs.id)) } : {}
146004
+ });
145160
146005
  if (retention.zone === "archive-candidate") {
145161
146006
  archivedIds.push(obs.id);
145162
146007
  }
@@ -145176,6 +146021,7 @@ var init_retention = __esm({
145176
146021
  init_esm_shims();
145177
146022
  init_obs_store();
145178
146023
  init_visibility();
146024
+ init_feedback_model();
145179
146025
  RETENTION_DAYS = {
145180
146026
  critical: 365,
145181
146027
  high: 180,
@@ -145212,7 +146058,7 @@ var init_retention = __esm({
145212
146058
  });
145213
146059
 
145214
146060
  // src/memory/owner.ts
145215
- import { randomUUID as randomUUID7 } from "crypto";
146061
+ import { randomUUID as randomUUID9 } from "crypto";
145216
146062
  import { promises as fs9 } from "fs";
145217
146063
  import path16 from "path";
145218
146064
  function ownerPath(dataDir) {
@@ -145242,7 +146088,7 @@ async function resolveLocalMemoryOwner(dataDir, options = {}) {
145242
146088
  await fs9.mkdir(path16.dirname(filePath), { recursive: true });
145243
146089
  const created = {
145244
146090
  version: 1,
145245
- id: "local-" + randomUUID7(),
146091
+ id: "local-" + randomUUID9(),
145246
146092
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
145247
146093
  };
145248
146094
  try {
@@ -145265,7 +146111,7 @@ var init_owner = __esm({
145265
146111
  });
145266
146112
 
145267
146113
  // src/memory/long-term-store.ts
145268
- import { randomUUID as randomUUID8 } from "crypto";
146114
+ import { randomUUID as randomUUID10 } from "crypto";
145269
146115
  function optionalText2(value) {
145270
146116
  return typeof value === "string" && value.trim() ? value : void 0;
145271
146117
  }
@@ -145316,7 +146162,7 @@ function rowToEvidence(row) {
145316
146162
  createdAt: row.createdAt
145317
146163
  };
145318
146164
  }
145319
- function rowToEvent(row) {
146165
+ function rowToEvent2(row) {
145320
146166
  return {
145321
146167
  id: row.id,
145322
146168
  memoryId: row.memoryId,
@@ -145425,7 +146271,7 @@ var init_long_term_store = __esm({
145425
146271
  }
145426
146272
  insertEvidence(input) {
145427
146273
  const evidence = {
145428
- id: randomUUID8(),
146274
+ id: randomUUID10(),
145429
146275
  ...input,
145430
146276
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
145431
146277
  };
@@ -145452,7 +146298,7 @@ var init_long_term_store = __esm({
145452
146298
  }
145453
146299
  recordEvent(input) {
145454
146300
  const event = {
145455
- id: randomUUID8(),
146301
+ id: randomUUID10(),
145456
146302
  ...input,
145457
146303
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
145458
146304
  };
@@ -145473,7 +146319,7 @@ var init_long_term_store = __esm({
145473
146319
  listEvents(memoryId) {
145474
146320
  return this.db.prepare(`
145475
146321
  SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
145476
- `).all(memoryId).map(rowToEvent);
146322
+ `).all(memoryId).map(rowToEvent2);
145477
146323
  }
145478
146324
  markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
145479
146325
  const unique = [...new Set(ids.filter(Boolean))];
@@ -145542,7 +146388,7 @@ __export(long_term_exports, {
145542
146388
  selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
145543
146389
  supersedeLongTermMemory: () => supersedeLongTermMemory
145544
146390
  });
145545
- import { randomUUID as randomUUID9 } from "crypto";
146391
+ import { randomUUID as randomUUID11 } from "crypto";
145546
146392
  function isOneOf(value, values) {
145547
146393
  return typeof value === "string" && values.includes(value);
145548
146394
  }
@@ -145724,7 +146570,7 @@ async function createManualLongTermMemory(input) {
145724
146570
  const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
145725
146571
  if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
145726
146572
  assertScope(input.scope);
145727
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID9(), relation: "supports" }];
146573
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID11(), relation: "supports" }];
145728
146574
  return createLongTermMemoryWithDataDir({
145729
146575
  dataDir: input.dataDir,
145730
146576
  originProjectId: input.projectId,
@@ -145779,7 +146625,7 @@ async function createLongTermMemoryWithDataDir(input) {
145779
146625
  assertPortableEvidence(input.scope, portability, evidenceInputs);
145780
146626
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
145781
146627
  const memory = {
145782
- id: randomUUID9(),
146628
+ id: randomUUID11(),
145783
146629
  originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
145784
146630
  ownerId: compactText(input.ownerId, "owner id", 160),
145785
146631
  scope: input.scope,
@@ -146098,7 +146944,7 @@ var init_long_term = __esm({
146098
146944
 
146099
146945
  // src/codegraph/code-state.ts
146100
146946
  import { execFile as execFile2 } from "child_process";
146101
- import { createHash as createHash6 } from "crypto";
146947
+ import { createHash as createHash7 } from "crypto";
146102
146948
  import { promisify as promisify3 } from "util";
146103
146949
  async function runGit(rootPath, args) {
146104
146950
  try {
@@ -146127,7 +146973,7 @@ function changedPathCount(status) {
146127
146973
  return count2;
146128
146974
  }
146129
146975
  function fingerprint(baseRevision, status) {
146130
- return createHash6("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
146976
+ return createHash7("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
146131
146977
  }
146132
146978
  async function collectCodeStateSnapshot(input) {
146133
146979
  const [revision, status] = await Promise.all([
@@ -146227,11 +147073,11 @@ __export(lite_provider_exports, {
146227
147073
  indexProjectLite: () => indexProjectLite,
146228
147074
  refreshProjectLite: () => refreshProjectLite
146229
147075
  });
146230
- import { createHash as createHash7 } from "crypto";
147076
+ import { createHash as createHash8 } from "crypto";
146231
147077
  import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
146232
147078
  import { join as join4, relative } from "path";
146233
147079
  function hashText(text2) {
146234
- return createHash7("sha256").update(text2).digest("hex");
147080
+ return createHash8("sha256").update(text2).digest("hex");
146235
147081
  }
146236
147082
  function extension2(path21) {
146237
147083
  const index = path21.lastIndexOf(".");
@@ -146641,7 +147487,7 @@ var claim_store_exports = {};
146641
147487
  __export(claim_store_exports, {
146642
147488
  ClaimStore: () => ClaimStore
146643
147489
  });
146644
- import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
147490
+ import { createHash as createHash9, randomUUID as randomUUID12 } from "crypto";
146645
147491
  function optionalText3(value) {
146646
147492
  return typeof value === "string" && value ? value : void 0;
146647
147493
  }
@@ -146680,7 +147526,7 @@ function rowToEvidence2(row) {
146680
147526
  createdAt: row.createdAt
146681
147527
  };
146682
147528
  }
146683
- function rowToEvent2(row) {
147529
+ function rowToEvent3(row) {
146684
147530
  return {
146685
147531
  id: row.id,
146686
147532
  projectId: row.projectId,
@@ -146694,7 +147540,7 @@ function rowToEvent2(row) {
146694
147540
  };
146695
147541
  }
146696
147542
  function evidenceKey(input) {
146697
- return createHash8("sha256").update(JSON.stringify([
147543
+ return createHash9("sha256").update(JSON.stringify([
146698
147544
  input.evidenceKind,
146699
147545
  input.evidenceId,
146700
147546
  input.relation,
@@ -146825,7 +147671,7 @@ var init_claim_store = __esm({
146825
147671
  locator, capturedHash, evidenceKey, createdAt
146826
147672
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
146827
147673
  `).run(
146828
- randomUUID10(),
147674
+ randomUUID12(),
146829
147675
  input.claimId,
146830
147676
  input.evidenceKind,
146831
147677
  input.evidenceId,
@@ -146847,7 +147693,7 @@ var init_claim_store = __esm({
146847
147693
  }
146848
147694
  recordEvent(input) {
146849
147695
  const event = {
146850
- id: randomUUID10(),
147696
+ id: randomUUID12(),
146851
147697
  ...input,
146852
147698
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
146853
147699
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -146875,7 +147721,7 @@ var init_claim_store = __esm({
146875
147721
  SELECT * FROM knowledge_claim_events
146876
147722
  WHERE claimId = ?
146877
147723
  ORDER BY createdAt ASC, id
146878
- `).all(claimId).map(rowToEvent2);
147724
+ `).all(claimId).map(rowToEvent3);
146879
147725
  }
146880
147726
  };
146881
147727
  }
@@ -146892,7 +147738,7 @@ __export(claims_exports, {
146892
147738
  supersedeClaim: () => supersedeClaim,
146893
147739
  writeClaim: () => writeClaim
146894
147740
  });
146895
- import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
147741
+ import { createHash as createHash10, randomUUID as randomUUID13 } from "crypto";
146896
147742
  function compactText2(value, field) {
146897
147743
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
146898
147744
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -146904,7 +147750,7 @@ function normalizedTerm(value) {
146904
147750
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
146905
147751
  }
146906
147752
  function hashIdentity(parts) {
146907
- return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
147753
+ return createHash10("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
146908
147754
  }
146909
147755
  function buildClaimIdentity(input) {
146910
147756
  const subject = normalizedTerm(input.subject);
@@ -146978,7 +147824,7 @@ function normalizeClaimInput(input) {
146978
147824
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
146979
147825
  return {
146980
147826
  claim: {
146981
- id: randomUUID11(),
147827
+ id: randomUUID13(),
146982
147828
  projectId,
146983
147829
  subject,
146984
147830
  predicate,
@@ -147145,7 +147991,7 @@ function supersedeClaim(store, input) {
147145
147991
  });
147146
147992
  }
147147
147993
  function hashObservation(observation) {
147148
- return createHash9("sha256").update(JSON.stringify([
147994
+ return createHash10("sha256").update(JSON.stringify([
147149
147995
  observation.title,
147150
147996
  observation.narrative,
147151
147997
  observation.facts,
@@ -147389,11 +148235,11 @@ var init_frontmatter = __esm({
147389
148235
  });
147390
148236
 
147391
148237
  // src/knowledge/markdown.ts
147392
- import { createHash as createHash10 } from "crypto";
148238
+ import { createHash as createHash11 } from "crypto";
147393
148239
  import { promises as fs10 } from "fs";
147394
148240
  import path17 from "path";
147395
148241
  function hashContent(content) {
147396
- return createHash10("sha256").update(content).digest("hex");
148242
+ return createHash11("sha256").update(content).digest("hex");
147397
148243
  }
147398
148244
  function stringField(data, key) {
147399
148245
  const value = data[key];
@@ -147519,11 +148365,11 @@ __export(wiki_exports, {
147519
148365
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
147520
148366
  readKnowledgePage: () => readKnowledgePage
147521
148367
  });
147522
- import { createHash as createHash11 } from "crypto";
148368
+ import { createHash as createHash12 } from "crypto";
147523
148369
  import { promises as fs11 } from "fs";
147524
148370
  import path18 from "path";
147525
148371
  function hash2(value) {
147526
- return createHash11("sha256").update(value).digest("hex");
148372
+ return createHash12("sha256").update(value).digest("hex");
147527
148373
  }
147528
148374
  function now() {
147529
148375
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -148063,7 +148909,7 @@ var init_workflow_sync = __esm({
148063
148909
  });
148064
148910
 
148065
148911
  // src/knowledge/workflow-store.ts
148066
- import { randomUUID as randomUUID12 } from "crypto";
148912
+ import { randomUUID as randomUUID14 } from "crypto";
148067
148913
  function parseJson(value, fallback) {
148068
148914
  if (typeof value !== "string" || !value) return fallback;
148069
148915
  try {
@@ -148163,7 +149009,7 @@ var init_workflow_store = __esm({
148163
149009
  }
148164
149010
  recordRun(input) {
148165
149011
  const run = {
148166
- id: randomUUID12(),
149012
+ id: randomUUID14(),
148167
149013
  workflowId: input.workflowId,
148168
149014
  projectId: input.projectId,
148169
149015
  task: input.task.trim(),
@@ -148231,7 +149077,7 @@ var init_outcome_types = __esm({
148231
149077
  });
148232
149078
 
148233
149079
  // src/knowledge/outcome-store.ts
148234
- import { randomUUID as randomUUID13 } from "crypto";
149080
+ import { randomUUID as randomUUID15 } from "crypto";
148235
149081
  function optionalText5(value) {
148236
149082
  return typeof value === "string" && value.trim() ? value : void 0;
148237
149083
  }
@@ -148275,7 +149121,7 @@ var init_outcome_store = __esm({
148275
149121
  }
148276
149122
  record(input) {
148277
149123
  const signal = {
148278
- id: randomUUID13(),
149124
+ id: randomUUID15(),
148279
149125
  projectId: text(input.projectId, "project id"),
148280
149126
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
148281
149127
  candidateId: text(input.candidateId, "candidate id"),
@@ -148380,11 +149226,11 @@ __export(workflows_exports, {
148380
149226
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
148381
149227
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
148382
149228
  });
148383
- import { createHash as createHash12 } from "crypto";
149229
+ import { createHash as createHash13 } from "crypto";
148384
149230
  import { promises as fs12 } from "fs";
148385
149231
  import path19 from "path";
148386
149232
  function hash3(value) {
148387
- return createHash12("sha256").update(value).digest("hex");
149233
+ return createHash13("sha256").update(value).digest("hex");
148388
149234
  }
148389
149235
  function now2() {
148390
149236
  return (/* @__PURE__ */ new Date()).toISOString();