memorix 1.9.5 → 1.9.6

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 (62) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +6 -1
  3. package/README.zh-CN.md +6 -1
  4. package/dist/cli/index.js +1647 -264
  5. package/dist/index.d.ts +12 -7
  6. package/dist/index.js +952 -182
  7. package/dist/maintenance-runner.js +243 -103
  8. package/dist/media-audio-runner.js +243 -103
  9. package/dist/media-video-runner.js +243 -103
  10. package/dist/memcode-runtime/CHANGELOG.md +42 -0
  11. package/dist/sdk.js +871 -157
  12. package/dist/vector-backfill-runner.js +243 -103
  13. package/docs/API_REFERENCE.md +36 -0
  14. package/docs/CONFIGURATION.md +43 -0
  15. package/docs/DEVELOPMENT.md +1 -1
  16. package/docs/KNOWN_ISSUES_AND_ROADMAP.md +49 -14
  17. package/docs/PERFORMANCE.md +46 -2
  18. package/docs/README.md +2 -1
  19. package/docs/SETUP.md +1 -1
  20. package/package.json +2 -1
  21. package/plugins/antigravity/memorix/plugin.json +1 -1
  22. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  23. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  24. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  25. package/plugins/copilot/memorix/plugin.json +1 -1
  26. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  27. package/plugins/hermes/memorix/plugin.yaml +1 -1
  28. package/plugins/omp/memorix/package.json +1 -1
  29. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  30. package/plugins/pi/memorix/package.json +1 -1
  31. package/server.json +2 -2
  32. package/src/cli/capability-map.ts +1 -0
  33. package/src/cli/command-guide.ts +13 -1
  34. package/src/cli/commands/context.ts +2 -0
  35. package/src/cli/commands/continuity.ts +155 -0
  36. package/src/cli/commands/init.ts +5 -2
  37. package/src/cli/commands/serve-http.ts +242 -42
  38. package/src/cli/index.ts +3 -1
  39. package/src/cli/tui/ConfigureView.tsx +2 -0
  40. package/src/codegraph/auto-context.ts +17 -0
  41. package/src/config/legacy-loader.ts +2 -2
  42. package/src/config/resolved-config.ts +8 -5
  43. package/src/config/yaml-loader.ts +20 -10
  44. package/src/embedding/api-provider.ts +4 -2
  45. package/src/embedding/fastembed-provider.ts +47 -14
  46. package/src/embedding/transformers-provider.ts +29 -7
  47. package/src/evaluation/memory-quality-gate.ts +90 -0
  48. package/src/hooks/official-skills.ts +2 -0
  49. package/src/knowledge/context-assembly.ts +1 -0
  50. package/src/knowledge/context-receipt.ts +15 -0
  51. package/src/knowledge/task-continuity.ts +413 -0
  52. package/src/knowledge/workset.ts +77 -0
  53. package/src/llm/provider.ts +2 -1
  54. package/src/orchestrate/coordinator.ts +60 -0
  55. package/src/orchestrate/effect-ledger.ts +160 -0
  56. package/src/orchestrate/pipeline-trace.ts +40 -6
  57. package/src/runtime/maintenance-jobs.ts +12 -6
  58. package/src/server/modern-mcp-bridge.ts +153 -27
  59. package/src/server/tool-profile.ts +3 -2
  60. package/src/server.ts +129 -5
  61. package/src/store/sqlite-db.ts +144 -4
  62. package/src/team/team-store.ts +62 -27
@@ -155,15 +155,20 @@ var init_dotenv_loader = __esm({
155
155
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
156
156
  import { homedir as homedir2 } from "os";
157
157
  import { parse as parseYamlDocument } from "yaml";
158
+ function cacheKey(homeDir, projectRoot) {
159
+ return `${homeDir}\0${projectRoot ?? ""}`;
160
+ }
158
161
  function initProjectRoot(root) {
159
162
  globalProjectRoot = root;
160
- configCache.delete(root);
163
+ configCache.delete(cacheKey(homedir2(), root));
161
164
  }
162
- function loadYamlConfig(projectRoot) {
165
+ function loadYamlConfig(projectRoot, options = {}) {
166
+ const homeDir = options.homeDir ?? homedir2();
163
167
  const resolvedRoot = projectRoot === null ? null : projectRoot ?? globalProjectRoot ?? null;
164
- const cached = configCache.get(resolvedRoot ?? null);
168
+ const key = cacheKey(homeDir, resolvedRoot);
169
+ const cached = configCache.get(key);
165
170
  if (cached) return cached;
166
- const userYaml = getGlobalYamlPath(homedir2());
171
+ const userYaml = getGlobalYamlPath(homeDir);
167
172
  const projectYaml = resolvedRoot ? getProjectYamlPath(resolvedRoot) : null;
168
173
  let userConfig = {};
169
174
  let projectConfig = {};
@@ -195,7 +200,7 @@ function loadYamlConfig(projectRoot) {
195
200
  server: { ...userConfig.server, ...projectConfig.server },
196
201
  team: { ...userConfig.team, ...projectConfig.team }
197
202
  };
198
- configCache.set(resolvedRoot ?? null, merged);
203
+ configCache.set(key, merged);
199
204
  return merged;
200
205
  }
201
206
  function parseYaml(content) {
@@ -572,11 +577,21 @@ function applySchemaMigrations(db2) {
572
577
  apply();
573
578
  }
574
579
  }
580
+ function normalizeDataDir(dataDir) {
581
+ return path3.resolve(dataDir);
582
+ }
583
+ function touchDatabase(normalized, db2) {
584
+ _dbCache.delete(normalized);
585
+ _dbCache.set(normalized, db2);
586
+ }
575
587
  function getDatabase(dataDir) {
576
- const normalized = path3.resolve(dataDir);
588
+ const normalized = normalizeDataDir(dataDir);
577
589
  assertNotHomeDataDir(normalized);
578
590
  const existing = _dbCache.get(normalized);
579
- if (existing) return existing;
591
+ if (existing) {
592
+ touchDatabase(normalized, existing);
593
+ return existing;
594
+ }
580
595
  loadBetterSqlite3();
581
596
  fs.mkdirSync(dataDir, { recursive: true });
582
597
  const dbPath = path3.join(dataDir, "memorix.db");
@@ -606,6 +621,8 @@ function getDatabase(dataDir) {
606
621
  db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
607
622
  db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
608
623
  db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
624
+ db2.exec(CREATE_TASK_CONTINUITY_EVENTS_TABLE);
625
+ db2.exec(CREATE_AGENT_EFFECTS_TABLE);
609
626
  try {
610
627
  db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
611
628
  } catch {
@@ -644,7 +661,7 @@ function getDatabase(dataDir) {
644
661
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
645
662
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
646
663
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
647
- _dbCache.set(normalized, db2);
664
+ touchDatabase(normalized, db2);
648
665
  return db2;
649
666
  } catch (error) {
650
667
  try {
@@ -662,8 +679,9 @@ function closeAllDatabases() {
662
679
  }
663
680
  _dbCache.delete(key);
664
681
  }
682
+ _dbLeases.clear();
665
683
  }
666
- 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;
684
+ 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_TASK_CONTINUITY_EVENTS_TABLE, CREATE_AGENT_EFFECTS_TABLE, CREATE_MCP_BINDINGS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache, _dbLeases;
667
685
  var init_sqlite_db = __esm({
668
686
  "src/store/sqlite-db.ts"() {
669
687
  "use strict";
@@ -1380,6 +1398,43 @@ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1380
1398
  delta REAL NOT NULL DEFAULT 0,
1381
1399
  observed_at TEXT NOT NULL
1382
1400
  );
1401
+ `;
1402
+ CREATE_TASK_CONTINUITY_EVENTS_TABLE = `
1403
+ CREATE TABLE IF NOT EXISTS task_continuity_events (
1404
+ id TEXT PRIMARY KEY,
1405
+ project_id TEXT NOT NULL,
1406
+ task_id TEXT NOT NULL,
1407
+ task TEXT NOT NULL,
1408
+ kind TEXT NOT NULL,
1409
+ content TEXT NOT NULL,
1410
+ item_status TEXT,
1411
+ source_ref TEXT,
1412
+ evidence_json TEXT NOT NULL DEFAULT '[]',
1413
+ actor TEXT,
1414
+ created_at TEXT NOT NULL,
1415
+ event_key TEXT,
1416
+ item_key TEXT,
1417
+ supersedes_id TEXT
1418
+ );
1419
+ `;
1420
+ CREATE_AGENT_EFFECTS_TABLE = `
1421
+ CREATE TABLE IF NOT EXISTS agent_effects (
1422
+ id TEXT PRIMARY KEY,
1423
+ pipeline_id TEXT NOT NULL,
1424
+ task_id TEXT NOT NULL,
1425
+ attempt INTEGER NOT NULL,
1426
+ effect_key TEXT NOT NULL,
1427
+ tool TEXT NOT NULL,
1428
+ risk_tier TEXT NOT NULL,
1429
+ replay_policy TEXT NOT NULL,
1430
+ status TEXT NOT NULL,
1431
+ call_id TEXT,
1432
+ idempotency_key TEXT NOT NULL,
1433
+ input_fingerprint TEXT,
1434
+ detail TEXT,
1435
+ started_at INTEGER NOT NULL,
1436
+ finished_at INTEGER
1437
+ );
1383
1438
  `;
1384
1439
  CREATE_MCP_BINDINGS_TABLE = `
1385
1440
  CREATE TABLE IF NOT EXISTS mcp_bindings (
@@ -1458,7 +1513,18 @@ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(projec
1458
1513
  CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1459
1514
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1460
1515
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1516
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_task ON task_continuity_events(project_id, task_id, created_at ASC);
1517
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_recent ON task_continuity_events(project_id, created_at DESC);
1518
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC);
1519
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
1520
+ ON task_continuity_events(project_id, task_id, event_key)
1521
+ WHERE event_key IS NOT NULL;
1522
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_effect_identity
1523
+ ON agent_effects(pipeline_id, task_id, attempt, effect_key);
1524
+ CREATE INDEX IF NOT EXISTS idx_agent_effect_pipeline ON agent_effects(pipeline_id, started_at ASC);
1461
1525
  CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1526
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC);
1527
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at);
1462
1528
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1463
1529
  ON maintenance_jobs(project_id, kind, dedupe_key)
1464
1530
  WHERE status IN ('pending', 'running', 'retry');
@@ -1629,6 +1695,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1629
1695
  db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1630
1696
  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)");
1631
1697
  db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1698
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC)");
1699
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at)");
1632
1700
  }
1633
1701
  },
1634
1702
  {
@@ -1639,9 +1707,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1639
1707
  addColumnIfMissing(db2, "code_symbols", "source", "source TEXT");
1640
1708
  addColumnIfMissing(db2, "code_edges", "source", "source TEXT");
1641
1709
  }
1710
+ },
1711
+ {
1712
+ id: "1.9-continuity-v2",
1713
+ apply: (db2) => {
1714
+ addColumnIfMissing(db2, "task_continuity_events", "event_key", "event_key TEXT");
1715
+ addColumnIfMissing(db2, "task_continuity_events", "item_key", "item_key TEXT");
1716
+ addColumnIfMissing(db2, "task_continuity_events", "supersedes_id", "supersedes_id TEXT");
1717
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC)");
1718
+ db2.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
1719
+ ON task_continuity_events(project_id, task_id, event_key)
1720
+ WHERE event_key IS NOT NULL`);
1721
+ }
1642
1722
  }
1643
1723
  ];
1644
1724
  _dbCache = /* @__PURE__ */ new Map();
1725
+ _dbLeases = /* @__PURE__ */ new Map();
1645
1726
  }
1646
1727
  });
1647
1728
 
@@ -2131,9 +2212,9 @@ var init_detector = __esm({
2131
2212
  // src/config/legacy-loader.ts
2132
2213
  import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
2133
2214
  import { homedir as homedir3 } from "os";
2134
- function loadFileConfig() {
2215
+ function loadFileConfig(homeDir = homedir3()) {
2135
2216
  if (cachedConfig !== null) return cachedConfig;
2136
- const configPath = getLegacyConfigJsonPath(homedir3());
2217
+ const configPath = getLegacyConfigJsonPath(homeDir);
2137
2218
  try {
2138
2219
  if (existsSync5(configPath)) {
2139
2220
  cachedConfig = JSON.parse(readFileSync5(configPath, "utf-8"));
@@ -2163,15 +2244,15 @@ import { homedir as homedir4 } from "os";
2163
2244
  function loadTomlConfig(options = {}) {
2164
2245
  const homeDir = options.homeDir ?? homedir4();
2165
2246
  const projectRoot = options.projectRoot ?? null;
2166
- const cacheKey = `${homeDir}\0${projectRoot ?? ""}`;
2167
- const cached = configCache2.get(cacheKey);
2247
+ const cacheKey3 = `${homeDir}\0${projectRoot ?? ""}`;
2248
+ const cached = configCache2.get(cacheKey3);
2168
2249
  if (cached) return cached;
2169
2250
  const userToml = getGlobalConfigTomlPath(homeDir);
2170
2251
  const projectToml = projectRoot ? getProjectConfigTomlPath(projectRoot) : null;
2171
2252
  const userConfig = existsSync6(userToml) ? parseTomlConfig(readFileSync6(userToml, "utf8"), userToml) : {};
2172
2253
  const projectConfig = projectToml && existsSync6(projectToml) ? parseTomlConfig(readFileSync6(projectToml, "utf8"), projectToml) : {};
2173
2254
  const merged = mergeTomlConfig(userConfig, projectConfig);
2174
- configCache2.set(cacheKey, merged);
2255
+ configCache2.set(cacheKey3, merged);
2175
2256
  return merged;
2176
2257
  }
2177
2258
  function mergeTomlConfig(base, override) {
@@ -2292,8 +2373,8 @@ function getResolvedConfig(options = {}) {
2292
2373
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
2293
2374
  loadDotenv(projectRoot === null ? void 0 : projectRoot ?? void 0, { userHomeDir: homeDir });
2294
2375
  const toml = loadTomlConfig({ projectRoot: projectRoot ?? null, homeDir });
2295
- const yaml = loadYamlConfig(projectRoot ?? null);
2296
- const legacy = loadFileConfig();
2376
+ const yaml = loadYamlConfig(projectRoot ?? null, { homeDir });
2377
+ const legacy = loadFileConfig(homeDir);
2297
2378
  const embeddingBaseUrl = first(process.env.MEMORIX_EMBEDDING_BASE_URL, toml.embedding?.base_url, yaml.embedding?.baseUrl, legacy.embeddingApi?.baseUrl);
2298
2379
  const openRouterEmbeddingApiKey = isOpenRouterUrl(embeddingBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
2299
2380
  const memoryLlmProvider = first(
@@ -2315,15 +2396,17 @@ function getResolvedConfig(options = {}) {
2315
2396
  legacy.llm?.baseUrl
2316
2397
  );
2317
2398
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
2399
+ const isAtlasCloud = memoryLlmProvider === "atlascloud";
2318
2400
  const memoryLlmApiKey = first(
2319
2401
  process.env.MEMORIX_LLM_API_KEY,
2320
2402
  process.env.MEMORIX_API_KEY,
2321
2403
  toml.memory?.llm?.api_key,
2322
2404
  yaml.llm?.apiKey,
2323
2405
  legacy.llm?.apiKey,
2324
- process.env.OPENAI_API_KEY,
2325
- process.env.ANTHROPIC_API_KEY,
2326
- openRouterMemoryLlmApiKey
2406
+ // Never borrow another provider's credentials for the Atlas Cloud preset.
2407
+ isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : process.env.OPENAI_API_KEY,
2408
+ isAtlasCloud ? void 0 : process.env.ANTHROPIC_API_KEY,
2409
+ isAtlasCloud ? void 0 : openRouterMemoryLlmApiKey
2327
2410
  );
2328
2411
  const resolved = {
2329
2412
  agent: {
@@ -2502,7 +2585,8 @@ function getEnvSourceNames() {
2502
2585
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
2503
2586
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
2504
2587
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
2505
- "OPENROUTER_API_KEY"
2588
+ "OPENROUTER_API_KEY",
2589
+ "ATLASCLOUD_API_KEY"
2506
2590
  ].filter((name) => process.env[name]);
2507
2591
  }
2508
2592
  function isOpenRouterUrl(value) {
@@ -2721,12 +2805,45 @@ function textHash(text2) {
2721
2805
  async function loadDiskCache() {
2722
2806
  try {
2723
2807
  const raw = await readFile(CACHE_FILE, "utf-8");
2808
+ if (Buffer.byteLength(raw, "utf8") > MAX_CACHE_FILE_BYTES) {
2809
+ console.error("[memorix] FastEmbed cache exceeds the disk load limit; starting with an empty cache");
2810
+ return;
2811
+ }
2724
2812
  const entries = JSON.parse(raw);
2725
- for (const [k, v] of entries) cache.set(k, v);
2726
- console.error(`[memorix] Loaded ${entries.length} cached embeddings from disk`);
2813
+ if (!Array.isArray(entries)) return;
2814
+ for (const entry of entries) {
2815
+ if (!Array.isArray(entry) || typeof entry[0] !== "string" || !isVector(entry[1])) continue;
2816
+ cacheSet(entry[0], entry[1], false);
2817
+ }
2818
+ console.error(`[memorix] Loaded ${cache.size} cached embeddings from disk`);
2727
2819
  } catch {
2728
2820
  }
2729
2821
  }
2822
+ function isVector(value) {
2823
+ return Array.isArray(value) && value.every((entry) => typeof entry === "number");
2824
+ }
2825
+ function maxCachePayloadBytes() {
2826
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
2827
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
2828
+ }
2829
+ function cacheSet(hash4, value, markDirty = true) {
2830
+ const incomingBytes = value.length * 8;
2831
+ if (incomingBytes > maxCachePayloadBytes()) return;
2832
+ const existing = cache.get(hash4);
2833
+ if (existing) cachePayloadBytes -= existing.length * 8;
2834
+ else {
2835
+ while (cache.size >= MAX_CACHE_SIZE || cachePayloadBytes + incomingBytes > maxCachePayloadBytes()) {
2836
+ const firstKey = cache.keys().next().value;
2837
+ if (firstKey === void 0) break;
2838
+ const old = cache.get(firstKey);
2839
+ cache.delete(firstKey);
2840
+ if (old) cachePayloadBytes -= old.length * 8;
2841
+ }
2842
+ }
2843
+ cache.set(hash4, value);
2844
+ cachePayloadBytes += incomingBytes;
2845
+ if (markDirty) diskCacheDirty = true;
2846
+ }
2730
2847
  async function saveDiskCache() {
2731
2848
  if (!diskCacheDirty) return;
2732
2849
  try {
@@ -2737,7 +2854,7 @@ async function saveDiskCache() {
2737
2854
  } catch {
2738
2855
  }
2739
2856
  }
2740
- var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, diskCacheDirty, FastEmbedProvider;
2857
+ var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, DEFAULT_CACHE_PAYLOAD_BYTES, MAX_CACHE_FILE_BYTES, cachePayloadBytes, diskCacheDirty, FastEmbedProvider;
2741
2858
  var init_fastembed_provider = __esm({
2742
2859
  "src/embedding/fastembed-provider.ts"() {
2743
2860
  "use strict";
@@ -2747,6 +2864,9 @@ var init_fastembed_provider = __esm({
2747
2864
  CACHE_FILE = join2(CACHE_DIR, ".embedding-cache.json");
2748
2865
  cache = /* @__PURE__ */ new Map();
2749
2866
  MAX_CACHE_SIZE = 5e3;
2867
+ DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
2868
+ MAX_CACHE_FILE_BYTES = 128 * 1024 * 1024;
2869
+ cachePayloadBytes = 0;
2750
2870
  diskCacheDirty = false;
2751
2871
  FastEmbedProvider = class _FastEmbedProvider {
2752
2872
  name = "fastembed-bge-small";
@@ -2788,7 +2908,7 @@ var init_fastembed_provider = __esm({
2788
2908
  if (result.length !== this.dimensions) {
2789
2909
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
2790
2910
  }
2791
- this.cacheSet(hash4, result);
2911
+ cacheSet(hash4, result);
2792
2912
  return result;
2793
2913
  }
2794
2914
  async embedBatch(texts) {
@@ -2813,7 +2933,7 @@ var init_fastembed_provider = __esm({
2813
2933
  const originalIdx = uncachedIndices[batchIdx];
2814
2934
  const plain = Array.from(vec);
2815
2935
  results[originalIdx] = plain;
2816
- this.cacheSet(textHash(uncachedTexts[batchIdx]), plain);
2936
+ cacheSet(textHash(uncachedTexts[batchIdx]), plain);
2817
2937
  batchIdx++;
2818
2938
  }
2819
2939
  }
@@ -2821,14 +2941,6 @@ var init_fastembed_provider = __esm({
2821
2941
  }
2822
2942
  return results;
2823
2943
  }
2824
- cacheSet(hash4, value) {
2825
- if (cache.size >= MAX_CACHE_SIZE) {
2826
- const firstKey = cache.keys().next().value;
2827
- if (firstKey !== void 0) cache.delete(firstKey);
2828
- }
2829
- cache.set(hash4, value);
2830
- diskCacheDirty = true;
2831
- }
2832
2944
  };
2833
2945
  }
2834
2946
  });
@@ -2838,7 +2950,15 @@ var transformers_provider_exports = {};
2838
2950
  __export(transformers_provider_exports, {
2839
2951
  TransformersProvider: () => TransformersProvider
2840
2952
  });
2841
- var cache2, MAX_CACHE_SIZE2, TransformersProvider;
2953
+ import { createHash as createHash2 } from "crypto";
2954
+ function cacheKey2(text2) {
2955
+ return createHash2("sha256").update(text2).digest("hex");
2956
+ }
2957
+ function maxCachePayloadBytes2() {
2958
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
2959
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES2;
2960
+ }
2961
+ var cache2, MAX_CACHE_SIZE2, DEFAULT_CACHE_PAYLOAD_BYTES2, cachePayloadBytes2, TransformersProvider;
2842
2962
  var init_transformers_provider = __esm({
2843
2963
  "src/embedding/transformers-provider.ts"() {
2844
2964
  "use strict";
@@ -2846,6 +2966,8 @@ var init_transformers_provider = __esm({
2846
2966
  init_provider();
2847
2967
  cache2 = /* @__PURE__ */ new Map();
2848
2968
  MAX_CACHE_SIZE2 = 5e3;
2969
+ DEFAULT_CACHE_PAYLOAD_BYTES2 = 64 * 1024 * 1024;
2970
+ cachePayloadBytes2 = 0;
2849
2971
  TransformersProvider = class _TransformersProvider {
2850
2972
  name = "transformers-minilm";
2851
2973
  dimensions = 384;
@@ -2880,7 +3002,7 @@ var init_transformers_provider = __esm({
2880
3002
  return Promise.all(inputs.map((input) => this.embedInput(input, options)));
2881
3003
  }
2882
3004
  async embed(text2) {
2883
- const cached = cache2.get(text2);
3005
+ const cached = cache2.get(cacheKey2(text2));
2884
3006
  if (cached) return cached;
2885
3007
  const output = await this.extractor(text2, {
2886
3008
  pooling: "mean",
@@ -2890,7 +3012,7 @@ var init_transformers_provider = __esm({
2890
3012
  if (result.length !== this.dimensions) {
2891
3013
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
2892
3014
  }
2893
- this.cacheSet(text2, result);
3015
+ this.cacheSet(cacheKey2(text2), result);
2894
3016
  return result;
2895
3017
  }
2896
3018
  async embedBatch(texts) {
@@ -2898,7 +3020,7 @@ var init_transformers_provider = __esm({
2898
3020
  const uncachedIndices = [];
2899
3021
  const uncachedTexts = [];
2900
3022
  for (let i2 = 0; i2 < texts.length; i2++) {
2901
- const cached = cache2.get(texts[i2]);
3023
+ const cached = cache2.get(cacheKey2(texts[i2]));
2902
3024
  if (cached) {
2903
3025
  results[i2] = cached;
2904
3026
  } else {
@@ -2916,17 +3038,27 @@ var init_transformers_provider = __esm({
2916
3038
  const vec = Array.from(allVecs[i2]);
2917
3039
  const originalIdx = uncachedIndices[i2];
2918
3040
  results[originalIdx] = vec;
2919
- this.cacheSet(uncachedTexts[i2], vec);
3041
+ this.cacheSet(cacheKey2(uncachedTexts[i2]), vec);
2920
3042
  }
2921
3043
  }
2922
3044
  return results;
2923
3045
  }
2924
3046
  cacheSet(key, value) {
2925
- if (cache2.size >= MAX_CACHE_SIZE2) {
2926
- const firstKey = cache2.keys().next().value;
2927
- if (firstKey !== void 0) cache2.delete(firstKey);
3047
+ const incomingBytes = value.length * 8;
3048
+ if (incomingBytes > maxCachePayloadBytes2()) return;
3049
+ const existing = cache2.get(key);
3050
+ if (existing) cachePayloadBytes2 -= existing.length * 8;
3051
+ else {
3052
+ while (cache2.size >= MAX_CACHE_SIZE2 || cachePayloadBytes2 + incomingBytes > maxCachePayloadBytes2()) {
3053
+ const firstKey = cache2.keys().next().value;
3054
+ if (firstKey === void 0) break;
3055
+ const old = cache2.get(firstKey);
3056
+ cache2.delete(firstKey);
3057
+ if (old) cachePayloadBytes2 -= old.length * 8;
3058
+ }
2928
3059
  }
2929
3060
  cache2.set(key, value);
3061
+ cachePayloadBytes2 += incomingBytes;
2930
3062
  }
2931
3063
  };
2932
3064
  }
@@ -2948,7 +3080,7 @@ __export(api_provider_exports, {
2948
3080
  saveDiskCacheNow: () => saveDiskCacheNow,
2949
3081
  seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
2950
3082
  });
2951
- import { createHash as createHash2 } from "crypto";
3083
+ import { createHash as createHash3 } from "crypto";
2952
3084
  import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
2953
3085
  import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
2954
3086
  import { join as join3 } from "path";
@@ -2975,7 +3107,7 @@ function cacheStatFile() {
2975
3107
  function payloadBytes(vector) {
2976
3108
  return vector.length * BYTES_PER_DIM;
2977
3109
  }
2978
- function maxCachePayloadBytes() {
3110
+ function maxCachePayloadBytes3() {
2979
3111
  const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
2980
3112
  if (raw) {
2981
3113
  const parsed = Number.parseInt(raw, 10);
@@ -2985,7 +3117,7 @@ function maxCachePayloadBytes() {
2985
3117
  }
2986
3118
  function maxEntriesForDimensions(dimensions) {
2987
3119
  const dim = Math.max(1, Math.floor(dimensions));
2988
- const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
3120
+ const byBytes = Math.floor(maxCachePayloadBytes3() / (dim * BYTES_PER_DIM));
2989
3121
  return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
2990
3122
  }
2991
3123
  function rememberDimensions(vector) {
@@ -2998,13 +3130,15 @@ function evictOldest() {
2998
3130
  if (firstKey === void 0) return;
2999
3131
  const old = cache3.get(firstKey);
3000
3132
  cache3.delete(firstKey);
3001
- if (old) cachePayloadBytes -= payloadBytes(old);
3133
+ if (old) cachePayloadBytes3 -= payloadBytes(old);
3002
3134
  }
3003
3135
  function evictToFit(incomingBytes, incomingDims) {
3136
+ if (incomingBytes > maxCachePayloadBytes3()) return false;
3004
3137
  const limit2 = maxEntriesForDimensions(incomingDims);
3005
- while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
3138
+ while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes3 + incomingBytes > maxCachePayloadBytes3())) {
3006
3139
  evictOldest();
3007
3140
  }
3141
+ return true;
3008
3142
  }
3009
3143
  function isNumberArray(value) {
3010
3144
  return Array.isArray(value) && value.every((entry) => typeof entry === "number");
@@ -3031,12 +3165,12 @@ function cacheInsert(hash4, value, dirty) {
3031
3165
  const incoming = payloadBytes(value);
3032
3166
  const existing = cache3.get(hash4);
3033
3167
  if (existing) {
3034
- cachePayloadBytes -= payloadBytes(existing);
3168
+ cachePayloadBytes3 -= payloadBytes(existing);
3035
3169
  cache3.delete(hash4);
3036
3170
  }
3037
- evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
3171
+ if (!evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS)) return;
3038
3172
  cache3.set(hash4, value);
3039
- cachePayloadBytes += incoming;
3173
+ cachePayloadBytes3 += incoming;
3040
3174
  if (dirty) diskCacheDirty2 = true;
3041
3175
  }
3042
3176
  async function readCacheStat() {
@@ -3072,7 +3206,7 @@ function cacheNamespace(config) {
3072
3206
  ].join("|");
3073
3207
  }
3074
3208
  function textHash2(text2, namespace) {
3075
- return createHash2("sha256").update(`${namespace}\0${text2}`).digest("hex").slice(0, 16);
3209
+ return createHash3("sha256").update(`${namespace}\0${text2}`).digest("hex").slice(0, 16);
3076
3210
  }
3077
3211
  function inputIdentity(input, options = {}) {
3078
3212
  return JSON.stringify({
@@ -3414,7 +3548,7 @@ function getApiEmbeddingDiskCacheLoadCountForTests() {
3414
3548
  }
3415
3549
  function resetApiEmbeddingCacheForTests() {
3416
3550
  cache3.clear();
3417
- cachePayloadBytes = 0;
3551
+ cachePayloadBytes3 = 0;
3418
3552
  cacheDimensions = null;
3419
3553
  diskCacheEntryCount = 0;
3420
3554
  diskCacheLoaded = false;
@@ -3425,14 +3559,14 @@ function resetApiEmbeddingCacheForTests() {
3425
3559
  }
3426
3560
  function seedApiEmbeddingCacheForTests(entries) {
3427
3561
  cache3.clear();
3428
- cachePayloadBytes = 0;
3562
+ cachePayloadBytes3 = 0;
3429
3563
  cacheDimensions = null;
3430
3564
  for (const [key, value] of entries) cacheInsert(key, value, true);
3431
3565
  diskCacheLoaded = true;
3432
3566
  diskCacheDirty2 = true;
3433
3567
  }
3434
3568
  function getApiEmbeddingCachePayloadBytesForTests() {
3435
- return cachePayloadBytes;
3569
+ return cachePayloadBytes3;
3436
3570
  }
3437
3571
  function getApiEmbeddingJsonlLineCountCallsForTests() {
3438
3572
  return jsonlLineCountCalls;
@@ -3606,7 +3740,7 @@ function scheduleDiskSave() {
3606
3740
  diskSaveTimer = null;
3607
3741
  }, DISK_SAVE_DEBOUNCE_MS);
3608
3742
  }
3609
- function cacheSet(hash4, value) {
3743
+ function cacheSet2(hash4, value) {
3610
3744
  cacheInsert(hash4, value, true);
3611
3745
  }
3612
3746
  function resolveEnvEmbeddingApiKey() {
@@ -3726,7 +3860,7 @@ async function waitForRetry(waitMs, signal) {
3726
3860
  abortSignal.addEventListener("abort", onAbort, { once: true });
3727
3861
  });
3728
3862
  }
3729
- var cache3, MAX_CACHE_ENTRIES, MAX_CACHE_PAYLOAD_BYTES, BYTES_PER_DIM, DEFAULT_ASSUMED_DIMENSIONS, MAX_MIGRATION_VALUE_BYTES, cachePayloadBytes, cacheDimensions, diskCacheEntryCount, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, diskCacheLoadCount, jsonlLineCountCalls, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES2, BASE_DELAY_MS, EmbeddingAPIError, APIEmbeddingProvider;
3863
+ var cache3, MAX_CACHE_ENTRIES, MAX_CACHE_PAYLOAD_BYTES, BYTES_PER_DIM, DEFAULT_ASSUMED_DIMENSIONS, MAX_MIGRATION_VALUE_BYTES, cachePayloadBytes3, cacheDimensions, diskCacheEntryCount, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, diskCacheLoadCount, jsonlLineCountCalls, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES2, BASE_DELAY_MS, EmbeddingAPIError, APIEmbeddingProvider;
3730
3864
  var init_api_provider = __esm({
3731
3865
  "src/embedding/api-provider.ts"() {
3732
3866
  "use strict";
@@ -3738,7 +3872,7 @@ var init_api_provider = __esm({
3738
3872
  BYTES_PER_DIM = 8;
3739
3873
  DEFAULT_ASSUMED_DIMENSIONS = 4096;
3740
3874
  MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
3741
- cachePayloadBytes = 0;
3875
+ cachePayloadBytes3 = 0;
3742
3876
  cacheDimensions = null;
3743
3877
  diskCacheEntryCount = 0;
3744
3878
  diskCacheDirty2 = false;
@@ -3923,7 +4057,7 @@ var init_api_provider = __esm({
3923
4057
  } else {
3924
4058
  embedding = await apiCall();
3925
4059
  }
3926
- cacheSet(hash4, embedding);
4060
+ cacheSet2(hash4, embedding);
3927
4061
  scheduleDiskSave();
3928
4062
  return embedding;
3929
4063
  }
@@ -3971,7 +4105,7 @@ var init_api_provider = __esm({
3971
4105
  for (const item of response.data) {
3972
4106
  const originalIdx = chunkIndices[item.index];
3973
4107
  results[originalIdx] = item.embedding;
3974
- cacheSet(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
4108
+ cacheSet2(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
3975
4109
  }
3976
4110
  } catch (error) {
3977
4111
  const providerLimit = parseBatchLimit(error);
@@ -4049,7 +4183,7 @@ var init_api_provider = __esm({
4049
4183
  throw new Error(`Expected ${this.dimensions}d, got ${embedding.length}d; dimension mismatch`);
4050
4184
  }
4051
4185
  this.trackUsage(response);
4052
- cacheSet(hash4, embedding);
4186
+ cacheSet2(hash4, embedding);
4053
4187
  scheduleDiskSave();
4054
4188
  return embedding;
4055
4189
  }
@@ -5432,6 +5566,7 @@ var init_provider2 = __esm({
5432
5566
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
5433
5567
  anthropic: { baseUrl: "https://api.anthropic.com/v1", model: "claude-3-5-haiku-latest" },
5434
5568
  openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "openai/gpt-4.1-nano" },
5569
+ atlascloud: { baseUrl: "https://api.atlascloud.ai/v1", model: "deepseek-ai/deepseek-v3.2" },
5435
5570
  custom: { baseUrl: "http://localhost:11434/v1", model: "llama3" }
5436
5571
  };
5437
5572
  currentConfig = null;
@@ -6195,7 +6330,7 @@ var init_obs_store = __esm({
6195
6330
  });
6196
6331
 
6197
6332
  // src/search/semantic-index.ts
6198
- import { createHash as createHash3 } from "crypto";
6333
+ import { createHash as createHash4 } from "crypto";
6199
6334
  import { createRequire as createRequire3 } from "module";
6200
6335
  import path8 from "path";
6201
6336
  function createSemanticIndexProfile(provider2) {
@@ -6209,7 +6344,7 @@ function stateKey(dataDir, profile) {
6209
6344
  return `${path8.resolve(dataDir)}::${profile.key}`;
6210
6345
  }
6211
6346
  function tableName(profile) {
6212
- const digest2 = createHash3("sha256").update(profile.key).digest("hex").slice(0, 20);
6347
+ const digest2 = createHash4("sha256").update(profile.key).digest("hex").slice(0, 20);
6213
6348
  return `${TABLE_PREFIX}${digest2}`;
6214
6349
  }
6215
6350
  function indexRoot(dataDir) {
@@ -8249,6 +8384,7 @@ var init_secret_filter = __esm({
8249
8384
  // src/runtime/maintenance-jobs.ts
8250
8385
  var maintenance_jobs_exports = {};
8251
8386
  __export(maintenance_jobs_exports, {
8387
+ DEFAULT_FAILED_HISTORY_RETENTION_MS: () => DEFAULT_FAILED_HISTORY_RETENTION_MS,
8252
8388
  MAINTENANCE_JOB_KINDS: () => MAINTENANCE_JOB_KINDS,
8253
8389
  MaintenanceJobStore: () => MaintenanceJobStore,
8254
8390
  MaintenanceJobWorker: () => MaintenanceJobWorker,
@@ -8298,7 +8434,7 @@ function normalizedKinds(kinds) {
8298
8434
  function getMaintenanceRetryDelayMs(attempts) {
8299
8435
  return Math.min(6e4, 1e3 * 2 ** Math.max(0, attempts - 1));
8300
8436
  }
8301
- var MAINTENANCE_JOB_KINDS, DEFAULT_MAX_ATTEMPTS, DEFAULT_LEASE_MS, DEFAULT_POLL_INTERVAL_MS, MAX_ERROR_LENGTH, DEFAULT_COMPLETED_HISTORY_RETENTION_MS, MaintenanceJobStore, MaintenanceJobWorker;
8437
+ var MAINTENANCE_JOB_KINDS, DEFAULT_MAX_ATTEMPTS, DEFAULT_LEASE_MS, DEFAULT_POLL_INTERVAL_MS, MAX_ERROR_LENGTH, DEFAULT_COMPLETED_HISTORY_RETENTION_MS, DEFAULT_FAILED_HISTORY_RETENTION_MS, MaintenanceJobStore, MaintenanceJobWorker;
8302
8438
  var init_maintenance_jobs = __esm({
8303
8439
  "src/runtime/maintenance-jobs.ts"() {
8304
8440
  "use strict";
@@ -8325,6 +8461,7 @@ var init_maintenance_jobs = __esm({
8325
8461
  DEFAULT_POLL_INTERVAL_MS = 2e3;
8326
8462
  MAX_ERROR_LENGTH = 1e3;
8327
8463
  DEFAULT_COMPLETED_HISTORY_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
8464
+ DEFAULT_FAILED_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
8328
8465
  MaintenanceJobStore = class {
8329
8466
  db;
8330
8467
  constructor(dataDir) {
@@ -8451,18 +8588,21 @@ var init_maintenance_jobs = __esm({
8451
8588
  }
8452
8589
  /**
8453
8590
  * Completed jobs are useful recent operator history, not permanent data.
8454
- * Failed jobs stay available for diagnosis until an operator explicitly
8455
- * resolves them or clears the database.
8591
+ * Failed diagnostics are retained longer, but are still bounded so a
8592
+ * permanently failing job kind cannot grow the database forever.
8456
8593
  */
8457
8594
  pruneCompletedHistory(options = {}) {
8458
8595
  const now3 = options.now ?? Date.now();
8459
8596
  const maxAgeMs = Number.isFinite(options.maxAgeMs) ? Math.max(0, Math.floor(options.maxAgeMs)) : DEFAULT_COMPLETED_HISTORY_RETENTION_MS;
8597
+ const failedMaxAgeMs = Number.isFinite(options.failedMaxAgeMs) ? Math.max(0, Math.floor(options.failedMaxAgeMs)) : DEFAULT_FAILED_HISTORY_RETENTION_MS;
8460
8598
  const result = this.db.prepare(`
8461
8599
  DELETE FROM maintenance_jobs
8462
- WHERE status = 'completed'
8600
+ WHERE (status = 'completed'
8463
8601
  AND completed_at IS NOT NULL
8464
- AND completed_at < ?
8465
- `).run(now3 - maxAgeMs);
8602
+ AND completed_at < ?)
8603
+ OR (status = 'failed'
8604
+ AND updated_at < ?)
8605
+ `).run(now3 - maxAgeMs, now3 - failedMaxAgeMs);
8466
8606
  return Number(result.changes ?? 0);
8467
8607
  }
8468
8608
  claimNext(options) {
@@ -8788,7 +8928,7 @@ var init_lifecycle = __esm({
8788
8928
  });
8789
8929
 
8790
8930
  // src/store/evidence-store.ts
8791
- import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
8931
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
8792
8932
  function safeJson(value, fallback) {
8793
8933
  if (typeof value !== "string" || value.length === 0) return fallback;
8794
8934
  try {
@@ -8822,7 +8962,7 @@ function rowToCard(row) {
8822
8962
  };
8823
8963
  }
8824
8964
  function observationHash(observation) {
8825
- return createHash4("sha256").update(JSON.stringify({
8965
+ return createHash5("sha256").update(JSON.stringify({
8826
8966
  projectId: observation.projectId,
8827
8967
  id: observation.id,
8828
8968
  title: observation.title,
@@ -9252,13 +9392,13 @@ __export(workspace_exports, {
9252
9392
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
9253
9393
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
9254
9394
  });
9255
- import { createHash as createHash5 } from "crypto";
9395
+ import { createHash as createHash6 } from "crypto";
9256
9396
  import { execFile } from "child_process";
9257
9397
  import { promises as fs6 } from "fs";
9258
9398
  import path10 from "path";
9259
9399
  import { promisify } from "util";
9260
9400
  function hash(value) {
9261
- return createHash5("sha256").update(value).digest("hex");
9401
+ return createHash6("sha256").update(value).digest("hex");
9262
9402
  }
9263
9403
  function workspaceId(projectId, mode) {
9264
9404
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -10393,14 +10533,14 @@ var init_types2 = __esm({
10393
10533
  });
10394
10534
 
10395
10535
  // src/media/asset-store.ts
10396
- import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
10536
+ import { createHash as createHash7, randomUUID as randomUUID6 } from "crypto";
10397
10537
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
10398
10538
  import path11 from "path";
10399
10539
  function mediaRoot(dataDir) {
10400
10540
  return path11.resolve(dataDir, "media");
10401
10541
  }
10402
10542
  function projectStorageKey(projectId) {
10403
- return createHash6("sha256").update(projectId).digest("hex").slice(0, 16);
10543
+ return createHash7("sha256").update(projectId).digest("hex").slice(0, 16);
10404
10544
  }
10405
10545
  function validateUnderRoot(root, candidate) {
10406
10546
  const resolved = path11.resolve(candidate);
@@ -10509,7 +10649,7 @@ async function importMediaBuffer(input) {
10509
10649
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
10510
10650
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
10511
10651
  const detected = detectMedia(input.bytes.subarray(0, 32));
10512
- const sha256 = createHash6("sha256").update(input.bytes).digest("hex");
10652
+ const sha256 = createHash7("sha256").update(input.bytes).digest("hex");
10513
10653
  return importKnownMedia({
10514
10654
  dataDir: input.dataDir,
10515
10655
  projectId: input.projectId,
@@ -10527,7 +10667,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
10527
10667
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
10528
10668
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
10529
10669
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
10530
- const sha256 = createHash6("sha256").update(bytes).digest("hex");
10670
+ const sha256 = createHash7("sha256").update(bytes).digest("hex");
10531
10671
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
10532
10672
  return bytes;
10533
10673
  }
@@ -69223,16 +69363,16 @@ var require_jwtaccess = __commonJS({
69223
69363
  * @returns A string that returns the cached key.
69224
69364
  */
69225
69365
  getCachedKey(url, scopes) {
69226
- let cacheKey = url;
69366
+ let cacheKey3 = url;
69227
69367
  if (scopes && Array.isArray(scopes) && scopes.length) {
69228
- cacheKey = url ? `${url}_${scopes.join("_")}` : `${scopes.join("_")}`;
69368
+ cacheKey3 = url ? `${url}_${scopes.join("_")}` : `${scopes.join("_")}`;
69229
69369
  } else if (typeof scopes === "string") {
69230
- cacheKey = url ? `${url}_${scopes}` : scopes;
69370
+ cacheKey3 = url ? `${url}_${scopes}` : scopes;
69231
69371
  }
69232
- if (!cacheKey) {
69372
+ if (!cacheKey3) {
69233
69373
  throw Error("Scopes or url must be provided");
69234
69374
  }
69235
- return cacheKey;
69375
+ return cacheKey3;
69236
69376
  }
69237
69377
  /**
69238
69378
  * Get a non-expired access token, after refreshing if necessary.
@@ -75778,7 +75918,7 @@ var require_websocket = __commonJS({
75778
75918
  var http3 = __require("http");
75779
75919
  var net = __require("net");
75780
75920
  var tls = __require("tls");
75781
- var { randomBytes, createHash: createHash16 } = __require("crypto");
75921
+ var { randomBytes, createHash: createHash17 } = __require("crypto");
75782
75922
  var { Duplex, Readable: Readable2 } = __require("stream");
75783
75923
  var { URL: URL2 } = __require("url");
75784
75924
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -76446,7 +76586,7 @@ var require_websocket = __commonJS({
76446
76586
  abortHandshake(websocket, socket, "Invalid Upgrade header");
76447
76587
  return;
76448
76588
  }
76449
- const digest2 = createHash16("sha1").update(key + GUID).digest("base64");
76589
+ const digest2 = createHash17("sha1").update(key + GUID).digest("base64");
76450
76590
  if (res.headers["sec-websocket-accept"] !== digest2) {
76451
76591
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
76452
76592
  return;
@@ -76818,7 +76958,7 @@ var require_websocket_server = __commonJS({
76818
76958
  var EventEmitter = __require("events");
76819
76959
  var http3 = __require("http");
76820
76960
  var { Duplex } = __require("stream");
76821
- var { createHash: createHash16 } = __require("crypto");
76961
+ var { createHash: createHash17 } = __require("crypto");
76822
76962
  var extension3 = require_extension();
76823
76963
  var PerMessageDeflate2 = require_permessage_deflate();
76824
76964
  var subprotocol2 = require_subprotocol();
@@ -77125,7 +77265,7 @@ var require_websocket_server = __commonJS({
77125
77265
  );
77126
77266
  }
77127
77267
  if (this._state > RUNNING) return abortHandshake(socket, 503);
77128
- const digest2 = createHash16("sha1").update(key + GUID).digest("base64");
77268
+ const digest2 = createHash17("sha1").update(key + GUID).digest("base64");
77129
77269
  const headers = [
77130
77270
  "HTTP/1.1 101 Switching Protocols",
77131
77271
  "Upgrade: websocket",
@@ -145704,9 +145844,9 @@ var init_long_term = __esm({
145704
145844
  });
145705
145845
 
145706
145846
  // src/codegraph/ids.ts
145707
- import { createHash as createHash7 } from "crypto";
145847
+ import { createHash as createHash8 } from "crypto";
145708
145848
  function digest(input) {
145709
- return createHash7("sha256").update(input).digest("hex").slice(0, 16);
145849
+ return createHash8("sha256").update(input).digest("hex").slice(0, 16);
145710
145850
  }
145711
145851
  function normalizeCodePath(path23) {
145712
145852
  return path23.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -146754,7 +146894,7 @@ var init_store = __esm({
146754
146894
 
146755
146895
  // src/codegraph/code-state.ts
146756
146896
  import { execFile as execFile2 } from "child_process";
146757
- import { createHash as createHash8 } from "crypto";
146897
+ import { createHash as createHash9 } from "crypto";
146758
146898
  import { promisify as promisify3 } from "util";
146759
146899
  async function runGit(rootPath, args) {
146760
146900
  try {
@@ -146783,7 +146923,7 @@ function changedPathCount(status) {
146783
146923
  return count2;
146784
146924
  }
146785
146925
  function fingerprint(baseRevision, status) {
146786
- return createHash8("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
146926
+ return createHash9("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
146787
146927
  }
146788
146928
  async function collectCodeStateSnapshot(input) {
146789
146929
  const [revision, status] = await Promise.all([
@@ -146877,12 +147017,12 @@ var init_exclude3 = __esm({
146877
147017
  });
146878
147018
 
146879
147019
  // src/codegraph/semantic-provider.ts
146880
- import { createHash as createHash9 } from "crypto";
147020
+ import { createHash as createHash10 } from "crypto";
146881
147021
  import "fs";
146882
147022
  import { resolve } from "path";
146883
147023
  import ts from "typescript";
146884
147024
  function hashText(value) {
146885
- return createHash9("sha256").update(value).digest("hex");
147025
+ return createHash10("sha256").update(value).digest("hex");
146886
147026
  }
146887
147027
  function canonicalPath(value) {
146888
147028
  const normalized = resolve(value).replace(/\\/g, "/");
@@ -147227,11 +147367,11 @@ __export(lite_provider_exports, {
147227
147367
  indexProjectLite: () => indexProjectLite,
147228
147368
  refreshProjectLite: () => refreshProjectLite
147229
147369
  });
147230
- import { createHash as createHash10 } from "crypto";
147370
+ import { createHash as createHash11 } from "crypto";
147231
147371
  import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
147232
147372
  import { join as join4, relative as relative2 } from "path";
147233
147373
  function hashText2(text2) {
147234
- return createHash10("sha256").update(text2).digest("hex");
147374
+ return createHash11("sha256").update(text2).digest("hex");
147235
147375
  }
147236
147376
  function extension2(path23) {
147237
147377
  const index = path23.lastIndexOf(".");
@@ -147864,7 +148004,7 @@ var claim_store_exports = {};
147864
148004
  __export(claim_store_exports, {
147865
148005
  ClaimStore: () => ClaimStore
147866
148006
  });
147867
- import { createHash as createHash11, randomUUID as randomUUID12 } from "crypto";
148007
+ import { createHash as createHash12, randomUUID as randomUUID12 } from "crypto";
147868
148008
  function optionalText3(value) {
147869
148009
  return typeof value === "string" && value ? value : void 0;
147870
148010
  }
@@ -147917,7 +148057,7 @@ function rowToEvent3(row) {
147917
148057
  };
147918
148058
  }
147919
148059
  function evidenceKey(input) {
147920
- return createHash11("sha256").update(JSON.stringify([
148060
+ return createHash12("sha256").update(JSON.stringify([
147921
148061
  input.evidenceKind,
147922
148062
  input.evidenceId,
147923
148063
  input.relation,
@@ -148115,7 +148255,7 @@ __export(claims_exports, {
148115
148255
  supersedeClaim: () => supersedeClaim,
148116
148256
  writeClaim: () => writeClaim
148117
148257
  });
148118
- import { createHash as createHash12, randomUUID as randomUUID13 } from "crypto";
148258
+ import { createHash as createHash13, randomUUID as randomUUID13 } from "crypto";
148119
148259
  function compactText2(value, field) {
148120
148260
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
148121
148261
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -148127,7 +148267,7 @@ function normalizedTerm(value) {
148127
148267
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
148128
148268
  }
148129
148269
  function hashIdentity(parts) {
148130
- return createHash12("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
148270
+ return createHash13("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
148131
148271
  }
148132
148272
  function buildClaimIdentity(input) {
148133
148273
  const subject = normalizedTerm(input.subject);
@@ -148368,7 +148508,7 @@ function supersedeClaim(store, input) {
148368
148508
  });
148369
148509
  }
148370
148510
  function hashObservation(observation) {
148371
- return createHash12("sha256").update(JSON.stringify([
148511
+ return createHash13("sha256").update(JSON.stringify([
148372
148512
  observation.title,
148373
148513
  observation.narrative,
148374
148514
  observation.facts,
@@ -148612,11 +148752,11 @@ var init_frontmatter = __esm({
148612
148752
  });
148613
148753
 
148614
148754
  // src/knowledge/markdown.ts
148615
- import { createHash as createHash13 } from "crypto";
148755
+ import { createHash as createHash14 } from "crypto";
148616
148756
  import { promises as fs10 } from "fs";
148617
148757
  import path18 from "path";
148618
148758
  function hashContent(content) {
148619
- return createHash13("sha256").update(content).digest("hex");
148759
+ return createHash14("sha256").update(content).digest("hex");
148620
148760
  }
148621
148761
  function stringField(data, key) {
148622
148762
  const value = data[key];
@@ -148742,11 +148882,11 @@ __export(wiki_exports, {
148742
148882
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
148743
148883
  readKnowledgePage: () => readKnowledgePage
148744
148884
  });
148745
- import { createHash as createHash14 } from "crypto";
148885
+ import { createHash as createHash15 } from "crypto";
148746
148886
  import { promises as fs11 } from "fs";
148747
148887
  import path19 from "path";
148748
148888
  function hash2(value) {
148749
- return createHash14("sha256").update(value).digest("hex");
148889
+ return createHash15("sha256").update(value).digest("hex");
148750
148890
  }
148751
148891
  function now() {
148752
148892
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -149603,11 +149743,11 @@ __export(workflows_exports, {
149603
149743
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
149604
149744
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
149605
149745
  });
149606
- import { createHash as createHash15 } from "crypto";
149746
+ import { createHash as createHash16 } from "crypto";
149607
149747
  import { promises as fs12 } from "fs";
149608
149748
  import path20 from "path";
149609
149749
  function hash3(value) {
149610
- return createHash15("sha256").update(value).digest("hex");
149750
+ return createHash16("sha256").update(value).digest("hex");
149611
149751
  }
149612
149752
  function now2() {
149613
149753
  return (/* @__PURE__ */ new Date()).toISOString();